
本文介绍在 laravel 中通过 eloquent 和 query builder 两种方式,为每个商品关联计算平均评分(avg)和评分总数(count),并确保空评分商品也能正确显示为 0。
本文介绍在 laravel 中通过 eloquent 和 query builder 两种方式,为每个商品关联计算平均评分(avg)和评分总数(count),并确保空评分商品也能正确显示为 0。
在构建电商或内容平台的商品评价系统时,常需同时展示商品列表及其对应的用户评分统计——包括平均分(保留小数)与总评分人数。Laravel 提供了灵活的数据聚合能力,可通过关系查询或原生联表高效实现该需求,无需对每个商品发起 N+1 查询。
✅ 推荐方案:使用 leftJoin + 聚合函数(兼顾性能与完整性)
最高效的方式是利用数据库层面的 LEFT JOIN,将 products 表与 reviews 表关联,并对每个商品一次性计算 AVG() 与 COUNT()。关键点在于使用 LEFT JOIN(而非 INNER JOIN),以确保即使某商品暂无任何评价,仍能出现在结果中,且其评分字段可设为默认值(如 0):
use Illuminate\Support\Facades\DB;
$products = DB::table('products')
->leftJoin('reviews', 'reviews.product_id', '=', 'products.id')
->select(
'products.*',
DB::raw('COALESCE(AVG(reviews.rating), 0) as average_rating'),
DB::raw('COUNT(reviews.id) as rating_count')
)
->groupBy('products.id') // 必须按主键分组,否则报错
->get();
⚠️ 注意事项:
- GROUP BY products.id 不可省略,否则聚合函数会作用于整个结果集;
- COALESCE(AVG(...), 0) 确保无评价的商品 average_rating 返回 0.0(而非 null);
- COUNT(reviews.id) 统计的是匹配到的 review 记录数,自动忽略 NULL(即未关联 review 的行),天然满足“零评分”场景。
✅ 进阶方案:基于 Eloquent 模型关系(更面向对象)
若已定义 Eloquent 模型及关联关系,推荐在 Product 模型中定义 ratings 关系,并使用 withCount() 与 withAvg()(Laravel 8.35+ 支持):
PHP中文网提供Laravel 13.2.0版本下载,Laravel框架 是基于 PHP 8.3+ 的高性能框架,官方推荐通过 Composer 安装。它内置 AI SDK、JSON:API Resources 及原生向量搜索,支持属性驱动开发与队列路由,大幅提升开发效率。相比旧版,13.2.0 优化了缓存 TTL 管理与实时通信,无需 Redis 即可横向扩展。作为现代 Web 开发首选,它兼顾安全与极速体验,助您快速构建企业级应用。
// 在 Product.php 模型中
public function ratings()
{
return $this->hasMany(Review::class, 'product_id');
}
然后在控制器中调用:
use App\Models\Product;
$products = Product::withCount('ratings')
->withAvg('ratings', 'rating')
->get()
->map(function ($product) {
return $product->merge([
'average_rating' => round($product->ratings_avg_rating, 2),
'rating_count' => $product->ratings_count,
]);
});
此方式语义清晰、易于测试,且支持后续链式扩展(如添加 whereHas() 等条件)。但注意:withAvg() 返回的是 float|null,建议用 round() 格式化小数位,并用 ?? 0 处理空值(或如上例在 map() 中统一处理)。
? 验证与调试提示
- 使用 dd($products->toArray()) 查看原始结构,确认 average_rating 与 rating_count 字段存在且类型正确;
- 若遇到 SQLSTATE[42000] 分组错误,请检查是否遗漏 groupBy() 或 SELECT 中包含未聚合/未分组的非主键字段;
- 生产环境建议为 reviews.product_id 添加数据库索引,大幅提升联表性能:
ALTER TABLE reviews ADD INDEX idx_product_id (product_id);
综上,联表聚合(Query Builder)适合高性能批量统计,Eloquent 关系聚合则更利于代码可维护性。根据项目复杂度与团队规范选择即可——两者均能优雅解决“每个商品的平均分与评分总数”这一核心需求。










