Laravel 10 中 with() 无法直接预加载每日关联统计数据,需用 withCount+子查询、leftJoin聚合、自定义withCount闭包或缓存方案实现;推荐单次 leftJoin + groupBy 方式避免 N+1。

在 Laravel 10 中,使用 Eloquent 的 with() 方法无法直接实现「每日关联统计数据」的预加载(比如每天有多少篇文章、订单数等),因为 with() 只支持关系预加载,不支持聚合查询 + 分组。要实现「按天统计关联数据并和主模型关联展示」,需结合 withCount()、子查询(Subquery)、或原生 SQL + leftJoin 等方式。以下是几种实用、可落地的方案。
方案一:用 withCount + 子查询统计「当天」关联数
适用于:需要查出每个日期(如 posts 表的 created_at 日期)对应的关联记录数量,且主模型是「日期维度表」或你已按日分组。
例如:统计每一天发布的文章数,以及当天的评论总数:
// 假设你有一个 DateRange 模型或用 DB::raw('DATE(created_at)') 构建日期列表
$dates = DB::table('posts')
->selectRaw('DATE(created_at) as date')
->groupBy('date')
->orderBy('date', 'desc')
->limit(7)
->get();
// 为每个日期补上当日评论数(用子查询)
$datesWithCommentCount = $dates->map(function ($date) {
$commentCount = DB::table('comments')
->whereDate('created_at', $date->date)
->count();
return array_merge((array)$date, ['comment_count' => $commentCount]);
});
✅ 优点:逻辑清晰、易调试;❌ 缺点:N+1 查询风险(若日期多,循环查评论)。
方案二:单次查询 + leftJoin 实现「按日聚合」(推荐)
适用于:主表是日期维度(如自定义日期表),或你想从 posts 表出发,统计每天的文章数 + 关联评论数/点赞数等。
用 DB::table 或 Post::query() 配合 leftJoin 和 groupBy:
$dailyStats = DB::table('posts')
->selectRaw('DATE(created_at) as date')
->selectRaw('COUNT(*) as post_count')
->selectRaw('COUNT(comments.id) as comment_count')
->selectRaw('COUNT(likes.id) as like_count')
->leftJoin('comments', function ($join) {
$join->on('comments.post_id', '=', 'posts.id')
->whereColumn('comments.created_at', 'posts.created_at'); // 粗略匹配到同一天(注意时区)
})
->leftJoin('likes', function ($join) {
$join->on('likes.post_id', '=', 'posts.id')
->whereDate('likes.created_at', DB::raw('DATE(posts.created_at)'));
})
->groupBy('date')
->orderBy('date', 'desc')
->limit(30)
->get();
⚠️ 注意:whereDate() 在 join 条件中需用 DB::raw() 包裹,否则 Laravel 不识别;时区统一很重要(建议数据库和 PHP 都用 UTC 或显式转换)。
方案三:用 withCount + 自定义子查询(Laravel 10+ 支持)
如果你坚持用 Eloquent Model(如 Post),又想「按天」统计关联数,可以给 withCount() 传入闭包子查询:
// 统计每篇文章「当天」的评论数(非全局,是 per-post)
$posts = Post::withCount(['comments' => function ($query) {
$query->whereDate('created_at', DB::raw('DATE(posts.created_at)'));
}])
->get();
// 但这样仍是按 post 查,不是按 day —— 所以更适合「查某天所有文章及其当日评论数」
$todayPosts = Post::whereDate('created_at', today())
->withCount(['comments' => function ($query) {
$query->whereDate('created_at', today());
}])
->get();
? 提示:这种写法适合「查某天的数据详情」,不适合「拉取最近 7 天的聚合统计」。
方案四:用 Laravel Scout 或缓存预热高频统计(生产优化)
如果「每日统计」是高频访问(如后台仪表盘),不建议每次实时查。可:
- 用
schedule:run每日凌晨跑一个 Artisan 命令,把昨日统计写入daily_stats表 - 用 Redis 缓存 JSON,键为
stats:daily:2024-06-15,TTL 设 24 小时 - 前端请求时直接读缓存或宽表,零数据库压力
缓存示例:
$key = 'stats:daily:' . today()->toDateString();
$data = Cache::remember($key, 3600, function () {
return DB::table('posts')
->selectRaw('DATE(created_at) as date')
->selectRaw('COUNT(*) as post_count')
->selectRaw('COUNT(comments.id) as comment_count')
->leftJoin('comments', 'comments.post_id', '=', 'posts.id')
->whereDate('posts.created_at', today())
->groupBy('date')
->first();
});
不复杂但容易忽略:日期函数跨数据库差异大(MySQL 的 DATE()、PostgreSQL 的 DATE(posts.created_at)、SQLite 的 DATE(posts.created_at) 行为一致,但时区处理需统一)。上线前务必在目标环境验证 SQL 输出。


















