Eager loading hasMany relationship in Laravel
Eager loading relationships in Laravel makes it
really easy to avoid N+1 query problems. This occurs when you run an additional query for each result in a previous
query. Take for example a model Post
that has a hasOne
relationship with User
.
$posts = Post::all(); foreach ($posts as $post) { // Retrieve the post author echo $post->user->name; }
This would run an additional query for each post author. To avoid this we use eager loading. This essentially lazy loads the relationship into one extra query.
$posts = Post::with('user')->get();
Now imagine the reverse of that example. You want to query all users and get their latest post. Easy we use eager
loading to lazy load the posts
relationship.
$users = User::with('posts')->get();
In this case eager loading causes to load all posts for every user. This is bad because we only need the latest post.
Because the user model has a hasMany
relationship with posts, all posts are eager loaded for every user. To solve this
you might think a simple ->limit(1)
would do the
trick. But that does not work.
The solution is actually pretty simple. In order to load the latest post you can simply create a new hasOne
relationship on the User
model.
public function latestPost() { return $this->hasOne(Post::class)->latest(); }
When you use this relationship with eager loading only one post will be queried per user.
$users = User::with('latestPost')->get(); foreach ($users as $user) { // Retrieve the latest post echo $user->latestPost->title; }
If you're wondering how you can easily keep track of how many queries are run I highly recommend you install barryvdh/laravel-debugbar.