18
25

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Laravel】WhereHasの使い方・デメリット

Last updated at Posted at 2022-06-05

どういうときに使う?

主に「リレーション先のテーブルの条件で検索したいとき」に使います。

例えば、ユーザー情報が入った"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で割と簡単に置き換えられますね。

ライブラリを作成してくださった方もいらっしゃるようです。

18
25
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
25

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?