Avoiding N+1 Problems in Laravel Like a Pro

Topics: Laravel, PHP on May 23, 2025

Getting started

One of the most common performance pitfalls in Laravel is the N+1 query problem.
It sneaks in silently and slows down your app dramatically, especially when working with Eloquent relationships.

In this tutorial, I’ll show how to detect and fix N+1 issues using eager loading and some cool tools.

Let’s assume we have the classic example of Post and Comment.

// models/Post.php
public function comments()
{
    return $this->hasMany(Comment::class);
}

The N+1 Trap

If you’re doing something like this:

$posts = Post::all();

foreach ($posts as $post) {
    echo $post->comments->count();
}

Laravel will run 1 query to get the posts...
...and then 1 query per post to get its comments. That’s N+1 queries 😱

So if you have 100 posts, you're running 101 queries.


Get to work

To fix that, you want to eager load the comments:

$posts = Post::with('comments')->get();

foreach ($posts as $post) {
    echo $post->comments->count();
}

Now Laravel will only run 2 queries:

  • One for the posts
  • One for all the comments that belong to those posts

Much better 🚀


Nested Relations? No problem.

You can go deep like this:

$posts = Post::with('comments.author')->get();

This will eager-load:

  • comments for each post
  • and the author of each comment

Let’s catch them automatically

Use Laravel Debugbar in dev to see queries visually:

composer require barryvdh/laravel-debugbar --dev

Or go deeper with:

php artisan tinker

DB::enableQueryLog();
$posts = Post::all();

dd(DB::getQueryLog());

Pro tip: Use load() when you already queried

If you already pulled the posts but forgot to eager-load:

$posts->load('comments');

Useful in controllers or service layers.


Real-world case

Let’s say you have this Blade template:

@foreach($users as $user)
    {{ $user->company->name }}
@endforeach

👀 Boom. N+1 right there.

You fix it in the controller like this:

$users = User::with('company')->get();

TL;DR

  • Use with() to eager-load relationships.
  • Use load() if you already queried.
  • Use Laravel Debugbar to spot issues fast.
  • Avoid querying inside loops.

In the next post we’ll cover how to lazy load only when needed, and use LazyCollection for big datasets.
Stay tuned 🔥