Laravel如何实现双列条件匹配的belongsTo关联获取产品所有单位
需求背景
现有Units、Products两张数据表,需要获取每个产品对应的全部单位(包含基础单位、子单位)。
表结构说明
Units表
| id | name | multiplier | base_unit_id |
|---|---|---|---|
| 1 | Piece | 1 | null |
| 2 | Dozen - 12 | 12 | 1 |
Products表
| id | name | cost | price | unit_id |
|---|---|---|---|---|
| 1 | product1 | 10 | 14 | 1 |
| 2 | product2 | 10 | 14 | 1 |
注:原示例中product2的id重复写为1属于笔误,实际应为自增主键
问题描述
在Product模型中定义units关联时,追加orWhere条件匹配base_unit_id等于当前产品unit_id的写法无法正常生效,原有错误写法如下:
public function units() { return $this->belongsTo(Unit::class, 'unit_id', 'id'); // 还需要匹配base_unit_id = 产品unit_id的单位 // 尝试过的错误写法,无法运行 // $this->belongsTo(Unit::class, 'unit_id', 'id')->orWhere('base_unit_id', $this->unit_id) }
期望通过如下预加载写法查询数据,每个产品下的units字段返回包含基础单位、所有子单位的集合:
$products = Product::with('units')->get();
实现方案
之前写法不生效有两个核心问题:
- 关联类型选错了:一个产品对应多个单位(基础单位+所有子单位),是一对多关系,不能用
belongsTo(反向属于关联,只能返回单个模型) - 直接在关联里调用
$this->unit_id不兼容预加载逻辑:预加载是批量执行查询,此时还未完成模型实例化,拿不到单个产品的unit_id值,必须用字段匹配语法实现跨表条件判断。
正确的模型关联定义写法如下,直接放在Product模型中即可:
public function units() { return $this->hasMany(Unit::class) ->where(function ($query) { $query->whereColumn('units.base_unit_id', '=', $this->qualifyColumn('unit_id')) ->orWhereColumn('units.id', '=', $this->qualifyColumn('unit_id')); }); }
逻辑说明
- 关联条件自动匹配两类单位:
units.id = products.unit_id:产品直接绑定的基础单位units.base_unit_id = products.unit_id:所有以当前产品基础单位为父单位的子单位
- 用闭包包裹查询条件,避免
orWhere破坏预加载自带的软删除、全局作用域等查询条件 - 用
whereColumn/orWhereColumn做表字段间的匹配,完全兼容预加载的批量查询逻辑,不需要依赖单个模型实例的属性,直接支持Product::with('units')->get()的写法 - 最终返回的每个产品的
units属性就是符合要求的单位集合,结构和预期一致。
内容的提问来源于stack exchange,提问作者Jihad Hashm
相关产品推荐
相关产品推荐

