Laravel中如何基于3个关联模型实现三层嵌套子查询?
两种现有写法的问题校验
- 第一种写法存在两个核心错误:
- 筛选条件写反:需求是筛选
delivered = true的关联记录,代码里写的是where('delivered', false),返回结果完全不符合预期 - 闭包内使用
pluck():这个写法会先执行内层查询,把所有匹配的ID拉到PHP内存中再组装成数组传给外层查询,数据量大时会出现内存溢出问题,不属于数据库层面的嵌套子查询,性能很差
- 筛选条件写反:需求是筛选
- 第二种写法逻辑方向正确:
delivered = true的筛选条件写对了,闭包内通过select()指定查询字段、直接构造查询逻辑,会生成数据库层面的嵌套子查询,不会提前加载数据到内存,性能正常- 存在可维护性隐患:代码里硬编码了
carts、accepted_carts表名,如果后续修改了模型绑定的表名、或者加了表前缀配置,这里会出现表名不匹配的错误,维护成本高
最佳实现方式
优先使用Laravel提供的关联查询whereHas实现,可读性、可维护性最好,性能和原生嵌套子查询一致:
- 先在对应模型中定义关联关系
// 在Transaction模型中定义和Cart的关联 public function carts() { return $this->hasMany(Cart::class, 'transaction_id'); } // 在Cart模型中定义和Acceptedcart的关联 public function acceptedCarts() { return $this->hasMany(Acceptedcart::class, 'cart_id'); }
- 直接通过嵌套关联筛选数据
$transactions = Transaction::whereHas('carts.acceptedCarts', function ($query) { $query->where('delivered', true); })->get();
如果不想定义模型关联,也要用动态获取表名的方式写嵌套子查询,避免硬编码表名的问题:
$transactions = Transaction::whereIn('id', function ($query) { $query->select('transaction_id') ->from((new Cart())->getTable()) ->whereIn('id', function ($subquery) { $subquery->select('cart_id') ->from((new Acceptedcart())->getTable()) ->where('delivered', true) ->distinct(); })->distinct(); })->get();
注意:如果存在一个Cart对应多条Acceptedcart记录的场景,要在子查询中加
distinct()去重,避免重复查询出同一条Transaction记录。
内容的提问来源于stack exchange,提问作者aref razavi
相关产品推荐
相关产品推荐

