PHP

[with・whereHas] Laravel、リレーション先を絞り込むか? リレーション先で絞り込むか?

Laravel、リレーション先を絞り込むか? リレーション先で絞り込むか?サムネ画像

こんにちは!

見出しは、なんか某アニメ映画のタイトルみたいなになっていますが、、、

要は、Laravelでリレーションを組んでいた場合、主テーブルに紐付いている従テーブルを絞り込むか従テーブルを元に主テーブルを絞り込むか、の解説をしていきます。

主テーブル・・・・・主キー(id)を持つテーブル
従テーブル・・・・・外部キー(***_id)を持つテーブル

今回は例として、

ゲーム情報を取得時に、特定のユーザーが登録しているゲームのお気に入り情報を取得する場合 (withメソッドを使う)
ゲーム情報を取得時に、特定のユーザーがお気に入りをしているゲームを取得 (whereHasメソッドを使う)

というケースを考えます。

環境

今回の環境

  • PHP 8.1.1
  • Laravel 9.19

テーブル構造

記事用なので簡潔に書きます。

ゲーム一覧テーブル (game)

ゲーム一覧を管理するテーブルになります。

カラム名 データ内容 FK
id ID
game_name ゲーム名

ゲームお気に入りテーブル (favorite)

ゲームをお気に入りした場合、このテーブルにレコードが追加されます。

カラム名 データ内容 FK
id ID
game_id ゲームID
user_id ユーザーID

ユーザーテーブル (users)

ユーザーを管理するテーブルになります。

カラム名 データ内容 FK
id ID
name ユーザー名

モデルファイル

ゲーム一覧モデル

ゲームモデルに、ゲームお気に入りモデルのリレーションを設定します。

ゲームは、いろんなユーザーがお気に入り登録できるので、1対多の関係になります。

public function favorite()
{
    return $this->hasMany('App\Models\FavoriteGames');
}

リレーション先を絞り込む場合

リレーション先を絞り込む(主テーブルに紐付いている従テーブルを絞り込む)場合を見ていきます。

例えば、特定のユーザーのお気に入り情報が欲しい場合、リレーション先である、お気に入りテーブルのuser_idを絞り込む必要があります。

その場合、以下のようにwithメソッドの中でクロージャーを使います。

Games::with(['favorite' => function ($query) use ($user_id) {
    $query->where('user_id', $user_id);
}])->where('id', $game_id)->first();

実際にゲームid=1のゲーム取得時に、ユーザーid=2のお気に入り情報のみを取得したい場合は、以下のようになります。

>>> Games::with(['favorite' => function ($query) {
        $query->where('user_id', 2);
    }])->where('id', 1)->first();

=> App\Models\Games {#3687
     id: 1,
     game_name: ファイナルファンタジー
     favorite: Illuminate\Database\Eloquent\Collection {#4831
       all: [
         App\Models\FavoriteGames {#4226
           id: 2,
           games_id: 1,
           user_id: 2,
         },
       ],
     },
   }

withメソッドのクロージャー内で、user_idを絞らないと、そのゲームお気に入り登録しているすべてのユーザー情報も取得してしまいます。

>>> Games::with(['favorite'])->where('id', 1)->first();

=> App\Models\Games {#3687
     id: 1,
     game_name: ファイナルファンタジー
     favorite: Illuminate\Database\Eloquent\Collection {#4831
       all: [
         App\Models\FavoriteGames {#4224  => ここユーザーの情報入らない
           id: 1,
           games_id: 1,
           user_id: 1,
         },
         App\Models\FavoriteGames {#4226
           id: 2,
           games_id: 1,
           user_id: 2,
         },
       ],
     },
   }

リレーション先で絞り込む場合

次にリレーション先で絞り込む(従テーブルを元に主テーブルを絞り込む)場合を見ていきます。

今回の場合、特定のユーザーが、お気に入りをしているゲームのみ取得したい場合、になります。

その場合、以下のようにwhereHasメソッドを使います。

Games::whereHas('favorite', function ($query) use ($user_id) {
    $query->where('user_id', $user_id);
});

実際に、ユーザーid=2がお気に入り登録しているゲームを取得してみます。

>>> Games::whereHas('favorite', function ($query) {
        $query->where('user_id', 2);
    })->get();

=> Illuminate\Database\Eloquent\Collection {#3697
     all: [
       App\Models\Games {#3708
         id: 60,
         game_name: "ポケモン",
       },
       App\Models\Games {#3680
         id: 61,
         game_name: "デジモン",
       },
       App\Models\Games {#3715
         id: 62,
         game_name: "フジモン",
       },
     ],
   }

以上になります。

-PHP
-, , , ,