概念
モデルを取得するときに、常にいくつかのリレーションをロードしたい場合は、モデルの$with
プロパティで定義出来ますが、このPRでデフォルトのEagerロードをオーバーライドして、$with
プロパティ内のすべてのアイテムを取得せず、withOnly
で定義されているリレーションのみ取得することができるようになりました。
セットアップ
ジュースの管理システムを例にして、ジュース、ジュースのメーカー(作っている会社)と種類(トマト、アップルなどなど)のモデルでそれぞれの適切な情報を管理する。その上この例のサービスの仕組みではジュースのモデルのデータを取得するとき、ほぼ全ての場合においてメーカーの情報も必要となります。(たとえばメーカーの名前を表示することなど)。そのためジュースのモデルの$with
で定義することになって、デフォルトでloadすることにします。
このようなジュースのモデルは下記のようになります。
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
/**
*ジュースモデル
*
*/
class Juice extends Model
{
/**
* 常にデフォルトとしていつも取得するリレーション定義
*
* @var array
*/
protected $with = ['maker'];
/**
* このJUICEを作っている会社
*/
public function maker()
{
return $this->belongsTo(Maker::class);
}
/**
* JUICEの種類
*/
public function type()
{
return $this->belongsTo(Type::class);
}
}
デフォルトでmaker
がていぎされていますので、取得する時メーカーの情報が自動的にジュースモデルと一緒に取得されます
$juice = App\Models\Juice::first();
// メーカーのリレーションも取得される
App\Models\Juice {
id: 1,
name: 'めっちゃおいしいジュース',
maker_id: 1,
type_id: 1,
...
maker: App\Models\Maker {
id: 1,
name: 'いい会社なまえ',
....
},
}
withOnlyの使い方
type
を引数としてwithOnly
の関数にわたして、メーカー(デフォルトでロードされるリレーション)の情報が取得されなくなります。
$juice = App\Models\Juice::withOnly('type')->first();
// メーカーのリレーション取得しない
// 種類のリレーションがロードされる
App\Models\Juice {
id: 1,
name: 'めっちゃおいしいジュース',
maker_id: 1,
type_id: 1,
...
type: App\Models\Type {
id: 1,
type_name: 'オレンジ',
....
},
}
複数のリレーションを取得したいときにはwithOnly
にリレーションの名前の配列を渡すことができます(デフォルトのEagerロードで定義されているリレーションでも可能)。空の配列の場合、すべてのリレーションが取得しないようになります。
$juice = App\Models\Juice::withOnly(['type', 'maker'])->first();
// メーカーと種類のリレーションどっちも取得
App\Models\Juice {
id: 1,
name: 'めっちゃおいしいジュース',
maker_id: 1,
type_id: 1,
...
type: App\Models\Type {
id: 1,
type_name: 'オレンジ',
....
},
maker: App\Models\Maker {
id: 1,
name: 'いい会社なまえ',
....
},
}
まとめ
$with
プロパティ内のリレーションを除外でき、withOnly
の関数の引数にわたされているリレーションのみ取得することが簡単にできます。
参照
https://github.com/laravel/framework/pull/37144
https://laravel.com/docs/8.x/eloquent-relationships#eager-loading-by-default