Eliminating the N+1 Problem in Laravel Applications
Understanding the N+1 Query Trap
The N+1 problem occurs when your application executes one query to fetch a parent record and then executes a separate query for every child record associated with it. In a
Prerequisites
To follow this guide, you should have a baseline understanding of
Key Libraries & Tools
- Laravel: A robust PHP framework providing the Eloquent ORM.
- Eloquent ORM: The database mapper used to interact with your data via PHP classes.
- DB Facade: A tool used here to enable query logging for performance monitoring.
Solving the Issue with Eager Loading
The most direct solution is using the with() method. This instructs WHERE IN statement.
// Instead of lazy loading, we eager load the relationship
$products = Product::with('prices')->get();
If you find yourself always needing a relationship, you can automate this by adding the $with property to your
class Product extends Model {
protected $with = ['prices', 'variants'];
}
Granular Control: Without and WithOnly
Sometimes, global eager loading is overkill. If you have the $with property set but want to exclude a specific relation for a single request, use the without() method. Conversely, withOnly() overrides the model's defaults entirely, ensuring you only pull exactly what you need for that specific execution context.
Enforcing Best Practices with PreventLazyLoading
Model::preventLazyLoading(). By placing this in your AppServiceProvider, the framework will throw a LazyLoadingViolationException whenever you accidentally trigger an N+1 query.
public function boot()
{
Model::preventLazyLoading(! app()->isProduction());
}
This configuration is a developer's best friend. It forces you to write clean, eager-loaded code during development while ensuring your production environment remains stable for end users.
Syntax Notes & Tips
- Query Logging: Use
DB::enableQueryLog()andDB::getQueryLog()to verify exactly how many queries your code triggers. - Array Syntax: The
$withproperty and thewith()method both accept arrays, allowing you to load multiple relationships simultaneously. - Nested Loading: You can eager load nested relations using dot notation, such as
with('prices.currency').
