Quantcast
Channel: laravel5.8タグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 87

【Laravel5.8】Eagerロード先テーブルは必ず主キーもSELECTしないといけない

$
0
0

テーブルBと、B.idにリレーションを張ってるテーブルAがあったとします。

モデルは普通。

モデル
// モデルAclassTableAextendsModel{// テーブル名protected$table='table_a';/**
     * リレーション
     * @return BelongsTo
     */publicfunctiontableb(){return$this->belongsTo(TableB::class,'b_id','id');}}// モデルBclassTableBextendsModel{// テーブル名protected$table='table_b';}

コントローラとテンプレも普通。

コントローラ
$tableas=TableA->get();
blade
@foreach($tableasas$tablea){{$tablea->name}}{{$tablea->tableb['name']}}@endforeach

さくっとできました。

できたはいいけどn+1問題が直撃する書き方です。
ループのたびにSELECT * FROM table_b WHERE id=xというSQLが走るので、とてもよろしくありません。
よってEagerローディングするようにしましょう。
コントローラにwith書くだけなので一瞬です。

コントローラ
$tableas=TableA->with('table_b')->get();

何十個も発行されていたSQLがわずか二つに減りました。

しかし、これもまだ無駄があります。
テーブルAもBもnameしか使ってないので、SELECT *ではなくSELECT nameのSQLを発行するようにしたいです。
このためにLaravelにはスコープという機能があります。

モデル
// モデルAclassTableAextendsModel{// テーブル名protected$table='table_a';/**
     * リレーション
     * @return BelongsTo
     */publicfunctiontableb(){return$this->belongsTo(TableB::class,'b_id','id');}/**
     * 取得対象カラム
     */publicstaticfunctionscopeName($query){return$query->select(['name']);}}// モデルBclassTableBextendsModel{// テーブル名protected$table='table_b';/**
     * 取得対象カラム
     */publicstaticfunctionscopeName($query){return$query->select(['name']);}}

コントローラからはスコープの呼び出しを追加します。

コントローラ
$tableas=TableA::name()->with(['tableb'=>function($q){$q->name();}])->get();

やったね。

はい、これテーブルBのデータ取って来れません。
リレーション先テーブルのデータに主キーがなかった場合、何故か紐付けてくれないのです。

実はEagerローディングは予想に反してJOINしていません。
発行されたSQLを見ると、SELECT name FROM table_a WHERE id IN (1, 2, 3); SELECT name FROM table_b WHERE id IN (4, 5, 6);みたいになっています。
別々に取ってきたデータを後でくっつけているので、くっつけるために主キーが必要になっています。

マニュアルにも特定カラムのEagerロードという項目で一応触れられているのですが、スコープとはだいぶ離れたところにあるうえにwith('author:id,name')という例示のせいで一見同じものに見えないため、関係ないと読み流してしまいました。

ということできちんと結合するには、主キーも取ってくるようにしましょう。

モデル
// モデルBpublicstaticfunctionscopeName($query){return$query->select(['id','name']);}

まとめ

SELECT先を指定するときは、必ず主キーも入れよう。


Viewing all articles
Browse latest Browse all 87

Trending Articles