Catch N+1 Queries Early with Laravel's Strict Model Mode
The N+1 query problem is easy to write and hard to see. You loop over a collection of models, touch a relationship inside the loop, and Eloquent quietly fires one extra query per row. Everything works in development with ten records and falls over in production with ten thousand. Eloquent ships with a strict mode that turns these silent mistakes into exceptions so you catch them the moment you write them.
Three Guards in One
Eloquent exposes three related guards. Each one targets a different kind of quiet mistake.
preventLazyLoading() throws a LazyLoadingViolationException whenever a relationship is loaded lazily instead of being eager loaded. That is your N+1 detector.
preventSilentlyDiscardingAttributes() throws when you try to mass assign an attribute that is not listed in the model's $fillable array, instead of silently dropping it.
preventAccessingMissingAttributes() throws a MissingAttributeException when you read an attribute that was never retrieved from the database or does not exist on the model.
Turning It On
The simplest approach is Model::shouldBeStrict(), which enables all three guards at once. Call it in the boot() method of your AppServiceProvider:
use Illuminate\Database\Eloquent\Model;
public function boot(): void
{
Model::shouldBeStrict();
}
You almost certainly do not want these exceptions crashing real users in production. Pass a boolean so the guards only fire outside of production:
use Illuminate\Support\Facades\App;
public function boot(): void
{
Model::shouldBeStrict(! App::isProduction());
}
Seeing It Catch a Bug
With strict mode on, this innocent-looking Blade loop now throws immediately:
$posts = Post::all();
foreach ($posts as $post) {
echo $post->author->name; // LazyLoadingViolationException
}
The fix is to declare what you need up front with eager loading:
$posts = Post::with('author')->get();
Enabling Guards Individually
If you want finer control, call the guards separately. This is handy when you want to catch lazy loading everywhere but are not ready to enforce the attribute rules yet:
Model::preventLazyLoading(! App::isProduction());
Model::preventSilentlyDiscardingAttributes(! App::isProduction());
Strict mode is one of those settings you turn on once and forget, and then it quietly saves you from shipping a slow page months later. Add it to a fresh project's service provider before you write a single query.
Further Reading
- Laravel documentation, "Eloquent: Getting Started", Configuring Eloquent Strictness (laravel.com/docs/13.x/eloquent#configuring-eloquent-strictness)
- Laravel documentation, "Eloquent Relationships", Preventing Lazy Loading (laravel.com/docs/13.x/eloquent-relationships#preventing-lazy-loading)