Laravel Eloquent递归关联如何向所有嵌套层级传递查询条件
Laravel递归自关联全层级过滤实现方案
问题场景
你在项目中定义了User模型的自引用递归关联,初始模型代码如下:
class User extends Model { public function children() { return $this->hasMany(self::class, 'level1_user_id') ->select(['id', 'first_name', 'last_name', 'level1_user_id']); } public function grandchildren() { return $this->children()->with(['grandchildren']); } }
控制器查询时尝试给grandchildren关联增加first_name = rose的过滤条件,但条件仅对第一层关联生效,深层嵌套的关联节点不会被过滤,初始控制器代码如下:
public function allChildren() { $user = User::where('id', 3)->with(['grandchildren' => function ($query) { $query->where('first_name', 'rose'); } ])->first(); return $user; }
你尝试修改grandchildren方法接收外部传入的查询对象透传到嵌套层的写法不符合Eloquent关联的调用规则,无法正常生效,错误尝试代码如下:
class User extends Model { public function children() { return $this->hasMany(self::class, 'level1_user_id') ->select(['id', 'first_name', 'last_name', 'level1_user_id']); } public function grandchildren(Query $inputQuery) { return $this->children()->with(['grandchildren' => function ($query) use ($inputQuery) { $query->$inputQuery; }]); } }
错误原因
Eloquent在渴求式加载、懒加载关联时,会自动无参调用关联方法获取关系定义,带参数的关联方法会被框架直接调用触发参数缺失错误,同时默认的with闭包只会作用在当前层关联,不会自动透传到递归嵌套的下一层。
实现代码
不需要修改原有模型的关联定义,直接通过闭包递归引用的方式,就能让过滤条件作用到所有递归层级,控制器代码调整如下:
public function allChildren() { // 定义过滤闭包,通过引用传值持有自身引用,实现条件递归传递 $filter = function ($query) use (&$filter) { $query->where('first_name', 'rose') ->with(['grandchildren' => $filter]); // 同一闭包传递给下一层关联 }; $user = User::where('id', 3) ->with(['grandchildren' => $filter]) ->first(); return $user; }
逻辑说明
- 闭包加
&$filter引用传递后,每一层关联加载时,都会把同一个过滤规则绑定到下一层的grandchildren关联上,从第一层到最深层的所有关联查询都会带上first_name = rose的过滤条件 - 如果需要复用这套过滤逻辑,可以把闭包封装成模型本地作用域,或者抽成公共查询方法,避免重复编写递归逻辑
- 原有模型中无参数的
grandchildren关联方法保留即可,不需要修改,无过滤需求的场景可以直接正常调用
内容的提问来源于stack exchange,提问作者nasim jahednia
相关产品推荐
相关产品推荐

