Laravel Magazine
Catch N+1 Queries Early with Laravel's Strict Model Mode

Catch N+1 Queries Early with Laravel's Strict Model Mode

Eric Van Johnson ·

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

Stay Updated

Subscribe to our newsletter

Get latest news, tutorials, community articles and podcast episodes delivered to your inbox.

Weekly articles
We send a new issue of the newsletter every week on Friday.
No spam
We'll never share your email address and you can opt out at any time.