最近 Laravel の Eager Loading ネタ無限に書いてる気がする…w
問題
Community
Member
Plan
というエンティティがある。
(有料コミュニティが存在し,それにメンバーが異なる料金プランで加入しているイメージ)
class Community extends Model
{
protected $with = ['members'];
public function members(): HasMany
{
return $this->hasMany(Member::class);
}
}
class Member extends Model
{
protected $with = ['plan'];
public function plan(): BelongsTo
{
return $this->belongsTo(Plan::class);
}
}
class Plan extends Model {}
ここで,
$communities = Community::all();
として取得すると, $communities[0]->members[0]->plan
の階層まで Eager Loading が行われる。
このとき ->plan
の階層の Eager Loading は行いたくない場合,どうすればいいだろうか?所詮1クエリしかないので多くの場合は支障は無いが,すでに別のクエリで取得済みなので余分なことは行いたくないというケースを想定する。
解決策
間違い
without
はドットチェーンで記述するネストしたリレーションには対応できない。
$communities = Community::without('members.plan')->get();
解法 (1)
with
で既存リレーションを without
付与したもので上書きしてしまう方法。
$communities = Community::with(['members' => function (HasMany $relation) {
$relation->without('plan');
}]);
解法 (2)
基本は解法 (1) と同じだが, スコープとして定義してしまう方法。記述量が少なくシンプルになるため,非常におすすめ。
class Community extends Model
{
protected $with = ['members'];
public function scopeWithoutMemberPlans(Builder $query): Builder
{
return isset($query->getEagerLoads()['members'])
? $query->with(['members' => function (HasMany $relation) {
$relation->without('plan');
}])
: $query;
}
}
$communities = Community::withoutMemberPlans()->all();
解法 (3)
Eloquent Collection クラスに実装する場合はこう書く。いったん members
のロードをやめてコレクションを取得したあと,追加でメソッドを呼ぶイメージ。
class Community extends Model
{
protected $with = ['members'];
public function newCollection(array $models = []): CommunityCollection
{
return new CommunityCollection($models);
}
public function members(): HasMany
{
return $this->hasMany(Member::class);
}
}
class CommunityCollection extends Collection
{
public function loadMembersWithoutPlan(): self
{
return $this->load(['members' => function (HasMany $relation) {
$relation->without('plan');
}]);
}
}
$communities = Community::without('members')->loadMembersWithoutPlan();
まとめ
- ドットチェイン形式ではネストした Eager Loading 制約を除外できない
- 連想配列形式での Eager Loading 制約の付与は以下のメソッドでできる
- Eloquent Collection →
load
- Eloquent Builder および Relation →
with
- Eloquent Collection →