Laravel链式调用中whereBetween嵌套后失效问题排查
问题场景与疑问
我有一个user_orders表,User模型与Orders模型是一对多关联(User -> hasMany() -> Orders),表数据如下:
| id | user_id | date |
|---|---|---|
| 123 | 12345 | 2024-02-01 |
| 124 | 12345 | 2024-02-02 |
| 125 | 12345 | 2024-02-03 |
| 126 | 45678 | 2024-02-01 |
| 127 | 45678 | 2024-02-03 |
需求是:找出$startDate到$endDate区间内每天都有订单的用户。比如当$startDate = '2024-02-01'且$endDate = '2024-02-03'时,仅应返回user_id = 12345,user_id = 45678因缺少2024-02-02的订单不应被返回。
现在遇到的问题是:为什么把whereBetween放入链式调用的闭包内后,代码就不再生效?
失效代码(返回了user 12345和45678)
User::whereHas('Orders', function ($query) use ($startDate, $endDate) { $query->where('some conditions') ->orWhere(function($query) use ($startDate, $endDate) { $query->whereBetween('date', [$startDate, $endDate]) ->groupBy('user_id') ->havingRaw('COUNT(DISTINCT date) = ? ', [diffInDays($startDate, $endDate)]); }); });
有效代码(仅正确返回user 12345)
User::whereHas('Orders', function ($query) use ($startDate, $endDate) { $query->where('some conditions') ->orWhereBetween('date', [$startDate, $endDate]) ->groupBy('user_id') ->havingRaw('COUNT(DISTINCT date) = ? ', [diffInDays($startDate, $endDate)]); });
问题原因解析
1. 失效代码的逻辑错误
你把groupBy和havingRaw放到了orWhere的子闭包里,这会导致分组和条件判断只作用于这个子查询分支,而非整个关联查询的结果:
where('some conditions')是独立分支,只要用户存在满足该条件的订单,不管日期区间要求是否达标,都会被筛选出来;- 即使用户没有满足
some conditions的订单,orWhere子闭包里的查询只是单独统计该用户在日期区间内的订单天数,分组在子查询内完成,没有和where('some conditions')的订单合并计算,最终可能误判(比如用户45678在区间内有2天订单,若diffInDays计算值为2,就会被错误选中)。
2. 有效代码的逻辑合理性
有效代码中,groupBy和havingRaw作用于整个关联查询的结果集:
- 先筛选出所有满足
some conditions或者 在日期区间内的订单; - 再按
user_id分组,统计该用户所有符合条件的订单中不同日期的数量; - 最后通过
havingRaw判断该数量是否等于日期区间的总天数,从而准确筛选出区间内每天都有订单的用户。
另外补充细节:你当前的diffInDays计算可能有误,比如2024-02-01到2024-02-03实际是3天,应该用$endDate->diffInDays($startDate) + 1获取区间总天数,否则COUNT(DISTINCT date)的判断会出错。
内容的提问来源于stack exchange,提问作者eggsloveoats
相关产品推荐
相关产品推荐

