最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
Laravel 用户订单与发货统计报表:按时间范围聚合查询教程
时间:2026-07-07 09:50:03 编辑:袖梨 来源:一聚教程网
本文详解如何在 Laravel 中构建用户维度的订单与发货数量统计报表,支持自定义起止日期筛选,通过 withCount 与条件关联查询精准获取每位用户在指定周期内的订单数和发货数。
本文详解如何在 laravel 中构建用户维度的订单与发货数量统计报表,支持自定义起止日期筛选,通过 `withcount` 与条件关联查询精准获取每位用户在指定周期内的订单数和发货数。
在开发后台报表功能时,常需统计每位用户在某段时间内的业务行为数据,例如订单提交量与发货请求量。本教程将基于你已定义的 Eloquent 关系(User ↔ Order、User ↔ Shipping),提供健壮、高效且可维护的查询实现方案。
✅ 正确做法:统一时间条件 + 条件聚合
你原代码中存在几个关键问题:
- whereHas() 内部误调用 get(),破坏了链式查询;
- orWhereHas() 未包裹在闭包中,导致 SQL 逻辑错误(OR 优先级干扰主 WHERE);
- groupBy('created_at') 在子查询中无意义,且会阻碍计数聚合;
- orderByRaw('orders_count + shippings_count DESC') 依赖 withCount 生成的字段,但原始查询未确保所有用户都满足时间条件(可能导致部分用户计数为 0 却被排除)。
✅ 推荐写法(无 scope 版):
$report = User::withCount([ 'orders' => function ($q) use ($from, $to) { if (!empty($from)) { $q->whereDate('created_at', '>=', $from); } if (!empty($to)) { $q->whereDate('created_at', '<=', $to); } }, 'shippings' => function ($q) use ($from, $to) { if (!empty($from)) { $q->whereDate('created_at', '>=', $from); } if (!empty($to)) { $q->whereDate('created_at', '<=', $to); } }])->whereHas('shippings', function ($q) use ($from, $to) { if (!empty($from)) { $q->whereDate('created_at', '>=', $from); } if (!empty($to)) { $q->whereDate('created_at', '<=', $to); }})->orWhereHas('orders', function ($q) use ($from, $to) { if (!empty($from)) { $q->whereDate('created_at', '>=', $from); } if (!empty($to)) { $q->whereDate('created_at', '<=', $to); }})->orderByRaw('orders_count + shippings_count DESC')->paginate(25);
⚠️ 注意:whereHas(...)->orWhereHas(...) 的组合默认会生成 (has_shippings AND has_orders) OR ... 的歧义逻辑。为确保语义清晰(即「有该时间段内订单 或 发货的用户」),建议外层包裹 where(function () { ... }):
$report = User::withCount([ 'orders' => fn($q) => $this->applyDateFilter($q, $from, $to), 'shippings' => fn($q) => $this->applyDateFilter($q, $from, $to),])->where(function ($q) use ($from, $to) { $q->whereHas('shippings', fn($sub) => $this->applyDateFilter($sub, $from, $to)) ->orWhereHas('orders', fn($sub) => $this->applyDateFilter($sub, $from, $to));})->orderByRaw('orders_count + shippings_count DESC')->paginate(25);
并在 Controller 或 Trait 中定义复用方法:
protected function applyDateFilter($query, $from, $to){ if ($from) $query->whereDate('created_at', '>=', $from); if ($to) $query->whereDate('created_at', '<=', $to); return $query;}
✅ 进阶优化:添加全局作用域(推荐)
为提升可读性与复用性,可在 Order 和 Shipping 模型中添加本地作用域:
// AppModelsOrder.phppublic function scopeWhereDateBetween($builder, $from = null, $to = null){ if ($from) { $builder->whereDate('created_at', '>=', $from); } if ($to) { $builder->whereDate('created_at', '<=', $to); } return $builder;}
同理为 Shipping 模型添加相同作用域。之后查询可大幅简化:
$report = User::withCount([ 'orders' => fn($q) => $q->whereDateBetween($from, $to), 'shippings' => fn($q) => $q->whereDateBetween($from, $to),])->where(function ($q) use ($from, $to) { $q->whereHas('shippings', fn($sub) => $sub->whereDateBetween($from, $to)) ->orWhereHas('orders', fn($sub) => $sub->whereDateBetween($from, $to));})->orderByDesc(DB::raw('orders_count + shippings_count'))->paginate(25);
? 使用说明与注意事项
- 日期格式:确保 $from 和 $to 是 Y-m-d 格式字符串(如 '2024-01-01'),whereDate() 会自动忽略时间部分,避免时区偏差;
- 空值处理:若仅选开始日期或结束日期,上述逻辑仍能正确生效;
- 性能提示:为 created_at 字段添加数据库索引(尤其在大数据量场景下);
- 前端校验:建议在表单侧限制 $to >= $from,并传递 ISO 标准日期;
- 空结果处理:分页结果中,orders_count 和 shippings_count 均为整数(含 0),无需额外判空。
最终,你可在 Blade 模板中直接访问:
@foreach($report as $user) <tr> <td>{{ $user->name }}</td> <td>{{ $user->orders_count }}</td> <td>{{ $user->shippings_count }}</td> <td>{{ $user->orders_count + $user->shippings_count }}</td> </tr>@endforeach
此方案兼顾准确性、可维护性与扩展性,是 Laravel 报表类查询的最佳实践之一。
相关文章
- 《Disney Lorcana: Wilds Unknown》预购开启 首批《Toy Story》及皮克斯卡牌购买指南 07-29
- 车来了赶车闹钟如何设置 07-29
- 崩坏星穹铁道余晖残卷巨剑守护打法攻略 07-29
- 崩坏星穹铁道砂金角色部分背景介绍 07-29
- 崩坏3雷电芽衣什么时候上线 07-29
- 玩具熊的五夜后宫4代噩梦气球男孩Nightmare Balloon Boy介绍 07-29