laravel 13 中按自然日分组统计订单需确保时区一致、正确截断日期并选择合适聚合方式:中小数据用 wheredate(),大数据用 db::raw('date(created_at)') + groupby();须配置 $casts 和数据库时区对齐。

在 Laravel 13 中按自然日(即以 00:00:00 到 23:59:59 为一天)分组统计订单数据,关键在于正确处理时间字段的时区、日期截断和聚合逻辑。Laravel 13 默认使用 Eloquent 的 `Carbon` 实例,配合数据库原生函数(如 `DATE()` 或 `CAST`)或 `whereDate()` 可实现精准分日统计。
使用 whereDate() 按天查询并手动聚合
适合中小数据量、需要灵活条件筛选的场景。`whereDate()` 会自动将时间字段转为日期字符串进行匹配,底层兼容 MySQL、PostgreSQL 和 SQLite。
- 确保订单模型的时间字段(如 created_at)是 `datetime` 类型且已设置 `$dates` 或 `casts`(Laravel 13 推荐用
protected $casts = ['created_at' => 'datetime'];) - 用
whereDate('created_at', $date)查询某一天全部订单,再调用count()、sum('total')等聚合方法 - 若需连续多日数据,可循环日期范围,但注意避免 N+1 查询——更推荐用原生分组方式(见下)
用 DB::raw() + groupBy() 做数据库级分日聚合
性能最优,尤其适用于大数据量报表。直接在 SQL 层完成日期截断与分组,减少 PHP 层数据搬运。
- MySQL:用
DATE(created_at)或CAST(created_at AS DATE)提取日期部分 - PostgreSQL:用
created_at::DATE或DATE(created_at) - Laravel 示例(MySQL):
$stats = Order::selectRaw('DATE(created_at) as date')
->selectRaw('COUNT(*) as count')
->selectRaw('SUM(total) as total_amount')
->whereBetween('created_at', [$start, $end])
->groupBy('date')
->orderBy('date')
->get();
结果中 date 是字符串格式(如 "2025-04-01"),天然对应自然日。
注意时区与数据库配置一致性
Laravel 应用时区(config/app.php 中的 'timezone')必须与数据库服务器时区一致,否则 DATE(created_at) 可能跨日错位。
- 检查 MySQL 时区:
SELECT @@global.time_zone, @@session.time_zone; - 在 Laravel 中统一设为
'Asia/Shanghai',并在数据库连接配置中添加'options' => [PDO::MYSQL_ATTR_INIT_COMMAND => "SET time_zone = '+08:00'"](MySQL) - 避免依赖 PHP 的
date_default_timezone_set(),优先由数据库完成时间解析
扩展:支持动态时间范围与空日期补全
若需返回固定区间(如最近 7 天)且包含 0 订单的日期,可先生成日期数组,再左关联查询:
- 用 Carbon 构造日期列表:
collect(range(0, 6))->map(fn($i) => today()->subDays($i)->toDateString()) - 结合
DB::table()->rightJoinSub()或应用层 merge 补零(简单场景推荐后者) - 示例补全逻辑:
$dates = collect(range(0, 6))->map(fn($i) => today()->subDays($i)->toDateString());
$rawStats = Order::selectRaw('DATE(created_at) as date, COUNT(*) as count')
->whereBetween('created_at', [today()->subDays(6), now()])
->groupBy('date')
->pluck('count', 'date');
$result = $dates->mapWithKeys(fn($d) => [$d => $rawStats->get($d, 0)]);











