😺
Laravelでリレーションを元に中間テーブルから直接データを取得するスマートなやり方
LaravelにてbelongsToManyのように中間テーブルがあるリレーションにて、極力データを取得せずに中間テーブルからデータを取得したいことがありました。
ちょっと調べてみたのですが、withPivotでデータを含める方法ばかりが検索にヒットするので調査をしました。
確認環境
- Laravel 7
結論
newPivotQuery()
を使うことで対応出来ます。
$post->favoriteUsers()->newPivotQuery()->pluck('user_id');
// select "user_id" from "favorite_post_users" where "post_id" = ?
思った手段
何も考えずに書く
今回、ユーザーIDの一覧が取得できればよかったので、単純に考えると以下になります。
$post->favoriteUsers()->pluck('id');
// select "id" from "users" inner join "favorite_post_users" on "users"."id" = "favorite_post_users"."user_id" where "favorite_post_users"."post_id" = ?
ただこの場合はusersテーブルから中間テーブルをinner joinして取得するクエリになります。
usersテーブルにしか存在しない情報を取得したい場合はこれを使うと良いでしょう。
DBファサードを利用する
ちょっと書くのが面倒なDBファサードの場合。
これでも取得はできるがちょっと長くなる。
\DB::table($post->favoriteUsers()->getTable())
->where($post->favoriteUsers()->getForeignPivotKeyName(), $post->id)
->pluck('user_id');
// select "user_id" from "favorite_post_users" where "post_id" = ?
newPivotを使ってみる
これはタイプヒントで出てきたのでやってみたのですが、たしかにモデルはつくられるもののPostモデルのidがwhereされませんでした。
これだとDBファサードでやるのと変わらないですね。。。
$post->favoriteUsers()->newPivot()->pluck('user_id');
// select "user_id" from "favorite_post_users"
newPivotQueryの場合
さて、実際に使えたnewPivotQueryは以下のようなソースになっておりました。
Laravel 7のコードから引っ張ってきたので今はもう少し違うかもしれない
public function newPivotQuery()
{
$query = $this->newPivotStatement();
foreach ($this->pivotWheres as $arguments) {
$query->where(...$arguments);
}
foreach ($this->pivotWhereIns as $arguments) {
$query->whereIn(...$arguments);
}
foreach ($this->pivotWhereNulls as $arguments) {
$query->whereNull(...$arguments);
}
return $query->where($this->foreignPivotKey, $this->parent->{$this->parentKey});
}
一番重要なのが最後に書いてあるwhereで、$this->parent->{$this->parentKey}
が書かれているのでリレーションを維持した状態で取得できるわけですね。
Discussion
普通に
where()
してもいい気がしますが、どうなのでしょうか?あくまでも極力whereを書かずに自動でやらせようと思った結果ですね