Laravel cookbook · Intermediate

Catch N+1 queries with strict Eloquent mode

Pages slow down as data grows because relations are lazy loaded in loops.

Create a free account to save this page, take notes and track your progress. Sign in

Approach

Turn on strict mode outside production so lazy loading, silently discarded attributes and missing attributes throw. Fix the offenders with with(), withCount() and load().

Solution

<?php
// app/Providers/AppServiceProvider.php
namespace App\Providers;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        Model::shouldBeStrict(! $this->app->isProduction());

        // In production, log lazy loading instead of throwing.
        Model::handleLazyLoadingViolationUsing(function (Model $model, string $relation) {
            logger()->warning('Lazy loaded relation', ['model' => $model::class, 'relation' => $relation]);
        });
    }
}
<?php
use App\Models\Post;

// Before: 1 query for posts + 1 per post for author + 1 per post for comment counts
// After: 3 queries total
$posts = Post::query()
    ->with(['author:id,name', 'tags'])
    ->withCount('comments')
    ->latest()
    ->paginate(20);

Pitfalls

  • Enabling strict mode only in tests, so lazy loading in rarely tested pages slips through.
  • Eager loading whole relations when only a count or a few columns are needed.
  • load() inside loops, which is the same N+1 problem.

Variations

  • Model::automaticallyEagerLoadRelationships() eager loads relations on first access for whole collections.
  • Laravel Debugbar or Telescope show query counts per request.

Further reading: Preventing lazy loading

Reference: https://laravel.com/docs/13.x/eloquent-relationships#preventing-lazy-loading

More Laravel recipes

Was this page helpful?