Laravel whereHas结合havingRaw做最低房价筛选不生效问题
问题根源
原有scope代码无法生效有三个核心原因:
- 在
whereHas的关联查询中使用聚合函数MIN()时,没有按关联外键villa_id分组,MySQL会将所有匹配的预订方案行合并为单个聚合结果,无法按单个别墅维度计算最低房价 - 直接将参数拼接进
havingRaw存在SQL注入风险,且未做参数绑定,部分环境下会出现类型匹配错误 - 若MySQL开启
ONLY_FULL_GROUP_BY模式(5.7+默认开启),未分组的聚合查询会直接抛出SQL语法错误
可直接运行的修复方案
1. 修正Villa模型中的scope方法
/** * 筛选最低房价高于阈值的别墅 */ public function scopeFilterMinRate($query, int $rate) { return $query->whereHas('bookingPlans', function ($q) use ($rate) { $q->groupBy('villa_id') ->havingRaw('MIN(rate) > ?', [$rate]); }); } /** * 筛选最低房价低于阈值的别墅 */ public function scopeFilterMaxRate($query, int $rate) { return $query->whereHas('bookingPlans', function ($q) use ($rate) { $q->groupBy('villa_id') ->havingRaw('MIN(rate) < ?', [$rate]); }); }
2. 修正控制器中的查询调用
原有控制器代码未对查询构建器实例重新赋值,部分场景下会出现筛选条件丢失的问题,调整为如下写法:
$villas = Villa::query()->active(); if ($request->leastRate >= 1) { $villas = $villas->filterMinRate($request->leastRate); } if ($request->maxRate >= 1) { $villas = $villas->filterMaxRate($request->maxRate); } // 后续获取结果逻辑 比如 $villas->get()
大数据量场景优化方案
如果booking_plans表数据量超过10万条,推荐使用子查询预聚合的方式替代whereHas联表分组查询,性能提升明显,scope写法如下:
public function scopeFilterMinRate($query, int $rate) { return $query->whereRaw( '(SELECT MIN(rate) FROM booking_plans WHERE booking_plans.villa_id = villas.id) > ?', [$rate] ); } public function scopeFilterMaxRate($query, int $rate) { return $query->whereRaw( '(SELECT MIN(rate) FROM booking_plans WHERE booking_plans.villa_id = villas.id) < ?', [$rate] ); }
注意:该写法需要确保
booking_plans表的villa_id字段已加索引,否则子查询性能会下降。
内容的提问来源于stack exchange,提问作者MilAD86
相关产品推荐
相关产品推荐

