How to Force Eager Loading and Prevent N+1 Issues in Laravel

How to Force Eager Loading and Prevent N+1 Issues in Laravel

Introduction

In a past article, I talked about the performance improvements that you can achieve by using “eager loading” in your Laravel database queries. For anyone who hasn’t read it, click here if you want to give it a quick read.

What is Eager Loading?

When you are fetching any models from the database and then doing any type of processing on the model’s relations, it’s important that you use eager loading. Eager loading is super simple using Laravel and basically prevents you from encountering the N+1 problem with your data. This problem is caused by making N+1 queries to the database, where N is the number of items being fetched from the database. To explain this better and give it some context, let’s check out the example below.

$comments = Comment::all(); foreach ($comments as $comment) {
print_r($comment->author->name);
}
$comments = Comment::with('authors')->get();foreach ($comments as $comment) {
print_r($comment->author->name);
}

How to Force Laravel to Use Eager Loading

A new feature (added by Mohamed Said) has recently been merged into the Laravel codebase that allows you to prevent lazy loading taking place. This feature is incredibly useful because it should help to ensure that the relationships are eager loaded. As a result of this, it will likely help us to improve performance and reduce the amount of queries that are made to the database as shown in the example above.

Model::preventLazyLoading();
namespace App\Providers;use Illuminate\Support\ServiceProvider;class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
// ...
Model::preventLazyLoading();
// ...
}
}

Allowing Eager Loading in Production Environments

It’s possible that you might only want to enable this feature when in your local development environment. By doing that, it can alert you to places in your code that’s using lazy loading while building new features, but not completely crash your production website. For this very reason, the preventLazyLoading() method accepts a boolean as an argument, so we could use the following line:

Model::preventLazyLoading(! app()->isProduction());
namespace App\Providers;use Illuminate\Support\ServiceProvider;class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
// ...
Model::preventLazyLoading(! app()->isProduction());
// ...
}
}

What Happens If We Try to Lazy Load?

If we have the feature enabled in our service provider and we try to lazy load a relationship on a model, an Illuminate\Database\LazyLoadingViolationException exception will be thrown.

$comments = Comment::all();foreach ($comments as $comment ) {
print_r($comment->author->name);
}
$comments = Comment::with('authors')->get();foreach ($comments as $comment) {
print_r($comment->author->name);
}

Conclusion

In my personal opinion, I think this feature is going to be really useful and it will likely help to encourage better model and database query practices. I have a feeling that this is going to be a feature that I use a daily basis and will likely think “ How did I use to survive without this?”.

I’m a Laravel web developer based in the UK. I specialise in building websites and systems for small businesses to help them grow and increase their sales.