When working with Laravel for the majority of your working day. You tend to come across a gotcha or two that either trips yourself or your fellow colleagues up from time to time.
I've put together a list of the Laravel gotchas that I have encountered and some of these have either tripped me up once or even twice!
Gotcha! Query Chunking
Let's run with the idea that we have initialised a new email column on the users table and we want to initialise each user's email to {user-id}@example.com.
We can do that with the following query:
User::query()
->whereNull('email')
->each(function (User $user) {
dump($user->id);
$user->update(['email' => "$user->[email protected]"]);
}, 10);
In theory - we should be setting every single user in our database that has a null email address with the new placeholder email. But there's a problem here.
There's a bunch of data that hasn't actually been touched at all. The reason for this is because chunk
will call a database query for a bulk of rows at a time using LIMIT/OFFSET.
Since we are manipulating the data we are performing a conditional query on, we really just want to get the first 10 rows every time since the list is shrinking.
A little less elegant of a solution but a do/while loop solves this problem.
do {
$users = User::query()
->whereNull('email')
->limit(10)
->get();
foreach ($users as $user) {
dump($user->id);
$user->update(['email' => "$user->[email protected]"]);
}
} while ($users->count());
Gotcha! Array::get
This is one that has caught me out a few times. Take the following array and run it through Arr::get / array_get with null specified as the key parameter. What do you expect to receive?
$animals = [
'dog' => 'woof',
'cat' => 'meow',
];
Arr::get($animals, null, 'some default value');
Arr::get above will return the full array of $animals
, ignoring the default return value that you may otherwise expect to receive.
Gotcha! Caching
Consider the following code snippet:
return Cache::rememberForever('user', function () {
return User::query()
->where('name', 'Bobettello')
->first();
});
The idea is we're caching the user 'Bobettello' to save us from performing a database query every time we want this user. In practice, we would be caching more expensive operations here but to highlight an issue it's been kept to the minimum.
What do you expect to happen when there's no user found? It returns null in this particular scenario. Fine - okay. But it's cached so we don't have to worry about calling our super expensive query again - or do we?
Returning null here doesn't actually cause the cache to well, cache. The next time this code runs it'll be running the super expensive extreme query again. This is also known as a cache miss.
The solution? Ensure null is never returned if you expect the cache to stick.
Gotcha! Env/Config Caching
In this example, I've gone ahead and set up a variable in my .env
file for:
MY_ENV_VAR = "Hello!"
Then thrown into a controller to return this value on my index page like so:
public function index()
{
return env('MY_ENV_VAR', "WHERE MY VAR?!");
}
And I navigate to this page in my browser and behold: "Hello!"
Right, so we're going to production next and we're running all the optimisation things that we do. Including php artisan config:cache
We deploy annnnnd... Eh?
When the config file is cached in Laravel, the env helper no longer loads the .env
file. To otherwise handle this it's just not a very good idea to be using env() at all. Define all your configuration variables within a config file and access them using config()
instead.
public function index()
{
return config('my_config.my_var', "WHERE MY VAR?!");
}
The Laravel documentation explains this issue as well and suggests only calling env()
from within your config files.
Gotcha! Route Model Binding
I have a route for FormSubmissionController@show
where within the controller I want to load in FormSubmission model by its id and return its data, or something like that.
In my routes file:
Route::get('/form-submission/{formSubmission}', [FormSubmissionController::class, 'show']);
Next, is the controller. Binding the FormSubmission parameter and returning its data as an array:
public function show(FormSubmission $submission)
{
return $submission->toArray();
}
Then nothing happens...
Let's dd the $submission to see what's happening:
It appears we are receiving a FormSubmission model as expected, but... Why is it empty?
Further debugging tells me that the model does not exist, yet it's given me a model.
dd($submission->exists); // returns false
The issue is all down to how we've named the variable in both the route and controller. In reality, if the model cannot be found we expect Laravel to give us a 404 error but this assumes the model is bound correctly.
To resolve this, make sure the variable name in the method signature matches the bound name in the route. {formSubmission}
=> $formSubmission
Route::get('/form-submission/{formSubmission}', [FormSubmissionController::class, 'show']);
public function show(FormSubmission $formSubmission)
{
return $formSubmission->toArray();
}
All good now!
Conclusion
This concludes the list of Laravel gotchas that I can recall for the time being.
If you have any of your own Laravel gotchas that have tripped you up. Feel free to reply in the comments with your own!