You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel一对多关联下按平均评分排序帖子及过滤问题

实现按平均评分排序帖子并过滤最低评分数量

嘿,我来帮你搞定这个问题!要实现按平均评分排序,同时过滤掉评分数量不足X条的帖子,我们可以利用Laravel的查询构建器和关联聚合功能来实现,下面分两种方案给你讲解:

方案一:使用Laravel的withAggregate(推荐,更简洁)

Laravel 8+ 提供了withAggregate方法,可以直接获取关联模型的聚合值(比如平均值),配合withCount统计评分数量,再用having过滤条件:

// 替换X为你需要的最小评分数量,比如3;stars替换为你的Rating模型中存储评分的字段名
$minRatings = X;

return Post::with(['user'])
    // 计算该帖子的平均评分,别名ratings_avg_stars
    ->withAggregate('ratings', 'stars', 'avg')
    // 统计该帖子的评分总数
    ->withCount('ratings')
    // 过滤出评分数量>=X的帖子
    ->having('ratings_count', '>=', $minRatings)
    // 按平均评分倒序排序(热门在前)
    ->orderByDesc('ratings_avg_stars')
    ->paginate(15)->onEachSide(1);

关键说明:

  • withAggregate('ratings', 'stars', 'avg'):第一个参数是关联名称(你的Post模型里已经定义了ratings关联),第二个是Rating表中存储评分的字段(比如stars、score,换成你实际的字段名),第三个是聚合函数(这里用avg求平均)。
  • withCount('ratings'):自动为每个Post模型添加ratings_count属性,记录该帖子的评分数量。
  • having('ratings_count', '>=', $minRatings):因为ratings_count是聚合统计出来的字段,所以要用having而不是where来过滤。

方案二:使用原生SQL聚合(兼容性更强)

如果你用的是Laravel 8以下版本,或者更习惯原生SQL的写法,可以用selectRaw结合leftJoin来实现:

$minRatings = X;

return Post::with(['user'])
    ->leftJoin('ratings', 'posts.id', '=', 'ratings.post_id')
    // 选择帖子所有字段,同时计算平均评分和评分数量
    ->selectRaw('posts.*, AVG(ratings.stars) as avg_rating, COUNT(ratings.id) as rating_count')
    // 按帖子ID分组,确保每个帖子只返回一条记录
    ->groupBy('posts.id')
    // 过滤评分数量>=X的帖子
    ->having('rating_count', '>=', $minRatings)
    // 按平均评分倒序排序
    ->orderByDesc('avg_rating')
    ->paginate(15)->onEachSide(1);

注意事项:

  • 如果你的MySQL开启了严格模式,groupBy可能需要包含posts表的所有字段,这时候可以把posts.*替换成具体的字段列表,或者在config/database.php中关闭strict模式(不推荐,最好用方案一)。
  • 同样要替换stars为你实际的评分字段名。

这样就能完美实现你要的按平均评分排序,同时只保留至少有X条评分的帖子啦!

内容的提问来源于stack exchange,提问作者Alex

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:02:29