Eager loading related models using Eloquent's with() method is the primary strategy to eliminate N+1 query bottlenecks in Laravel, fetching all associated data in a minimal number of queries. This technique dramatically reduces database load by converting many individual queries into a few optimized ones.
Here's a breakdown of how to implement eager loading effectively:
1. Understand the N+1 Problem: An N+1 query occurs when you retrieve a collection of parent models and then iterate through them, executing a separate query for each parent to fetch its related child models. For N parent models, this results in 1 (parent query) + N (child queries) total database hits.
2. Basic Eager Loading with with(): Use the with() method on your Eloquent query builder to specify relationships that should be loaded. Eloquent will execute one query for the parent models and a separate, optimized query for all related child models, joining them in memory.
```php
// N+1 problem example:
// $users = App\Models\User::all();
// foreach ($users as $user) {
// echo $user->posts->count(); // Each $user->posts triggers a query
// }
// Eager loading solution (2 queries total):
$users = App\Models\User::with('posts')->get();
foreach ($users as $user) {
echo $user->posts->count(); // Posts are already loaded
}
```
3. Eager Loading Multiple Relationships: Pass an array of relationship names to with() to load several relationships simultaneously.
```php
$users = App\Models\User::with(['posts', 'profile'])->get();
```
4. Nested Eager Loading: Use dot notation to eager load nested relationships.
```php
$users = App\Models\User::with('posts.comments')->get();
```
5. Conditional Eager Loading: Apply constraints to eager-loaded relationships by passing a closure to the with() method. This allows filtering the related models before they are loaded.
```php
$users = App\Models\User::with(['posts' => function ($query) {
$query->where('published', true)->orderBy('published_at', 'desc');
}])->get();
```
6. Lazy Eager Loading: If you already have an Eloquent model or collection instance, you can eager load relationships using the load() or loadMissing() methods. loadMissing() only loads relationships that haven't been loaded yet.
```php
$user = App\Models\User::find(1);
$user->load('posts'); // Loads posts for a single model
$users = App\Models\User::all();
$users->loadMissing('posts'); // Loads posts for a collection, if not already loaded
```
N+1 vs. Eager Loading Comparison
| Feature | N+1 Problem | Eager Loading (with()) |
| :---------------- | :------------------------------------------- | :---------------------------------------------------- |
| Queries Executed | 1 + N (where N is number of parent records) | 2 (1 for parents, 1 for all related children) |
| Performance | Poor, especially with large N | Excellent, optimized for bulk data retrieval |
| Database Load | High, many small, inefficient queries | Low, fewer, larger, more efficient queries |
| Code Complexity | Often appears simpler initially, but hides perf issues | Slightly more explicit, but prevents hidden perf issues |
Eager loading is crucial for optimizing read-heavy operations. Always analyze your queries, for example using EXPLAIN ANALYZE on the resulting SQL, to confirm the effectiveness of your eager loading strategies.