Eager Load Relationships on an Existing Model in Laravel

Ep#30@Laracasts: Eager Load Relationships on an Existing Model

This post is a part of the Week X of 100DaysOfCode Laravel Challenge series. We are building the Blog project while following the Laravel 8 from Scratch series on Laracasts with the 100Day…


This content originally appeared on DEV Community and was authored by Arif Iqbal

Ep#30@Laracasts: Eager Load Relationships on an Existing Model

This post is a part of the Week X of 100DaysOfCode Laravel Challenge series. We are building the Blog project while following the Laravel 8 from Scratch series on Laracasts with the 100DaysOfCode challenge.

Lazy Eager Loading

In previous episodes, we fixed the N+1 problem on the blog overview page by Eager loading the Category and User models with the Post model as part of the query.

  • routes/web.php
Route::get('/', function () {
    return view('posts', [
        'posts' => Post::latest()->with("category", "author")->get()
    ]);
});

Now the N+1 problem is again there on the Author and Category pages.

N+1 on author page

The route definitions for these pages are as follows:

  • routes/web.php
Route::get('/categories/{category:slug}', function (Category $category) {
    return view('posts', [
        'posts' => $category->posts
    ]);
});

Route::get('/authors/{author:username}', function (User $author) {
    return view('posts', [
        'posts' => $author->posts
    ]);
});

So, here is the problem. How to load relationships on already-retrieved models like above? The solution is Lazy Eager Loading. Use the load() method with relation names passed as parameters.

  • routes/web.php
Route::get('/categories/{category:slug}', function (Category $category) {
    return view('posts', [
        'posts' => $category->posts->load("author", "category")
    ]);
});

Route::get('/authors/{author:username}', function (User $author) {
    return view('posts', [
        'posts' => $author->posts->load("author", "category")
    ]);
});

12 vs 4. The number of queries reduced from 12 to 4, a big improvement.

Lazy Eager Loading

Eager Loading By Default

Another way is to always load the relationships whenever a model is retrieved. This can be accomplished by introducing a $with property to the model.

  • App\Models\Post
<?php

namespace App\Models;

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

class Post extends Model
{
    use HasFactory;

    protected $guarded = [];

    protected $with = ['category', 'author'];

    public function getRouteKeyName()
    {
        return 'slug';
    }

    public function category() {
        return $this->belongsTo(Category::class);
    }

    public function author() {
        return $this->belongsTo(User::class, "user_id");
    }
}

Now you can remove the Lazy Eager Loading load() methods from the route definitions.

  • route/web.php
Route::get('/categories/{category:slug}', function (Category $category) {
    return view('posts', [
        'posts' => $category->posts
    ]);
});

Route::get('/authors/{author:username}', function (User $author) {
    return view('posts', [
        'posts' => $author->posts
    ]);
});

Reload your blog overview page to verify the N+1 problem still doesn't exist.

Now every time a Post will be retrieved, the related User and Category models will be loaded by default. Let's test in tinker.

Boot up tinker php artisan tinker

>>> Post::first();
=> App\Models\Post {#4287
     id: 1,
     user_id: 1,
     category_id: 1,
     title: "Dignissimos animi aut consequatur aspernatur in libero labore.",
     slug: "similique-repellendus-id-est-et-illum",
     excerpt: "Vitae voluptatibus aut et id blanditiis.",
     body: "Ut dolor sit quia minima.",
     published_at: null,
     created_at: "2021-12-25 14:18:42",
     updated_at: "2021-12-25 14:18:42",
     category: App\Models\Category {#4249
       id: 1,
       title: "magnam",
       slug: "dolor",
       created_at: "2021-12-25 14:18:42",
       updated_at: "2021-12-25 14:18:42",
     },
     author: App\Models\User {#4511
       id: 1,
       name: "John Doe",
       username: "JohnDoe",
       email: "orin79@example.com",
       email_verified_at: "2021-12-25 14:18:42",
       #password: "$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi"
,
       #remember_token: "qFYOFvkIyS",
       created_at: "2021-12-25 14:18:42",
       updated_at: "2021-12-25 14:18:42",
     },
   }
>>>

Notice that the Category and User models loaded by default with the Post model. If you would like to selectively remove one of these loaded related models, you can pass its name to the without() as:

>>> Post::without("category")->first();
=> App\Models\Post {#4515
     id: 1,
     user_id: 1,
     category_id: 1,
     title: "Dignissimos animi aut consequatur aspernatur in libero labore.",
     slug: "similique-repellendus-id-est-et-illum",
     excerpt: "Vitae voluptatibus aut et id blanditiis.",
     body: "Ut dolor sit quia minima.",
     published_at: null,
     created_at: "2021-12-25 14:18:42",
     updated_at: "2021-12-25 14:18:42",
     author: App\Models\User {#4514
       id: 1,
       name: "John Doe",
       username: "JohnDoe",
       email: "orin79@example.com",
       email_verified_at: "2021-12-25 14:18:42",
       #password: "$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi"
,
       #remember_token: "qFYOFvkIyS",
       created_at: "2021-12-25 14:18:42",
       updated_at: "2021-12-25 14:18:42",
     },
   }
>>>

Notice that now the Category model is included but the User is not. Want to exclude the User too?

>>> Post::without("category", "author")->first();
=> App\Models\Post {#3564
     id: 1,
     user_id: 1,
     category_id: 1,
     title: "Dignissimos animi aut consequatur aspernatur in libero labore.",
     slug: "similique-repellendus-id-est-et-illum",
     excerpt: "Vitae voluptatibus aut et id blanditiis.",
     body: "Ut dolor sit quia minima.",
     published_at: null,
     created_at: "2021-12-25 14:18:42",
     updated_at: "2021-12-25 14:18:42",
   }
>>>

Lazy Eager Loading or Eager Loading By Default?

So, what is the better option? When to use which approach? You know the answer better. I mean it depends on the conditions under which you will use one approach or the other.

Lazy eager loading is useful when you are not sure in the first place if the relations will be used. Later at some point, you load load() the relations based on some condition.

On the other hand, if you are sure that the relations will always be needed whenever the main model will be referenced, then better to load them by default using the $with property as explained above.

Thank you for following along with me. Stay tuned for the next article. Comments and suggestions are always appreciated and welcome.

~ Happy Coding!


This content originally appeared on DEV Community and was authored by Arif Iqbal


Print Share Comment Cite Upload Translate Updates
APA

Arif Iqbal | Sciencx (2021-12-26T18:59:01+00:00) Eager Load Relationships on an Existing Model in Laravel. Retrieved from https://www.scien.cx/2021/12/26/eager-load-relationships-on-an-existing-model-in-laravel/

MLA
" » Eager Load Relationships on an Existing Model in Laravel." Arif Iqbal | Sciencx - Sunday December 26, 2021, https://www.scien.cx/2021/12/26/eager-load-relationships-on-an-existing-model-in-laravel/
HARVARD
Arif Iqbal | Sciencx Sunday December 26, 2021 » Eager Load Relationships on an Existing Model in Laravel., viewed ,<https://www.scien.cx/2021/12/26/eager-load-relationships-on-an-existing-model-in-laravel/>
VANCOUVER
Arif Iqbal | Sciencx - » Eager Load Relationships on an Existing Model in Laravel. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2021/12/26/eager-load-relationships-on-an-existing-model-in-laravel/
CHICAGO
" » Eager Load Relationships on an Existing Model in Laravel." Arif Iqbal | Sciencx - Accessed . https://www.scien.cx/2021/12/26/eager-load-relationships-on-an-existing-model-in-laravel/
IEEE
" » Eager Load Relationships on an Existing Model in Laravel." Arif Iqbal | Sciencx [Online]. Available: https://www.scien.cx/2021/12/26/eager-load-relationships-on-an-existing-model-in-laravel/. [Accessed: ]
rf:citation
» Eager Load Relationships on an Existing Model in Laravel | Arif Iqbal | Sciencx | https://www.scien.cx/2021/12/26/eager-load-relationships-on-an-existing-model-in-laravel/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.