ネストされたリレーションのN+1問題を回避したいので調査した。
Laravel:5.6.26
例としてERとLaravelのコード
UserController.php
public function index()
{
$users = User::limit(5)->get();
return view('user.index')->with('users', $users);
}
index.blade.php
@foreach ($users as $user)
<p>{{ $user->name }}</p>
<ul>
@foreach ($user->posts as $post)
<li>{{ $post->title }}</li>
<li>コメント数:{{ count($post->comments) }}</li>
@endforeach
</ul>
@endforeach
結果、下記の様なselect文が発行される
select * from users limit 5
select * from posts where posts.user_id = 1
select * from comments where comments.post_id = 10
select * from posts where posts.user_id = 2
select * from comments where comments.post_id = 20
select * from posts where posts.user_id = 3
select * from comments where comments.post_id = 30
select * from posts where posts.user_id = 4
select * from comments where comments.post_id = 40
select * from posts where posts.user_id = 5
select * from comments where comments.post_id = 50
N+1問題を回避するためにwith
を使う
UserController.php
public function index()
{
$users = User::with('posts')->limit(5)->get();
return view('user.index')->with('users', $users);
}
select * from users limit 5
select * from posts where posts.user_id in (1, 2, 3, 4, 5)
select * from comments where comments.post_id = 10
select * from comments where comments.post_id = 20
select * from comments where comments.post_id = 30
select * from comments where comments.post_id = 40
select * from comments where comments.post_id = 50
postsは回避されたがcommentsがまだ
withでドット指定
UserController.php
public function index()
{
$users = User::with('posts.comments')->limit(5)->get();
return view('user.index')->with('users', $users);
}
select * from users limit 5
select * from posts where posts.user_id in (1, 2, 3, 4, 5)
select * from comments where comments.post_id in (10, 20, 30, 40, 50)
ネストされたリレーション先のcommentsまで対応できる
RailsのbulletみたいなやつがLaravelにあるといいんだけど無いっぽい。
※プロダクトのコードで試した為に上記コードは記事作成時に手で起こしてあります。コピペしても動かないかも
追記
N+1が発生しているか、発見できるLaravel N+1 Query Detectorってライブラリがあったようです
LaravelでN+1を検出させる