Don’t truncate in Laravel Excel imports

One of the greatest things about Laravel Excel is its event system – you can easily register closures or classes to be executed during certain events in the import lifecycle, such as before and after the import.

I’ve often wanted to truncate a database table to import the data ‘fresh’ before an import – I can think of twice off the top of my head where I’ve wanted to do this within commercial software. One thing to remember, however, is that all Laravel Excel imports are wrapped in a database transaction. To quote the documentation found here:

The entire import is automatically wrapped in a database transaction, that means that every error will rollback the entire import. When using batch inserts, only the current batch will be rollbacked.

You can of course, disable database transactions entirely if you’d prefer to do it yourself and/or if you really need database truncation, but for the most part, just remember that calling truncate() on an Eloquent model or table will itself trigger a database transaction and subsequently your import may fail due to the nested transactions if not supported by your DBMS.

If in doubt, simply use Model::query()->delete() to blitz your table free of entries before your import. Your importer class should implement the WithEvents interface. Your implementation might look a little like this:

public function registerEvents(): array
{
	return [
		BeforeImport::class => function (): void {
			// Don't call truncate, call delete!
			Model::query()->delete();
		},
	];
}

The problems with returning void in Laravel controllers

If there’s one thing you can depend on Laravel for, it’s ensuring that your controllers return a response to the client.

Whether you’re explicitly returning responses using the response() helper, or depending on Laravel’s magic to convert arrays and collections into JSON, the two things that these strategies have in common is explicitness. You’re explicitly returning a type of response to the client, and as a result, you, your colleagues, and Laravel all understand what is expected.

But then let’s take the following snippet of code as an example where this principle is broken. Not the most practical, but something you might see in a controller:

public function delete(Request $request, User $user): void
{
    if (auth()->check() && $user->isAdmin()) {
      $user->delete();
    }
}

Ignoring the authorisation here, what’s wrong with this method?

It doesn’t return anything.

Of course, Laravel is still able to work with this. It’ll send a default response with a 200 OK status code (assuming we didn’t run into any exceptions) and call it a day. It’s quite likely that the developer intends for the response to be this way, and any front-ends talking to this endpoint are happy provided they get a successful response code, especially given that in this example, we’re handing a DELETE request.

As you can see though, we’re making assumptions. Did the developer actually intend to return nothing, and is a 200 status code the most applicable in this scenario? What if Laravel’s behaviour changes in later revisions? This means we’d have to check for any controllers that return void and potentially change their behaviour, and/or the implementations that work with them.

So what in my opinion should you do in this situation? Simply responding with an empty response, or even better, with a response()->noContent() would be more explicit about what you intend to do to all involved. Better than a comment, and better than relying on default behaviours.

Just my two cents about void controllers, and would love to hear from other developers who agree or disagree and why.