Laravel Eloquent Eager Loading

- LAST UPDATED

Before Discussing Laravel Eloquent Eager loading, let's have an idea about how queries are generated in Laravel when using relationships. For simplicity in usual relationship queries, it creates multiple queries whenever we fetch a relationship. For example, let's say we have a user in our system, and there are posts in our application where users make posts. Let's say we want to get 10 posts of 10 users and user details using Laravel relationships. In that case, Lazy loading will query the database 11 times.

What is Eager Loading ?

So technically, eager loading is a way to load the data all at once by reducing the number of queries needed compared to Lazy loading without writing extra queries and by just using the Laravel Eloquent relationship. So here, we will fetch all the data once instead of when needed. Sometimes it may be optional when using belongsTo relationship with single data. Whenever more rows are involved in a query, it is preferred to use eager loading to avoid unnecessary load on the query when the number of queries increases. 

Let's see how lazy and eager loading works in Laravel eloquent relationships.

In the example below, we are using the default User model in Laravel. Along with that, we also created 2 new models. Posts model and Category model. The User model has hasMany relationship with Post (This is not ideal if the User has so many posts). The Post model has belongsTo relationship with the Category model.

User Model

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;


class User extends Authenticatable
{
    /**
     * All posts.
     */
    public function posts()
    {
        return $this->hasMany(Post::class);
    }
}

Post Model

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    use HasFactory;

    /**
     * All posts.
     */
    public function category()
    {
        return $this->belongsTo(Category::class);
    }
}

Category Model

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Category extends Model
{
    use HasFactory;
}

So we have some relational data in our database. Let's see a typical scenario with lazy loading when fetching data of 10 users with their posts and category. The below relation will run almost 111 queries.

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\User;

class IndexController extends Controller
{
    /**
     * Handle the incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function __invoke(Request $request)
    {
        $users = User::query()->limit(10)->get();
        foreach ($users as $user) {
            $postCount = $user->posts->count();
            echo $user->name.' has '.$postCount;
            if($postCount > 0){
                foreach ($user->posts as $post) {
                    echo $post->heading.' under '.$post->category->heading;
                }
            }
        }
    }
}

So the lazy loading will 111 queries for fetching data. 

Lets see another scenario if we are fetching just posts and omitting the category part of the query.

$users = User::query()->limit(10)->get();
foreach ($users as $user) {
    $postCount = $user->posts->count();
    echo $user->name.' has '.$postCount;
    if($postCount > 0){
        foreach ($user->posts as $post) {
            echo $post->heading;
        }
    }
}

So the above case leads to around 11 queries. So here we can see there is a case of n+1 queries. To avoid this scenario, we can use the Laravel "with" method, which helps reduce the number of queries by using in queries in backend instead of running queries when data is used in the application.

How to Use Eager Loading in Laravel Eloquent

We can do eager loading in Laravel using the Laravel query builder "with" method. Using "with" in a query with a relationship name will reduce the number of queries by fetching all relational data using a single query.

$users = User::query()->with('posts')->limit(10)->get();
foreach ($users as $user) {
    $postCount = $user->posts->count();
    echo $user->name.' has '.$postCount;
    if($postCount > 0){
        foreach ($user->posts as $post) {
            echo $post->heading;
        }
    }
}

Let's update the data query using the "with" method and just fetching user details and user posts. Here we used a "with" method just using the 'posts' relationship. It avoids the n+1 queries issue and will create only 2 queries. So we can see a drastic change of almost 90% when using a single relation.

Laravel eager can be used with nested relationships as well as we can use it along with multiple relationships as well. Lets see category example as reference. So we are updating the with relationship by updating posts to posts.category. So the final code will looks like this.

$users = User::query()->with('posts.category')->limit(10)->get();
foreach ($users as $user) {
    $postCount = $user->posts->count();
    echo $user->name.' has '.$postCount;
    if($postCount > 0){
        foreach ($user->posts as $post) {
            echo $post->heading.' under '.$post->category->heading;
        }
    }
}

So we can see the change here in with statement. This will reduce 111 queries to just 3 queries. 

So this is clearly visible that, number of queries has been reduced almost 98% here. But here you can see a large in query being used for getting category details. That may consume much more memory than getting 1 or 2 queries, But this approach will defintely reduce server load by reducing the number querise being executed in the server.

Eager Loading Multiple Relationships

We can eager load multiple relationships using laravel with. To achive this we just needs to pass an array to with method. So in the above example we need to fetch account details of user along with posts and categories, in that case we can use it like this.

$users = User::query()->with(['posts.category','account'])->limit(10)->get();

In the above example we added a new relationship to the with method to fetch account along with user details.

Advantages of Using Eager Loading

  • So eager loading will help us to reduce the number of queries.
  • The above scenario will reduce the load on the server and database
  • It could help reduce codes as we fetch data in a single method instead of multiple statements.
  • Very useful in the case of APIs, relational data can be sent to the front-end application in a single request instead of using multiple requests.

So eager loading is pretty useful when it is used properly. It also has got some disadvantages. As eager loading will only return results after all queries, sometimes it may lead to slow application loading when many relations are used in a single Laravel Eloquent query. This also increases memory usage, as we fetched multiple rows of data in a single query using eager loading. This is okay in most cases as we are reducing the number of queries being executed.