My posts index page is slow. The debug bar shows 101 queries for 100 posts, one extra query per post to load the author. How do I fix this and how do I stop it from happening again in Laravel 12?
DEV3 Developer Forum
How do I find and fix N+1 queries in Eloquent?
Every post is reviewed first. Usually reviewed within 24 hours.
1 reply
That is the classic N+1 problem: one query loads the posts, then lazy loading $post->author runs one more query for each row.
Fix: eager load
$posts = Post::with(['author', 'tags'])->latest()->paginate(20);
This runs 3 queries (4 with the paginate() count query) no matter how many posts there are. Nested relations use dot notation: with('comments.user'). Select only the columns you need, but always include the foreign key:
Post::with('author:id,name')->get();
Aggregates without loading rows
Post::withCount('comments')->withSum('orderItems', 'qty')->get();
Load later when the collection already exists
$posts->load('author'); // always
$posts->loadMissing('author'); // only if not loaded
Make N+1 fail loudly in development
In AppServiceProvider::boot():
Model::preventLazyLoading(! app()->isProduction());
Any lazy load then throws a LazyLoadingViolationException, so you find the problem in development instead of production. Recent Laravel 12 releases (12.8 and later) also offer opt-in automatic eager loading: call Model::automaticallyEagerLoadRelationships() in boot() (it is off by default), or ->withRelationshipAutoloading() on a single query or collection. It is a handy safety net, but explicit with() is clearer and what most teams use.
Pitfalls
- Calling
$post->comments()->count()inside a loop is still one query per row. UsewithCount. - Accessors that touch a relation cause hidden N+1. Eager load what the accessor needs.
whereHasfilters but does not load. You still needwith()if you display the relation.- Check the real count with Laravel Debugbar or Telescope, or
DB::listen.