概要
Laravel組み込みのORMであるEloquentが実際どんなクエリを発行しているのか、理解を深めるために調べてみました。
今回はリレーションに関わる基本的な部分を記載しています。
環境
Laravel v6.20.26
ER図
モデルへ定義したリレーション
Country.php
public function posts()
{
return $this->hasManyThrough('App\Post', 'App\User');
}
User.php
public function posts()
{
return $this->hasMany('App\Post');
}
public function favorite_posts()
{
return $this->belongsToMany('App\Post', 'favorite', 'user_id', 'post_id');
}
Post.php
public function user()
{
return $this->belongsTo('App\User');
}
public function favorite_users()
{
return $this->belongsToMany('App\User', 'favorite', 'post_id', 'user_id');
}
クエリ一覧
基本的に各テーブルはid=1のデータを利用しています。
hasMany
$user->posts;
select * from `posts` where `posts`.`user_id` = 1 and `posts`.`user_id` is not null
belongsTo
$post->user;
select * from `users` where `users`.`id` = 1 limit 1
hasManyThrough
$country->posts;
select
`posts`.*,
`users`.`country_id` as `laravel_through_key`
from
`posts`
inner join
`users`
on `users`.`id` = `posts`.`user_id`
where
`users`.`country_id` = 1
belongsToMany
$user->favorite_posts;
select
`posts`.*,
`favorite`.`user_id` as `pivot_user_id`,
`favorite`.`post_id` as `pivot_post_id`
from
`posts`
inner join
`favorite`
on `posts`.`id` = `favorite`.`post_id`
where
`favorite`.`user_id` = 1
attach/detach/sync
syncがattachとdetachの組み合わせなのでそちらだけ紹介します。
事前にpost_id=1,2がattachされています。事前にユーザーに紐づくfavoriteを全て取得し、内部でレコードの有無を判断して作成・削除をしています。
$user->favorite_posts()->sync([1,3]);
select * from `favorite` where `favorite`.`user_id` = 1;
delete from `favorite` where `favorite`.`user_id` = 1 and `favorite`.`post_id` in (2);
insert into `favorite` (`post_id`, `user_id`) values (3, 1);
with(Eager Load)
User::with(['posts'])->get();
select * from `users`;
select * from `posts` where `posts`.`user_id` in (1, 2);
条件に合致するuserを全て取得した後、対応するposを全て取得し内部で紐付けています。
データベースへの問い合わせが一度で済むので、N+1問題への対処として利用されます。
まとめ
シンプルなリレーションに関しては予想通りだと思いますが、syncなどはクエリでの処理とアプリでの処理がどのように分離されているのかがわからなかったので勉強になりました。
備忘録代わりに今後もまとめていきたいです。