Laravel中不使用Join过滤hasMany关联:子父字段匹配/不匹配
解决方案:无需Join实现HasMany关联的跨模型字段筛选
要实现子模型字段与父模型字段匹配/不匹配的筛选,同时避免Join带来的关联字段丢失问题,直接用Laravel查询构建器的whereColumn方法即可,无需手动Join:
修改Order模型的关联方法
class Order extends Model { public function items() { return $this->hasMany(OrderItem::class, 'order_id', 'id'); } public function items_removed_by_store() { return $this->hasMany(OrderItem::class, 'order_id', 'id') ->onlyTrashed() // 直接对比子模型与父模型的字段,无需Join ->whereColumn('order_item.deleted_by', '!=', 'order.customer_id'); } public function items_removed_by_customer() { return $this->hasMany(OrderItem::class, 'order_id', 'id') ->onlyTrashed() ->whereColumn('order_item.deleted_by', '=', 'order.customer_id'); } }
原理说明
whereColumn方法支持直接对比不同表的字段,Laravel会自动处理表名的关联逻辑,因为hasMany本身已经基于order_id建立了Order和OrderItem的关联关系,无需额外Join操作。- 这种方式不会破坏OrderItem的其他关联(比如
options),因为没有用select限制返回字段,也不会因为Join导致字段覆盖或丢失。 - 执行
Location::has('orders.items_removed_by_customer')->get()时,Laravel会生成嵌套的EXISTS子查询,而非Join查询,既满足筛选需求,又能保证关联数据的完整性。
替代方案(whereRaw)
如果需要更灵活的字段对比逻辑,也可以用whereRaw实现相同效果:
// 客户移除的商品 ->whereRaw('order_item.deleted_by = order.customer_id') // 门店移除的商品 ->whereRaw('order_item.deleted_by != order.customer_id')
内容的提问来源于stack exchange,提问作者MonOve
相关产品推荐
相关产品推荐

