如何在Laravel模型关联查询中禁用$with与$appends属性
Laravel 模型全局预加载、自定义追加属性的临时禁用方案
之前尝试的写法为什么不生效
你调用setEagerLoads([])没有达到预期效果,核心是两套预加载逻辑完全独立,根本没法互相覆盖:
- 模型类上静态定义的
protected $with是模型级的强制全局规则,只要实例化这个模型,就会自动按配置加载对应关联,这套逻辑是绑定在模型生命周期上的,和关联查询层的配置无关。 setEagerLoads()的作用仅仅是设置当前关联查询主动要加载的关联列表,它既不会修改、也无法清除模型类上绑定的全局$with配置,自然达不到清空自动加载关联的效果。
具体实现方法
1. 临时关闭模型全局$with自动预加载
Laravel 8及以上版本原生提供了withoutEagerLoads()方法,可以在查询构建阶段直接清空本次查询关联的所有全局预加载配置,哪怕模型类上定义了$with也不会触发自动加载。
对应的关联方法可以直接调整为:
public function payment() { return $this->belongsTo('App\Payment', 'payment_id', 'id') ->withoutEagerLoads() ->withTrashed(); }
如果不想修改关联方法本身的默认行为,只想在特定业务场景下获取不带全局预加载的实例,也可以在调用位置链式调用方法:
// 默认获取(带全局$with配置的关联) $defaultPayment = $model->payment; // 轻量获取(不带任何全局预加载) $lightweightPayment = $model->payment()->withoutEagerLoads()->first();
如果使用的Laravel版本低于8.0,没有原生withoutEagerLoads()方法,可以通过临时覆盖模型的$with属性实现等价效果,避免不必要的关联查询开销。
2. 临时禁用模型全局$appends自定义追加属性
$appends定义的自定义属性是绑定在模型实例层面的序列化逻辑,无法在查询构建阶段提前处理,必须等模型从数据库查询出来、完成实例化之后再动态调整,常用的实现方式有两种:
- 直接调用模型自带的
setAppends([])方法,清空当前实例上所有的全局追加属性:// 先通过withoutEagerLoads拿轻量实例,减少不必要的关联查询开销 $payment = $model->payment()->withoutEagerLoads()->first(); // 清空当前实例的所有自定义追加属性 $payment->setAppends([]); - 如果需要频繁使用这种轻量加载模式,可以在Payment模型中加个简单的开关标记,避免每次手动调用
setAppends():- 在Payment模型中新增公开属性作为控制开关:
// 控制是否启用自定义追加属性 public $enableAppends = true; - 重写模型自带的
getAppends()方法,根据开关返回对应配置:public function getAppends() { // 开关关闭时,直接返回空数组,不加载任何自定义追加属性 if (! $this->enableAppends) { return []; } return $this->appends; }
$payment = $model->payment()->withoutEagerLoads()->first(); $payment->enableAppends = false; - 在Payment模型中新增公开属性作为控制开关:
内容的提问来源于stack exchange,提问作者Stephen H. Anderson
相关产品推荐
相关产品推荐

