Laravel用Eloquent实现多态关联浏览量按分类分组统计的方法
Eloquent 等价实现方案
方案1:与原生SQL逻辑完全对齐的查询写法
直接通过查询构建器复现原SQL的逻辑链路,每一步与原SQL对应:
$categoryTotalViews = Post::query() // 关联所属分类表,对应LEFT JOIN categories逻辑 ->leftJoin('categories', 'categories.id', '=', 'posts.category_id') // 关联浏览量表,对应LEFT JOIN views逻辑 ->leftJoin('views', function ($join) { $join->on('views.viewable_id', '=', 'posts.id') // 匹配多态关联的类型约束,锁定Post类型的浏览记录 ->where('views.viewable_type', Post::class); }) // 按分类ID分组,避免重名分类导致的统计偏差 ->groupBy('categories.id', 'categories.title') // 指定返回字段和统计值 ->selectRaw( 'categories.title AS category, COUNT(views.id) AS totalViews' ) ->get();
相比你提供的原生SQL,这里把
views.viewable_type的判断放到了JOIN闭包内,而非WHERE条件中。原写法会导致LEFT JOIN views退化为INNER JOIN,最终无法返回没有任何浏览记录的分类(这类分类的totalViews会被过滤掉),调整后逻辑更严谨。
方案2:基于关联链路的Eloquent优雅写法
不需要手写Join逻辑,直接利用模型间已定义的关联完成统计。首先在Category模型中补充对应反向关联:
class Category extends Model { // 对应Post模型中belongsTo(Category)的反向关联 public function posts() { return $this->hasMany(Post::class); } }
之后直接使用Laravel内置的关联聚合方法完成查询:
$categoryTotalViews = Category::query() // 自动沿Category -> Post -> View的关联链路,统计每个分类下所有浏览记录的总和 ->withSum('posts.views as totalViews') // 指定返回字段 ->select('title as category', 'totalViews') ->get();
该写法由Eloquent自动处理多态关联的类型匹配、表关联、分组逻辑,生成的查询结果与手写逻辑等价,维护成本更低。
注意事项
- 分组统计时必须以
categories.id作为分组主键,不要仅按分类名称分组,避免不同分类重名导致的统计合并错误。 - 所有非聚合的查询字段必须包含在GROUP BY子句中,适配MySQL严格模式的校验规则,避免出现非预期的结果偏差。
内容的提问来源于stack exchange,提问作者Andreas Hunter
相关产品推荐
相关产品推荐

