Eager-Loading Comments in Laravel Eloquent with Constraints to Fix N+1 Queries
Learn how to use Laravel Eloquent’s constrained eager loading to eliminate N+1 queries when loading related data such as approved comments, with a concrete example and trade‑offs.
07 Sept 2025, 23:04 UTC

Problem: N+1 queries when listing posts with comments
When you display a list of blog posts and want to show each post’s approved comments, a naive Eloquent loop triggers one query for the posts and then an additional query for every post to fetch its comments. This pattern is known as the N+1 query problem and quickly degrades performance as the number of posts grows.
How constrained eager loading solves it
Eloquent’s with() method can load relationships in advance. By passing a closure you can add constraints such as a where clause or limit the columns with select. The generated SQL then contains a single JOIN‑like query for the relationship, regardless of how many parent rows you have.
Worked example: posts with approved comments
use App\Models\Post;
use Illuminate\Support\Facades\DB;
// Enable the query log so we can inspect what was executed
DB::enableQueryLog();
$posts = Post::with(['comments' => function ($query) {
$query->where('approved', true)
->select('id', 'post_id', 'body');
}])->get();
// After the call, inspect the log
$queries = DB::getQueryLog();
// For debugging you could return the count
return response()->json([
'postCount' => $posts->count(),
'queryCount' => count($queries),
'queries' => $queries
]);
Assuming you have 25 posts, the log will show exactly two queries: one that selects the posts and another that fetches only the approved comment columns for all those posts. No matter how many posts you add, the query count stays at two.
Trade‑off and limitation
While eager loading removes the extra queries, the closure’s constraints still dictate how much data is pulled. If you omit the select or use a loose where, Eloquent will hydrate full comment models, which can increase memory consumption. In large result sets this may offset the query‑count gain, especially when the relationship table is wide.
How to verify the improvement
- Create a fresh Laravel project (or use an existing one) and seed a
poststable and acommentstable with a foreign keypost_id. - Insert a handful of posts and, for each post, several comments with mixed
approvedvalues. - Add a route that runs the code snippet above.
- Hit the route with a tool like curl or a browser and inspect the JSON response. The
queryCountshould be 2. - Optionally, call
memory_get_usage()before and after theget()call to see the impact of theselectclause; compare it to a version that omits theselectto observe the difference.
If you see more than two queries, double‑check that the closure is correctly placed inside the with array and that you are using Laravel 5.2 or newer (the closure syntax for constrained eager loading was introduced in 5.2).
Closing
Constrained eager loading is a straightforward way to eliminate the N+1 problem when you need related data with specific filters or column selections. By pairing with() with a closure you keep the query count low while still controlling exactly what Eloquent hydrates. Remember to inspect the generated SQL, watch memory usage when you broaden the constraints, and keep your Laravel version up to date to enjoy the full feature set.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.