Get "PHP 8 in a Nuthshell" (Now with PHP 8.4)
Amit Merchant

Amit Merchant

A blog on PHP, JavaScript, and more

Preventing N+1 issues globally in Laravel using Auto Eager Loading

Eager loading in Laravel is a way to load related models when you query a model. This is done to prevent the N+1 problem, which occurs when you load a model and then load its related models one by one, resulting in multiple queries to the database.

This can be a performance issue, especially when you have many related models. Eager loading allows you to load all the related models in a single query, which can significantly improve performance.

For example, if you have a User model that has many Post models, you can eager load the posts like this:

$users = User::with('posts')->get();

foreach ($users as $user) {
    // This will not run a separate query for each user
    echo $user->posts->title; 
}

This will load all the users and their posts in a single query, instead of loading each user’s posts one by one like so.

$users = User::all();

foreach ($users as $user) {
    // This will run a separate query for each user
    echo $user->posts->title; 
}

This will result in an N+1 problem, where you will have one query to load all the users and then one query for each user to load their posts.

This is great but as you can tell, eager loading is not always the default behavior in Laravel. You have to explicitly tell Laravel to eager load the related models using the with() method.

To fix this, Laravel now has a way to enable eager loading globally that will automatically eager load the related models for you.

Auto Eager Loading Globally

Laravel 12.x introduced (contributed by Serhii Litvinchuk) a new method called automaticallyEagerLoadRelationships() on the Model class. This method allows you to enable auto eager loading across all the models in your application.

Important: This feature is still in beta and the Laravel team is gathering community feedback on this. So, you may expect some changes in this functionality in the future.

You can enable this feature by adding the following code to your AppServiceProvider:

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function boot()
    {
        Model::automaticallyEagerLoadRelationships();
    }
}

So, now, even the problematic code with N+1 issues, as I showed earlier, will work without any issues.

// Since auto eager loading is enabled globally, 
// this will work without any N+1 issues
$users = User::all();

foreach ($users as $user) {
    echo $user->posts->title; 
}

Auto Eager Loading on Models

If you don’t want to enable auto eager loading globally, you can also enable it on a per-model basis. You can do this by adding the automaticallyEagerLoadRelationships() method to your model like so.

use App\Models\User;

User::automaticallyEagerLoadRelationships();

Auto Eager Loading on Queries

And finally, you can also enable auto eager loading on a per-query basis. You can do this by using the withRelationshipAutoloading() method like so.

$users = User::all()->withRelationshipAutoloading();

foreach ($users as $user) {
    echo $user->posts->title; 
}

In closing

I think this is a great feature to have in Laravel and potentially a lifesaver for newbies who are not familiar with eager loading.

You can read more about this feature in this PR.

Learn the fundamentals of PHP 8 (including 8.1, 8.2, 8.3, and 8.4), the latest version of PHP, and how to use it today with my new book PHP 8 in a Nutshell. It's a no-fluff and easy-to-digest guide to the latest features and nitty-gritty details of PHP 8. So, if you're looking for a quick and easy way to PHP 8, this is the book for you.

Like this article?

Buy me a coffee

👋 Hi there! I'm Amit. I write articles about all things web development. You can become a sponsor on my blog to help me continue my writing journey and get your brand in front of thousands of eyes.

Comments?