You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel 10多层嵌套关联查询中检查is_enabled字段的最优方案

Laravel 10多层关联模型级联检查is_enabled的最优方案

针对你提出的多层关联模型检查is_enabled字段的问题,不需要完全依赖深度嵌套whereHas,可以根据场景选择以下几种更高效的实现方式:

一、嵌套whereHas + 预加载过滤(基础场景)

如果只是临时需要针对特定查询做过滤,你可以在whereHas中通过嵌套闭包逐层检查关联模型的is_enabled,同时在预加载(with)时也对每层关联做过滤,确保返回的关联数据都是启用状态的:

$app = Application::with([
    'modelable',
    'pingtree_group' => function ($query) {
        $query->where('is_enabled', true)
              ->with([
                  'pingtree_group_entries' => function ($q) {
                      $q->where('is_enabled', true)
                        ->with([
                            'pingtree' => function ($p) {
                                $p->with([
                                    'pingtree_entries' => function ($pe) {
                                        $pe->with([
                                            'buyer_tier' => function ($bt) {
                                                $bt->where('is_enabled', true);
                                            }
                                        ]);
                                    }
                                ]);
                            }
                        ]);
                  }
              ]);
    }
])
->where('id', 30)
->whereHas('pingtree_group', function ($query) {
    $query->where('is_enabled', true)
          ->whereHas('pingtree_group_entries', function ($q) {
              $q->where('is_enabled', true)
                ->whereHas('pingtree', function ($p) {
                    $p->whereHas('pingtree_entries', function ($pe) {
                        $pe->whereHas('buyer_tier', function ($bt) {
                            $bt->where('is_enabled', true);
                        });
                    });
                });
          });
})
->first();

这里的whereHas用于确保主模型(Application)存在符合所有启用条件的关联链,而with中的过滤则保证预加载的关联数据本身都是启用状态,不会带回禁用的记录。

二、全局作用域(推荐,减少重复代码)

如果多个模型都包含is_enabled字段,且大部分场景下只需要查询启用状态的记录,全局作用域是最优解——一次性配置,所有查询自动生效:

1. 创建全局作用域类

<?php

namespace App\Scopes;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;

class EnabledScope implements Scope
{
    public function apply(Builder $builder, Model $model)
    {
        // 仅对包含is_enabled字段的模型生效
        if (in_array('is_enabled', $model->getFillable()) || $model->hasColumn('is_enabled')) {
            $builder->where('is_enabled', true);
        }
    }
}

2. 在目标模型中注册作用域

在PingtreeGroup、PingtreeGroupEntry、BuyerTier等需要过滤的模型的boot方法中添加:

protected static function boot()
{
    parent::boot();

    static::addGlobalScope(new \App\Scopes\EnabledScope);
}

3. 简化查询代码

配置完成后,你的查询可以大幅简化,whereHas支持直接用点语法链式关联,全局作用域会自动帮你添加is_enabled=true的条件:

$app = Application::with([
    'modelable',
    'pingtree_group.pingtree_group_entries.pingtree.pingtree_entries.buyer_tier'
])->where('id', 30)
  ->whereHas('pingtree_group.pingtree_group_entries.pingtree.pingtree_entries.buyer_tier')
  ->first();

如果偶尔需要查询禁用的记录,只需临时取消作用域即可:

PingtreeGroup::withoutGlobalScope(\App\Scopes\EnabledScope::class)->get();

三、自定义关联方法(特定场景复用)

如果某些关联路径需要频繁检查is_enabled,可以在模型中定义带过滤逻辑的关联方法,让代码更具语义化:

比如在PingtreeGroup模型中:

public function enabledEntries()
{
    return $this->hasMany(PingtreeGroupEntry::class)->where('is_enabled', true);
}

在PingtreeGroupEntry模型中:

public function enabledPingtree()
{
    return $this->belongsTo(Pingtree::class)->where('is_enabled', true);
}

以此类推定义后续的enabledEntries、enabledBuyerTier关联,之后查询时直接使用这些自定义关联:

$app = Application::with([
    'modelable',
    'pingtree_group.enabledEntries.enabledPingtree.enabledEntries.enabledBuyerTier'
])->where('id', 30)
  ->whereHas('pingtree_group.enabledEntries.enabledPingtree.enabledEntries.enabledBuyerTier')
  ->first();

总结

  • 全局作用域适合大部分场景,减少重复代码,维护成本最低;
  • 嵌套whereHas+预加载过滤适合临时的、特殊的查询需求;
  • 自定义关联方法适合需要频繁复用特定关联过滤逻辑的场景。

内容的提问来源于stack exchange,提问作者Ryan H

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 22:00:41