リレーションにクエリを投げる時、「hasだっけ?whereHasだっけ?」となることがちょくちょくあるので、 Eloquent: Relationships > Querying Relations
を読んでまとめてみた。
公式ドキュメント
2018.12.5 追記
5.7 のドキュメントで追記された内容についても記載したので、↑のURLも5.7に変更しました。
単純にクエリを投げる
Eloquentの全てのタイプのリレーションはメソド経由で定義され、リレーションにもクエリビルダが使えるのでメソドチェーンでかける。
例えば User
モデルに Posts
モデルがリレーションがされている場合、
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* Get all of the posts for the user.
*/
public function posts()
{
return $this->hasMany('App\Post');
}
}
$user = App\User::find(1);
$user->posts()->where('active', 1)->get();
こんな感じで posts()
からさらに制約が追加できる。
もちろん where
メソド以外のクエリビルダメソドも使えます。
リレーションが存在しているか、のクエリ
リレーションの存在有無を元にレコードを取りたいときは has
orHas
メソドが使える。
// comments がある Post だけをとってくる
$posts = App\Post::has('comments')->get();
// 3つ以上の comments がある Post だけをとってくる
$posts = App\Post::has('comments', '>=', 3)->get();
// ネストされたリレーションも .(ドット) を使って対象にできる
$posts = App\Post::has('comments.votes')->get();
もっとカスタマイズしたい場合は whereHas
orWhereHas
を使う
$posts = App\Post::whereHas('comments', function ($query) {
$query->where('content', 'like', 'foo%');
})->get();
リレーションが存在していない、のクエリ
さっきと逆だが使い方はほぼ同じで、 doesntHave
orDoesntHave
を使えばよい。
// comments がない Post だけをとってくる
$posts = App\Post::doesntHave('comments')->get();
よりカスタマイズしたい場合は whereDoesntHave
orWhereDoesntHave
。
$posts = App\Post::whereDoesntHave('comments', function ($query) {
$query->where('content', 'like', 'foo%');
})->get();
// ネストされたリレーション
$posts = App\Post::whereDoesntHave('comments.author', function ($query) {
$query->where('banned', 1);
})->get();
リレーションモデルのレコード数をとる
なんと、「リレーションデータの内容は読み込まず、レコード数だけをとる」という方法がある。(知らなかった)
withCount
メソドを使って、カラムを {relation}_count
とするだけ。超簡単。
// comments のレコード数をとる
$posts = App\Post::withCount('comments')->get();
foreach ($posts as $post) {
echo $post->comments_count;
}
// comments、votes のレコード数をとる
$posts = App\Post::withCount(['votes', 'comments' => function ($query) {
$query->where('content', 'like', 'foo%');
}])->get();
echo $posts[0]->votes_count;
echo $posts[0]->comments_count;
また、レコード数にエイリアスをつけることも可能。
$posts = App\Post::withCount([
'comments',
'comments as pending_comments_count' => function ($query) {
$query->where('approved', false);
}
])->get();
echo $posts[0]->comments_count;
echo $posts[0]->pending_comments_count;
withCount
と select
を一緒に使うときは順序に注意
5.7 ドキュメントに追記されている内容。
(新機能というより、注意書きとして追記されたと思われる)
select
のあとに withCount
を呼び出してね と。
$query = App\Post::select(['title', 'body'])->withCount('comments');
echo $posts[0]->title;
echo $posts[0]->body;
echo $posts[0]->comments_count;
まとめ
- リレーションはメソドで定義する
- リレーションメソドにはクエリビルダが使え、チェーンメソドが書ける
- 「リレーションが存在しているか」の判定には
has
orHas
- もっとカスタマイズしたいときは
whereHas
orWhereHas
- もっとカスタマイズしたいときは
- 「リレーションが存在していないか」の判定には
doesntHave
orDoesntHave
- もっとカスタマイズしたいときは
whereDoesntHave
orWhereDoesntHave
- もっとカスタマイズしたいときは
- リレーションモデルのレコード数をとるには
withCount
を使って、{relation}_count
で取れる- エイリアスをつけることも可能
-
select
と一緒に使うときは順序に注意