一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

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 报表类查询的最佳实践之一。

热门栏目