So, through 3 sections about Relationships in Laravel, it’s been quite long. This part will be the last part about this topic, I share about Eager Loading and how to insert and update relationships. Part 3 you see here offline.
4. Eager Loading
Referring to Eager Loading, you must remember vấn đề N + 1 query
right away. This problem occurs when you call the model’s relationship as an attribute, when the relational data is “lazy loaded”. Easier to understand, the relationship does not actually load before you call it. When calling the relationship will meet executing N + 1 query. If you use eager loading , this problem will be reduced.
For example, Model Book
is related to Author
1 2 3 4 5 6 7 8 | class Book extends Model { public function author() { return $this->belongsTo(Author::class); } } |
Then take all the books and print out the author’s name:
1 2 3 4 5 6 | $books = Book::all(); foreach ($books as $book) { echo $book->author->name; } |
The first command line will execute a query that retrieves all the books. Suppose there are N books. The loop will repeat N times, each time performing a query to retrieve the author’s name of the book. So N + 1 queries already.
If you use eager loading , you will only lose 2 queries . We will use the
with()
. The parameter passed is the name of the relationship to retrieve the same model.
1 2 3 4 5 6 | $books = Book::with('author')->get(); foreach ($books as $book) { echo $book->author->name; } |
The relationship will then load with the model. 2 queries were executed:
1 2 3 4 | select * from books select * from authors where id in (1, 2, 3, 4, 5, ...) |
Want to get multiple relationships with one statement , passing the array array to some function with()
.
1 2 | $books = Book::with(['author', 'publisher'])->get(); |
Eager loading nested
For example, to retrieve the author (author) of a book and how to contact (contacts) of that author, we use the ‘.’ Please.
1 2 | $books = Book::with('author.contacts')->get(); |
Eager loading nested in polymorphic relationships
For example, we have model ActivityFeed
:
1 2 3 4 5 6 7 8 9 10 11 | class ActivityFeed extends Model { /** * Get the parent of the activity feed record. */ public function parentable() { return $this->morphTo(); } } |
Assuming 3 models Event, Photo, Post
can create ActivityFeed
. Model Event
belongsTo Calendar
, Photo
belongsTo Tag
, Post
belongsTo Author
. To query ActivityFeed
and retrieve parentable
along with corresponding belongsTo relationships, we use morphWith()
.
1 2 3 4 5 6 7 8 9 10 11 | use IlluminateDatabaseEloquentRelationsMorphTo; $activities = ActivityFeed::query() ->with(['parentable' => function (MorphTo $morphTo) { $morphTo->morphWith([ Event::class => ['calendar'], Photo::class => ['tags'], Post::class => ['author'], ]); }])->get(); |
Eager loading with columns
Sometimes when I don’t want to get all the fields from a model, I can select specific fields. Note that you should get the id
and other foreign keys.
For example, I only took the column id and name of the author.
1 2 | $books = Book::with('author:id,name')->get(); |
Eager loading by default
If you want each model query to use Eager loading, you can add the $with
attribute to the model.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class Book extends Model { // luôn load quan hệ author protected $with = ['author']; /** * Get the author that wrote the book. */ public function author() { return $this->belongsTo(Author::class); } } |
If there is a default, there is a way to remove these defaults. If you don’t want to load more author
, use without()
.
1 2 | $books = Book::without('author')->get(); |
Add constraints for Eager Loading
A little more advanced, we will add constraints when using Eager Loading. In some cases, relationships have more conditions.
For example, only load posts
with title
containing the first
word.
1 2 3 4 | $users = User::with(['posts' => function ($query) { $query->where('title', 'like', '%first%'); }])->get(); |
Note : Do not use the limit
and take
methods in this case.
Lazy Eager Loading
Lazy Eager Loading is different from Lazy Loading above. This method is used when you want to load a relationship after you have queryed the parent model, when it is used, it will load. Of course it still optimizes the query offline. Instead of using with()
we will use load()
.
1 2 3 4 5 6 | $books = Book::all(); if ($someCondition) { $books->load('author', 'publisher'); } |
The parameters passed are the relationships to retrieve.
Similarly above, we can add query constraints for Lazy Eager Loading.
1 2 3 4 | $author->load(['books' => function ($query) { $query->orderBy('published_date', 'asc'); }]); |
loadMissing()
Because the query retrieves the relationship after the parent model has been retrieved, you can load the relationship when it has not been loaded before.
1 2 3 4 5 6 7 8 9 10 | public function format(Book $book) { $book->loadMissing('author'); return [ 'name' => $book->name, 'author' => $book->author->name, ]; } |
Lazy Eager Loading nested & morphTo
Using the example of the Eager Loading section, instead of using withMorth()
we use loadMorph()
.
1 2 3 4 5 6 7 8 | $activities = ActivityFeed::with('parentable') ->get() ->loadMorph('parentable', [ Event::class => ['calendar'], Photo::class => ['tags'], Post::class => ['author'], ]); |
summary
I would like to end Relationships in Laravel here. Hopefully after reading, you can understand and apply the relationship to your project 1 really confident and accurate.
References
https://laravel.com/docs/6.x/eloquent-relationships#constraining-eager-loads