概要
LaravelのEloquentにて利用できるwithとloadメソッドですが、
特定のカラムだけを取得したい時に、その手法や気をつけて欲しいことなどを下記2つTIPSとして紹介します。
- 連続したリレーション先でも特定カラムを取得したい場合
- クロージャを使いつつ特定カラムを取得したい場合
※例として、すべてwith
を使ってコードを載せていますが、同様の内容をload
でも利用可能です。
※「Laravel9以降」「php8以降」を前提としてコードを記載しています。
前提知識
通常、with
やload
で必要な特定カラムを取得したい場合、
下記のように、リレーション名(posts
など)のあとに:
をつけて必要なカラム名(id,user_id,title
など)を記載すればOKです。
User::with('posts:id,user_id,title')->get();
1. 連続したリレーション先でも特定カラムを取得したい場合
基本的にドット記法で連続したリレーションを記載しつつ、:
で必要なカラムを記載すればOKです。
// postsに紐づいたcommentsも一緒に取得&カラムを絞りたい場合
User::with([
'posts:id,user_id,title',
'posts.comments:id,post_id,content',
)->get();
特定の時だけ連続したリレーション(posts.comments
など)を取得したい、かつ、when
を使って、それを表現したい場合は、when
を書く場所に要注意です。(下記)
NGパターン
when
をwith
の後に記載
User::with('posts:id,user_id,title')
->when($condition, fn ($q) => $q->with('posts.comments:id,post_id,content'))
->get();
こう書くと、with('posts:id,user_id,title')
の方のカラム制限が外れてしまい、
with('posts')
と書いたのと同じように動いてしまいます。
(comments
の方のカラム制限=with('posts.comments:id,post_id,content')
は機能します。)
OKパターン
when
をwith
の前に記載
User::when($condition, fn ($q) => $q->with('posts.comments:id,post_id,content'))
->with('posts:id,user_id,title')
->get();
こう書くと、posts
とposts.cooments
、どちらも特定のカラムだけ取得できます。
意外と気付きにくいので、お気をつけください。
2. クロージャを使いつつ特定カラムを取得したい場合
with
内でクロージャを使いたい場合は、:
を使った書き方ができません。
// NG(エラー発生)
User::with(['posts:id,user_id,title' => fn ($q) => $q->latest()])->get();
そのため、クロージャ内でselect
を使って絞りましょう。
// OK
User::with(['posts' => fn ($q) => $q->select(['id', 'user_id', 'title'])->latest()])->get();
select
内でカラムを絞る場合、リレーション元と先で、同じ名前のカラム名が存在する場合は、テーブル名を明示する必要があります。(下記)
// usersテーブルにも「id」、postsテーブルにも「id」カラムが存在する場合、'posts.id'と明示が必要
User::with(['posts' => fn ($q) => $q->select(['posts.id', 'user_id', 'title'])->latest()])->get();
終わりに
必要なカラムだけ取得する癖をつけると、プロダクトが拡大してDBテーブルにカラムを追加した場合などでもPFMの低下を抑えることができます。
普段から必要なカラムだけ取得するよう心がけていきたいですね。