Querying Related Data: Mastering Eager Loading in Laravel
Stop the N+1 query problem in its tracks. Learn how to use eager loading in Laravel to keep your application fast and efficient as your data grows.
Previously in this course, we covered the introduction to database relationships in Laravel, where we defined how our User and Task models interact. In this lesson, we are going to move beyond simply defining those relationships and learn how to query them efficiently.
When you start fetching related data, you'll inevitably run into the "N+1" problem. If you’ve ever noticed your application slowing down as you add more records, this is almost certainly the culprit.
The N+1 Query Problem Explained
Imagine you want to display a list of all your tasks, along with the name of the user who created each task. If you do this in a loop, your code might look like this:
PHP#6A9955">// In your controller $tasks = Task::all(); #6A9955">// In your Blade view @foreach ($tasks as $task) <p>{{ $task->title }} - Created by: {{ $task->user->name }}</p> @endforeach
On the surface, this looks correct. However, here is what happens under the hood:
- Laravel runs one query to fetch all tasks (
SELECT * FROM tasks). - For every single task in that collection, Laravel executes a separate query to fetch the user (
SELECT * FROM users WHERE id = ?).
If you have 50 tasks, you are firing 51 queries to the database (1 + 50). This is the N+1 problem. As your application scales, this will destroy your performance.
Solving N+1 with Eager Loading
Eager loading tells Laravel to fetch the related models upfront using a single additional query. Instead of running a query inside the loop, Laravel will perform two queries total: one for the tasks and one for all the users associated with those tasks.
To implement this, we use the with() method in our controller:
PHP#6A9955">// Optimized controller code $tasks = Task::with('user')->get();
By adding with('user'), Laravel intelligently gathers all the user_id values from the tasks and executes a single query like SELECT * FROM users WHERE id IN (1, 2, 3...). The performance difference is massive, especially when dealing with hundreds or thousands of records.
Querying Based on Relationships
Sometimes you don't just want to load related data; you want to filter your results based on them. For example, in our Task Manager, you might want to show only tasks that have a specific category or only tasks where the user is an "admin."
We use the has() or whereHas() methods for this.
Using has()
If you want to retrieve only tasks that have at least one comment (assuming you added a comment relationship later):
PHP$tasks = Task::has('comments')->get();
Using whereHas()
This is more powerful because it allows you to add constraints to the related model. Suppose you want to find all tasks created by users whose name starts with "John":
PHP$tasks = Task::whereHas('user', function ($query) { $query->where('name', 'like', 'John%'); })->get();
This allows you to perform complex filtering without manually joining tables, keeping your code readable and maintainable.
Hands-on Exercise
- Open your
TasksControllerin your Task Manager project. - Locate the index method where you currently fetch tasks.
- Update the query to eager load the
userrelationship usingTask::with('user')->get(). - Open the
tasks/index.blade.phpfile and ensure you are displaying the user's name. - Check your Laravel Debugbar (if installed) or your database logs to verify that the number of queries has dropped significantly.
Common Pitfalls
- Eager Loading Everything: Don't use
with()for every relationship by default. Only load what you actually need in the view. Loading unnecessary data consumes memory and slows down your application. - Forgetting the Relationship Name: The string passed to
with()must match the exact method name defined in your model. If your method ispublic function owner(), you must callwith('owner'), notwith('user'). - N+1 in API Responses: This problem isn't limited to Blade views. If you are returning JSON from an API, make sure to use
with()there as well, otherwise, your JSON serialization will trigger the same N+1 queries.
Recap
We’ve covered the essential tools for working with related data:
- Eager Loading (
with): Use this to prevent the N+1 query problem by batch-fetching relationships. - Relationship Querying (
whereHas): Use this to filter primary records based on conditions applied to their related models.
By mastering these techniques, you ensure your Task Manager remains performant even as your database grows.
Up next: We will dive into Handling File Uploads to allow users to attach files to their tasks.
Work with me

Laravel REST API Development
Clean, secure, well-documented Laravel REST APIs — the backend engine for your app, mobile client, or SaaS. Built by an API specialist.

FilamentPHP Admin Panel & Dashboard Development
A powerful admin panel for your Laravel app — built with FilamentPHP so you can manage everything without touching the database.