どういうときに使う?
主に「リレーション先のテーブルの条件で検索したいとき」に使います。
例えば、ユーザー情報が入った"usersテーブル"と記事の情報が入った"articlesテーブル"があるとして、「2022年6月5日以降に記事を投稿したユーザーだけを取得したい」という場合、「created_atが2022年6月5日以降の記事」を投稿した 「ユーザー」 を取得する必要が有ります。
つまり、「usersテーブル」にとってリレーション先のテーブルである「articlesテーブル」の条件(ここではcreated_atの日付)を使用してユーザーを検索する、ということになります。
こういったケースで役に立つのがWhereHasです。
基本の使い方
上に書いたケースでは下記のようになります。
(すでにUserモデルにリレーションメソッドが定義されている前提)
$users = User::whereHas('articles', function ($q) {
$q->where('created_at', '>=', '2022-06-05');
})->get();
※whereHasの第一引数にはリレーションメソッド名が入ります。
変数を使いたい場合
これはwhereHasに限ったことでは有りませんが、useメソッドを使用するとクロージャの中で変数が使えます。
$date = '2022-06-05';
$users = User::whereHas('articles', function ($q) use ($date) {
$q->where('created_at', '>=', $date);
})->get();
中間テーブルの条件で検索したい
上に書いたusersテーブルとarticlesテーブルの例は1対多でしたが
多対多の場合でも同じ書き方で問題ありません。
しかし、「多対多で、なおかつ中間テーブルの条件で検索したい場合」は注意が必要です。
具体的な例としては、usersテーブルとarticlesテーブル、そして、どのユーザーがどの記事にいいねを押したかを管理するための中間テーブル「likesテーブル」があるとして、「2022年6月5日以降にいいねが押された記事のみを取得したい」といったケースです。
この場合、以下のようにクロージャの中のcreated_atにテーブル名をつけてlikes.created_adとする必要があります。
(すでにArticleモデルにリレーションメソッドが定義されている前提)
$articles = Article::whereHas('likes', function ($q) {
$q->where('likes.created_at', '>=', '2022-06-05');
})->get();
WhereHasのデメリット
WhereHasは動作が重いようです。
小規模サイト等では特に問題にならないかもしれませんが
大量の処理が必要になるようなケースでは避けた方がいいかもしれません。
参考になる記事様↓
代替
下記の記事様を見て知ったのですが
一対多の場合であればWhereInで割と簡単に置き換えられますね。
ライブラリを作成してくださった方もいらっしゃるようです。