概要
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の低下を抑えることができます。
普段から必要なカラムだけ取得するよう心がけていきたいですね。