Laravel withSum关联查询如何设置关联whereBetween并按日期排序?
关联查询加日期区间过滤的正确实现
首先要避开两个绝大多数人都会踩的坑:
- 单独给
with预加载加日期过滤,但withSum聚合不做同步过滤的话,会出现预加载的关联数据是指定时间范围的,但sum统计值是全时间的,两个口径完全对不上 - 直接在主查询链上对关联表的
date字段加where条件的话,会自动触发内连接逻辑,把指定时间范围内没有任何预订/支出记录的度假村直接过滤掉,不符合常规业务查询预期——我们通常是要保留所有营业中度假村,哪怕它在区间内没有业务记录,只是关联数据和统计值为0即可。
正确实现代码
先定义好需要筛选的日期区间,把相同的过滤逻辑同步应用到预加载和聚合计算上,保证统计口径统一:
// 可根据业务实际从请求、配置中获取起止时间 $startDate = '2024-01-01'; $endDate = '2024-03-31'; // 抽成公共闭包避免重复写相同逻辑 $dateRangeFilter = function ($query) use ($startDate, $endDate) { $query->whereBetween('date', [$startDate, $endDate]); }; $resorts = Resort::where('status', 1) // 预加载带日期过滤的预订关联 ->with(['bookings' => $dateRangeFilter]) // 聚合预订金额时应用完全相同的日期过滤 ->withSum(['bookings' => $dateRangeFilter], 'amount') // 预加载带日期过滤的支出关联 ->with(['expenses' => $dateRangeFilter]) // 聚合支出金额时应用完全相同的日期过滤 ->withSum(['expenses' => $dateRangeFilter], 'amount') // 如果需要按聚合结果排序,直接用withSum生成的默认字段名即可 ->orderByDesc('bookings_sum_amount') ->get();
补充说明
- 聚合生成的字段默认命名规则是
{关联名}_sum_{被统计字段名},也就是上面代码里的bookings_sum_amount、expenses_sum_amount,不需要额外加别名就能直接用于排序 - 这种写法不会过滤掉没有区间内业务的度假村,这类度假村的关联集合会是空数组,sum值会是0,符合常规列表统计的需求
- 如果需要只保留区间内有业务记录的度假村,可以额外加
whereHas做关联存在性判断,不会影响预加载和聚合的过滤逻辑
内容的提问来源于stack exchange,提问作者Akshay Ashokan
相关产品推荐
相关产品推荐

