Laravel多态关联下查询同州同分类Buro的实现问题
如何筛选与当前订单同州且同分类的Buro?
我来帮你搞定这个多态关联的查询问题!你遇到的核心难点就是如何正确关联多态的Address表,同时跨多层关联筛选State,下面给你两种靠谱的解决方案:
方案一:用Eloquent关联查询(推荐,更符合Laravel风格)
首先,我们可以利用Eloquent的whereHas方法,层层嵌套检查关联模型的条件——这样既能保证代码简洁,又能利用Laravel的关联特性避免手写复杂的join语句。
1. 基础写法(适配你当前的模型关联)
假设你暂时不想修改现有模型的关联定义,直接用addressable这个morphMany关联:
// 先获取目标订单的分类ID和州标题 $targetCategoryId = $order->category_id; $targetStateTitle = $order->addressable[0]->city->state->title; // 查询符合条件的Buro $matchingBuros = Buro::where('category_id', $targetCategoryId) ->whereHas('addressable', function ($addressQuery) use ($targetStateTitle) { // 检查Buro的Address是否关联了符合条件的City $addressQuery->whereHas('city', function ($cityQuery) use ($targetStateTitle) { // 检查City是否关联了目标州 $cityQuery->whereHas('state', function ($stateQuery) use ($targetStateTitle) { $stateQuery->where('title', $targetStateTitle); }); }); }) ->pluck('title', 'id') ->all();
2. 优化写法(修正模型关联,逻辑更清晰)
你提到每个Buro和Order仅对应一个Address,那其实把morphMany改成morphOne更贴合实际场景,这样关联名称也可以更直观:
// 在Buro模型里修正关联 public function address() { return $this->morphOne(App\Address::class, 'addressable'); } // 在Order模型里同理修正 public function address() { return $this->morphOne(App\Address::class, 'addressable'); }
修正后,查询代码可以简化成链式关联的写法,可读性更强:
$targetCategoryId = $order->category_id; $targetStateTitle = $order->address->city->state->title; $matchingBuros = Buro::where('category_id', $targetCategoryId) ->whereHas('address.city.state', function ($stateQuery) use ($targetStateTitle) { $stateQuery->where('title', $targetStateTitle); }) ->pluck('title', 'id') ->all();
方案二:用Query Builder手写关联(适合需要精细控制SQL的场景)
如果你更倾向于用Query Builder,核心是正确处理多态关联的两个条件:addressable_id对应Buro的ID,addressable_type对应Buro模型的完整类名(比如App\Buro)。这里修正你之前的错误写法:
$targetCategoryId = $order->category_id; $targetStateTitle = $order->addressable[0]->city->state->title; $matchingBuros = DB::table('buros') // 关联分类表,确保分类匹配 ->join('categories', 'categories.id', '=', 'buros.category_id') ->where('categories.id', $targetCategoryId) // 关键:用闭包同时指定多态关联的两个条件 ->join('addresses', function ($join) { $join->on('addresses.addressable_id', '=', 'buros.id') ->where('addresses.addressable_type', '=', 'App\Buro'); // 注意类名要和模型里的一致 }) // 关联城市表(这里注意字段名,你之前写的cities.states应该是笔误,改成state_id) ->join('cities', 'addresses.city_id', '=', 'cities.id') // 关联州表 ->join('states', 'cities.state_id', '=', 'states.id') // 筛选目标州 ->where('states.title', $targetStateTitle) // 选择需要的字段 ->select('buros.id', 'buros.title') ->pluck('title', 'id') ->all();
关键注意点
- 多态关联必须同时匹配
addressable_id和addressable_type,缺一不可,否则会把Order的Address也关联进来; - 如果你的模型类名有命名空间,
addressable_type要写完整的类名(比如App\Buro),和数据库里存储的一致; - 推荐优先用Eloquent的
whereHas写法,它会自动处理关联的约束,比手写join更不容易出错。
内容的提问来源于stack exchange,提问作者Rafael Munoz
相关产品推荐
相关产品推荐

