Optimizing Eloquent Relationships at Scale: Advanced Eager Loading Techniques

B
B Vikas Chandra
Author
43 views 4 min read min read
laravel php web development backend development software engineering laravel eloquent eager loading n+1 query problem database optimization laravel performance query optimization laravel best practices app performance optimization performance tuning efficient code
Optimizing Eloquent Relationships at Scale: Advanced Eager Loading Techniques

Introduction

If your Laravel application is slowing down as your database grows, the culprit might not be your server—it’s often your database queries. Specifically, the notorious N+1 query problem.

Eloquent makes working with relationships simple and elegant, but without proper optimization, it can generate hundreds of unnecessary queries. In this blog, we’ll explore advanced eager loading techniques to fix these issues and scale your Laravel app efficiently.

Understanding the N+1 Query Problem

Let’s start with an example. Suppose you have a Post model and each post has many comments.

$posts = Post::all();

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

It looks simple—but behind the scenes, this code runs 1 query to get posts + 1 query per post to fetch comments. If you have 100 posts, that’s 101 queries!

The Fix: Basic Eager Loading

Eager loading helps by loading all related models in a single query:

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

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

Now Laravel executes just 2 queries — one for posts, one for comments.

That’s a massive performance improvement. But we can take this even further.

Nested Eager Loading (Loading Relationships of Relationships)

Sometimes, you need relationships within relationships.

For example, fetching comments along with the users who wrote them:

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

This loads posts, their comments, and each comment’s user in just three optimized queries — regardless of dataset size.

Selective Eager Loading (Only What You Need)

Instead of fetching all columns, select only the fields you need:

$posts = Post::with(['comments:id,post_id,body'])->get();

This ensures Eloquent doesn’t waste memory or time retrieving unnecessary data like timestamps or metadata.

Conditional Eager Loading

You can also apply conditions to eager loading:

$posts = Post::with(['comments' => function($query) {
    $query->where('approved', true);
}])->get();

This is handy when you want only specific related records—like approved comments or active users—improving query speed even more.

Lazy Eager Loading (After the Fact)

Sometimes you need to load relationships after the initial query:

$posts = Post::where('status', 'published')->get();

// Later decide to load comments
$posts->load('comments');

This avoids over-fetching data when you’re not sure what relationships you’ll need upfront.

The Power of loadMissing()

When you’re working with partially loaded relationships, loadMissing() ensures you don’t re-fetch already loaded data:

$post->loadMissing('comments.user');

Laravel intelligently loads only the missing relationships, saving unnecessary database calls.

Real-World Example: Optimizing User Profiles with Posts and Activities

In one of my Laravel projects, I worked on a user dashboard where each user had:

  • Multiple posts
  • Profile details
  • Recent activities

Initially, the code looked like this:

$users = User::all();

foreach ($users as $user) {
    echo $user->posts->count();
    echo $user->activities->count();
}

At first, it worked fine — but as the database grew to 10,000+ users, the page started taking 10–15 seconds to load.

When I inspected the queries using Laravel Debugbar, I realized there were tens of thousands of queries happening because of the N+1 problem.

Fixing It with Advanced Eager Loading

I refactored the code like this:

$users = User::with([
    'posts:id,user_id,title,status',
    'activities:id,user_id,action,created_at'
])->get();

This reduced the queries from 20,001 to just 3, drastically improving the load time to under 1 second.

Additional Optimization

To make it even more efficient, I added a condition to only fetch recent activities:

$users = User::with(['activities' => function ($query) {
    $query->latest()->limit(5);
}])->get();

Now, each user’s dashboard shows their 5 most recent activities, and the database only loads relevant data.

 Result

  • Query count: Reduced by 99%
  • Response time: From 15s → 900ms
  • Server CPU load: Dropped significantly

This was a perfect example of how advanced eager loading can transform a sluggish dashboard into a smooth, production-ready experience.

Conclusion

Optimizing Eloquent relationships isn’t just about cleaner code—it’s about making your application scalable and efficient.

By mastering eager loading, nested relationships, and conditional queries, you’ll eliminate the N+1 problem and make your Laravel app run smoother than ever.

Key Takeaways

  1. Use with() for eager loading related data
  2. Avoid fetching unnecessary columns
  3. Apply conditions to limit results
  4. Use load() and loadMissing() for flexibility
  5. Profile queries using Debugbar or Telescope


Join the conversation
43 views
0 comments
Nov 02, 2025

Comments

0
No comments yet

Be the first to start the discussion!