How to Fix the N+1 Query Problem in Laravel 11 — With Real Examples
What is the N+1 Query Problem?
The N+1 query problem occurs when application code executes one database query to fetch a parent record (the "1"), and then executes subsequent queries for each child record (the "N") inside a loop.
If you fetch 100 records, your application ends up executing $100 + 1 = 101$ database queries instead of just 2. This creates massive database overhead and latency.
Real Code Example: The N+1 Problem in Action
Imagine an exhibition management system where an Exhibitor has many ExhibitionStands.
The Models
// App/Models/Exhibitor.php
public function stands() {
return $this->hasMany(ExhibitionStand::class);
}
The Unoptimized Controller Logic
// Fetching exhibitors without eager loading
$exhibitors = Exhibitor::limit(5)->get();
foreach ($exhibitors as $exhibitor) {
// Each iteration fires a new query to fetch the stands
echo $exhibitor->name . ' - Stand Size: ' . $exhibitor->stands->first()?->size;
}What Happens in the Background?
Laravel executes the following SQL queries behind the scenes:
SQL
SELECT * FROM exhibitors LIMIT 5; -- (The "1" Query) SELECT * FROM exhibition_stands WHERE exhibitor_id = 1; -- (N Query 1) SELECT * FROM exhibition_stands WHERE exhibitor_id = 2; -- (N Query 2) SELECT * FROM exhibition_stands WHERE exhibitor_id = 3; -- (N Query 3) SELECT * FROM exhibition_stands WHERE exhibitor_id = 4; -- (N Query 4) SELECT * FROM exhibition_stands WHERE exhibitor_id = 5; -- (N Query 5)
If there are 500 exhibitors, the app will hit the database 501 times.
Fix 1: Eager Loading with with()
The primary solution to this issue is Eager Loading. By using the with() method, Laravel instructs Eloquent to fetch the relationship data in a single query using an upstream SQL IN statement.
PHP
// Optimized Controller Logic
$exhibitors = Exhibitor::with('stands')->limit(5)->get();
foreach ($exhibitors as $exhibitor) {
echo $exhibitor->name . ' - Stand Size: ' . $exhibitor->stands->first()?->size;
}The Optimized Background Queries
Now, the application executes exactly 2 queries, regardless of how many records exist:
SQL
SELECT * FROM exhibitors LIMIT 5; SELECT * FROM exhibition_stands WHERE exhibitor_id IN (1, 2, 3, 4, 5);
Fix 2: Optimizing Aggregates with withCount()
Sometimes the actual relationship data isn't needed—only the total number of child records matters (e.g., displaying how many stands an exhibitor owns). Loading the entire relationship just to count them wastes server memory.
Instead of with('stands'), use withCount() to let the database handle the aggregation directly via a subquery.
PHP
// Fetches exhibitors and attaches a "stands_count" attribute
$exhibitors = Exhibitor::withCount('stands')->get();
foreach ($exhibitors as $exhibitor) {
echo $exhibitor->name . ' has ' . $exhibitor->stands_count . ' stands.';
}
Fix 3: Lazy Eager Loading with load()
There are scenarios where records must be loaded conditionally, or the parent model instance is already instantiated (like within a Laravel Blade component or an API resource). In these instances, use Lazy Eager Loading via the load() method.
PHP
$exhibitors = Exhibitor::all();
if ($needStandDetails) {
// Eager loads the relationship only when the condition is met
$exhibitors->load('stands');
}
Fix 4: How to Detect N+1 Queries Automatically
Manually inspecting code for N+1 issues can be tedious. Use these automated methods to catch them during development.
Method A: Use Laravel Debugbar
Install barryvdh/laravel-debugbar in the development environment. It provides a visual timeline in the browser showing the exact number of queries executed per request, highlighting duplicates.
Method B: Strict Loading Prevention in Laravel 11
Laravel allows developers to completely prohibit lazy loading during development. Add the following logic to the AppServiceProvider.php file:
PHP
// App/Providers/AppServiceProvider.php
public function boot(): void
{
// Throws an exception if an N+1 query is triggered during development
Model::preventLazyLoading(! app()->isProduction());
}
If an N+1 query slips into the code, Laravel throws a LazyLoadingViolationException instantly, ensuring it is fixed before reaching production.
Fix 5: Avoiding Unnecessary Columns with select()
Eager loading with('stands') fetches every single column (SELECT *) from the related table. If the table contains heavy text fields or metadata, it increases memory usage unnecessarily.
Refine eager loading queries to select only the necessary columns. Note: Always include the foreign key (exhibitor_id) in the selection, or Eloquent will not be able to map the relationship.
PHP
$exhibitors = Exhibitor::with('stands:id,exhibitor_id,size,cost')->get();
Performance Comparison: Before vs. After
The impact of fixing N+1 queries scales dramatically with data volume. Consider a page rendering a list of 500 records:
Metric | Unoptimized (Lazy Loading) | Optimized (Eager Loading) | ImpactDatabase Queries | 501 queries | 2 queries | 99.6% Reduction
Memory Usage | High (creates 500 query instances) | Low (reuses collection arrays) | Lower RAM footprint
Page Load Time | ~1.4 seconds | ~180 milliseconds | 87% Faster Response
When NOT to Eager Load (Avoiding Over-fetching)
Eager loading is highly effective, but it should not be applied universally. Avoid eager loading when:
- The relationship data is massive: If an exhibitor has 10,000 logs or past interaction records, eager loading them all into memory at once can cause a PHP memory exhaustion error. Use pagination for child records instead.
- The relationship is rarely accessed: If the child data is only displayed on a rare toggle-click, load it asynchronously via an API call or use lazy eager loading (load()) when requested.
Wrap Up
Fixing N+1 queries is one of the fastest ways to optimize a Laravel application. By transitioning from lazy loading to with(), leveraging withCount(), and enabling strict loading checks in development, application backends remain performant and scalable.
🚀 Need a Laravel Developer Who Writes Optimized Code?
If your application is running slow, struggling with database bottlenecks, or requires a custom business tool built to professional performance standards, let's fix it.[Let's Connect in Noida to Optimize Your Web Application]
https://aruntyagi.com/laravel-development
Related Posts
PHP Laravel Framework Deep Dive
Explore the powerful features of Laravel framework and how to leverage them in your projects.
Laravel Developer in Dubai – Arun Tyagi Web Development Solutions
In today’s competitive digital landscape, businesses in Dubai need fast, secure, and scalable web ap...