LoginSignup
18
14

More than 5 years have passed since last update.

【Laravel】 ネストした既定の Eager Loading を一時的に無効化する

Last updated at Posted at 2018-09-27

最近 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
18
14
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
14