Eloquentのリレーションメソッドを活用して、紐付くモデルを取得する方法について解説します。
リレーションメソッドを活用することで、少ない処理で目的のデータを取得できるようになります。
実際に実行されるSQLを載せています。どういった動作が行われるのか理解するのに役立つと思います。
なお、Eloquentのリレーションについて以下3回に渡って解説しています。他の記事も参考にしてください。
- Eloquentのリレーション活用方法【関連の定義】
- Eloquentのリレーション活用方法【関連の取得】 ← 今回
- Eloquentのリレーション活用方法【紐付けの設定と解除】
動作確認に利用したリポジトリです。
https://github.com/raku-raku/laravel_eloquent_practice
「関連モデル」を取得
動的プロパティで取得
動的プロパティで「関連モデル」を取得できます。
動的プロパティは __getマジックメソッド
で実装されています。 Illuminate\Database\Eloquent\Model
の __getマジックメソッド
経由で各リレーションクラスの getResultsメソッド
が呼ばれます。
>>> User::find(1)->phone
=> App\Models\Phone {#2939
id: 1,
user_id: 1,
tel: "072-038-7531",
created_at: "2018-11-15 06:38:14",
updated_at: "2018-11-15 06:38:14",
}
リレーションメソッドで取得
関連モデルを絞り込んで取得したい場合、メソッドとしてアクセスします。
メソッドとしてアクセスすると、条件絞り込みのメソッドを繋げることができます。
>>> User::find(1)->posts()->where('title', 'LIKE', '%この%')->toSql()
=> "select *
from `posts`
where `posts`.`user_id` = ?
and `posts`.`user_id` is not null
and `title` LIKE ?"
>>>
>>> User::find(1)->posts()->where('title', 'LIKE', '%この%')->get();
=> Illuminate\Database\Eloquent\Collection {#2963
all: [
App\Models\Post {#2926
id: 6,
user_id: 1,
title: "手の方で、このそこですからだ。",
body: "やの中はもう一つの方たいくほんとうの鼠ねずみいろの霧きりして誰だれから一生けん命めいめんの柱はしの前に女たちの流ながら答えましく立派りっぱいにあかり天の川の中でのでしたりしているだろう。こいつも窓まどかってるんです。その大きな橋はしばらく、船に乗",
created_at: "2018-11-15 06:38:14",
updated_at: "2018-11-15 06:38:14",
},
],
}
中間テーブルのカラム取得
中間テーブルのカラムは pivot
経由でアクセスできます。
>>> User::find(1)->roles()->first()
=> App\Models\Role {#2928
id: 1,
role: "管理者",
created_at: "2018-11-15 06:38:14",
updated_at: "2018-11-15 06:38:14",
pivot: Illuminate\Database\Eloquent\Relations\Pivot {#2976
user_id: 1,
role_id: 1,
column1: "King. 'It began with the Gryphon. 'Then, you.",
column2: "So she swallowed one of the trees upon her knee.",
created_at: "2018-11-15 06:38:14",
updated_at: "2018-11-15 06:38:14",
},
}
>>>
>>> User::find(1)->roles()->first()->pivot->column1
=> "King. 'It began with the Gryphon. 'Then, you."
「自モデル」を取得
関連が存在する自モデル
hasメソッド
を利用すると、「関連モデル」が存在する「自モデル」を取得できます。doesntHaveメソッド
を利用すると、「関連モデル」が存在しない「自モデル」を取得できます。
>>> User::has('posts')->toSql()
=> "select *
from `users`
where exists (select *
from `posts`
where `users`.`id` = `posts`.`user_id`)"
>>>
>>> User::doesntHave('posts')->toSql()
=> "select *
from `users`
where not exists (select *
from `posts`
where `users`.`id` = `posts`.`user_id`)"
>>>
>>> User::has('posts')->pluck('name')
=> Illuminate\Support\Collection {#2967
all: [
"山岸 くみ子",
"山本 直人",
"山口 里佳",
],
}
関連が特定条件で存在する自モデル
追加条件をつけたい場合、 whereHasメソッド
whereDoesntHaveメソッド
を利用します。
>>> Post::whereHas('comments', function ($query) {
... $query->where('content', 'like', 'foo%');
... })->toSql()
=> "select *
from `posts`
where exists (select *
from `comments`
where `posts`.`id` = `comments`.`commentable_id`
and `comments`.`commentable_type` = ?
and `content` like ?)"
「関連モデル」の総数を取得
withCountメソッド
を利用すると「関連モデル」の総数も取得されます。
>>> User::withCount('posts')->toSql()
=> "select `users`.*,
(select count(*) from `posts` where `users`.`id` = `posts`.`user_id`) as `posts_count`
from `users`"
>>>
>>> User::withCount('posts')->first()
=> App\Models\User {#2999
id: 1,
name: "山岸 くみ子",
country_id: 5,
created_at: "2018-11-15 06:38:14",
updated_at: "2018-11-15 06:38:14",
posts_count: 4,
}
上記例では、 posts_count
に総数が設定されています。
「関連モデル」をEagerローディング
N+1クエリ問題
の対策として、Eagerローディングする方法を確認します。
withでEagerロード
User::with('posts')->get();
select * from `users`
select * from `posts` where `posts`.`user_id` in ('1', '2', '3')
loadでEagerロード
$users = User::get();
if ($condition) {
$users->load('posts');
}
select * from `users`
select * from `posts` where `posts`.`user_id` in ('1', '2', '3')
条件ごとに、Eagerロードするリレーションを変更したいケースなどに有効です。
ネストしたEagerロード
Country::with('users.posts')->get();
select * from `countries`
select * from `users` where `users`.`country_id` in ('1', '2', '3', '4', '5', '6', '7', '8', '9', '10')
select * from `posts` where `posts`.`user_id` in ('1', '2', '3')
複数リレーションをEagerロード
User::with(['posts', 'videos'])->get();
select * from `users`
select * from `posts` where `posts`.`user_id` in ('1', '2', '3')
select * from `videos` where `videos`.`user_id` in ('1', '2', '3')
特定条件をつけてEagerロード
User::with(['posts' => function ($query) {
$query->where('title', 'like', '%first%');
}])->get();
select * from `users`
select * from `posts` where `posts`.`user_id` in ('1', '2', '3') and `title` like '%first%'