Laravel用户订单与发货统计报表按日期范围聚合查询
今天来聊聊 Laravel 开发中一个非常实用的场景:如何按指定日期范围统计每个用户的订单数和发货数。核心思路是巧妙结合 withCount、条件闭包与 whereHas,才能获得精准且高效的分页报表数据。
在进行用户行为分析报表开发时,统计每位用户在特定时间段内的订单量(orders_count)和发货单量(shippings_count)几乎是必不可少的任务。这里的关键挑战在于:既要确保只统计符合日期条件的关联记录,又要避免日期参数为空时引发 SQL 错误,同时保证分页结果的逻辑正确性 —— 也就是说,最终应仅返回在指定时间段内至少有一条订单或一条发货记录的用户。
✅ 正确实现方式
实现思路非常清晰:所有日期过滤逻辑必须统一放在 withCount 的闭包内,并配合 whereHas 精确筛选主模型。 以下代码兼容 $from 或 $to 为空的情况,堪称标准做法:
$report = User::withCount([ 'orders' => function ($query) use ($from, $to) { if (!empty($from)) { $query->whereDate('created_at', '>=', $from); } if (!empty($to)) { $query->whereDate('created_at', '<=', $to); } }, 'shippings' => function ($query) use ($from, $to) { if (!empty($from)) { $query->whereDate('created_at', '>=', $from); } if (!empty($to)) { $query->whereDate('created_at', '<=', $to); } }])->whereHas('shippings', function ($query) use ($from, $to) { if (!empty($from)) { $query->whereDate('created_at', '>=', $from); } if (!empty($to)) { $query->whereDate('created_at', '<=', $to); }})->orWhereHas('orders', function ($query) use ($from, $to) { if (!empty($from)) { $query->whereDate('created_at', '>=', $from); } if (!empty($to)) { $query->whereDate('created_at', '<=', $to); }})->orderByRaw('orders_count + shippings_count DESC')->paginate(25);⚠️ 需要特别留意:whereHas(...)->orWhereHas(...) 构成的是 OR 关系,确保返回的是“有订单 或 有发货”的用户。而 withCount 中的闭包各自独立控制计数范围,其逻辑必须与 whereHas 保持一致,否则数据将出现错位。
? 进阶优化:封装为本地作用域(推荐)
如果多个地方都需要使用这套逻辑,将其封装为作用域方法会更加优雅。在 Order 和 Shipping 模型中添加如下代码:
// AppModelsOrder.php & AppModelsShipping.phppublic function scopeWhereDateBetween($builder, $from, $to){ if (!empty($from)) { $builder->whereDate('created_at', '>=', $from); } if (!empty($to)) { $builder->whereDate('created_at', '<=', $to); } return $builder;}调用时变得非常简洁:
$report = User::withCount([ 'orders' => fn ($q) => $q->whereDateBetween($from, $to), 'shippings' => fn ($q) => $q->whereDateBetween($from, $to),])->whereHas('shippings', fn ($q) => $q->whereDateBetween($from, $to))->orWhereHas('orders', fn ($q) => $q->whereDateBetween($from, $to))->orderByRaw('orders_count + shippings_count DESC')->paginate(25);? 关键注意事项
- ❌ 在 whereHas 闭包中调用 get() 或 groupBy() 是常见的错误 —— 这会打断查询链,直接引发异常。
- ✅ 使用 whereDate() 而非 whereBetween('created_at', [...]),这样可以忽略时间部分,避免因时区或精度问题导致的遗漏统计。
- ✅ withCount 闭包仅影响计数,不影响主查询结果集,因此必须通过 whereHas/orWhereHas 显式筛选出有效用户。
- ✅ 若希望包含没有任何订单和发货记录的用户(显示 0 计数),可移除 whereHas/orWhereHas,只保留 withCount —— 但需注意分页性能,因为会扫描全量用户。
- ✅ 建议为 orders.created_at 和 shippings.created_at 添加数据库索引,能显著提升查询效率。
最终获取的 $report 是一个 LengthAwarePaginator 实例,在视图中直接遍历,访问 user->orders_count 和 user->shippings_count 属性即可。一个清晰且高效的运营报表,就是这样搭建而成的。
游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。
同类文章
列表遍历时动态判断阈值并返回相应文本
遍历数值列表时,先筛选满足阈值的元素,再根据结果输出列表或友好提示。推荐使用列表推导式结合条件判断,注意边界用`>=`,空列表自动为假。也可用`any()`提前终止遍历,提升效率。此法简洁,避免显式循环,特别适合阈值筛选。
Maven项目中如何强制使用本地构建的依赖版本
多模块开发中强制使用本地依赖的正确做法是使用-SNAPSHOT版本并配合`-U`参数强制更新,或重构为多模块项目统一管理生命周期。避免使用版本范围语法或手动复制JAR,确保构建行为可靠可重复。
Go语言net.Conn并发写安全与原子性保障解析
Go标准库中net Conn支持并发方法调用,但Write()不保证原子性。多goroutine同时写入时,系统可能拆分数据包,导致内容交错,破坏消息边界。必须使用互斥锁或bufio Writer等显式同步机制确保写操作完整性,不可依赖系统调用本身的原子性。
Python在Windows系统中获取指定卷标U盘驱动器字母的方法
使用Pythonwmi库通过Win32_Volume接口查询Windows系统中卷标为“TOSHIBA”的U盘盘符。需安装wmi和pywin32,注意大小写区分及多设备过滤。仅适用于Windows。
TP6.0消息已读功能基于Redis Bitmap未读计数方案
TP6 0中使用Redis位图实现消息已读标记,每条消息仅占一个比特,内存效率高。需将消息ID映射为连续偏移量,通过SETBIT和GETBIT操作。需提前维护用户ID到偏移量的映射表,注意键过期与驱动类型(phpredis与predis)问题。此方法内存极省,适合海量消息场景,且需确保偏移量唯一。
- 日榜
- 周榜
- 月榜
相关攻略
2026-07-08 06:50
2026-07-08 06:50
2026-07-08 06:50
2026-07-08 06:49
2026-07-08 06:49
2026-07-08 06:49
2026-07-08 06:49
2026-07-08 06:49
热门教程
- 游戏攻略
- 安卓教程
- 苹果教程
- 电脑教程
热门话题

