基于食谱受欢迎程度排序顶级用户的Laravel实现问题
基于食谱受欢迎程度排序顶级用户的Laravel实现问题
看起来你现在的思路有点走偏啦——你当前用recipe_popularity统计的是用户有多少个"符合popular条件的食谱",但这完全不是咱们要的「用户所有食谱的总点赞/总保存/总点踩之和」对吧?咱们的核心需求是把用户名下所有食谱的互动数据加总,再按「保存数>点赞数>点踩数(越少越好)」的优先级排序,下面给你一步步捋清楚解决方案:
问题根源分析
你当前的代码:
$authorsOfTheWeek = User::select(['id','name', 'photo']) ->withCount([ 'recipes as recipe_popularity' => fn($query) => $query->popular() ]) ->having('recipe_popularity', '>', 0) ->orderByDesc('recipe_popularity') ->get();
这里的recipe_popularity其实是统计该用户有多少个满足popular作用域的食谱,而不是把每个食谱的likesCount/savedCount等指标加起来,完全不符合"总互动量排序"的需求。
正确解决方案(Laravel 8.37+ 推荐写法)
Laravel 8.37之后支持对「关联的关联」使用withSum和withCount,咱们可以直接在User查询中汇总所有食谱的总互动数据:
1. 直接查询写法
use Illuminate\Database\Eloquent\Builder; $authorsOfTheWeek = User::select(['id', 'name', 'photo']) // 汇总所有食谱的总点赞数 ->withSum([ 'recipes.votes as total_likes' => fn(Builder $q) => $q->where('vote', 1), // 汇总所有食谱的总点踩数 'recipes.votes as total_dislikes' => fn(Builder $q) => $q->where('vote', -1), ]) // 汇总所有食谱的总保存数 ->withCount([ 'recipes.savedByUsers as total_saves' ]) // 按优先级排序:保存数(权重最高)> 点赞数 > 点踩数(越少越靠前) ->orderByDesc('total_saves') ->orderByDesc('total_likes') ->orderBy('total_dislikes') // 过滤掉完全没有互动的用户(可选) ->havingRaw('total_saves + total_likes + total_dislikes > 0') ->get();
2. 封装成User模型作用域(复用更方便)
在User.php中添加一个自定义作用域:
use Illuminate\Database\Eloquent\Builder; public function scopeTopByRecipePopularity(Builder $query): Builder { return $query->select(['id', 'name', 'photo']) ->withSum([ 'recipes.votes as total_likes' => fn(Builder $q) => $q->where('vote', 1), 'recipes.votes as total_dislikes' => fn(Builder $q) => $q->where('vote', -1), ]) ->withCount([ 'recipes.savedByUsers as total_saves' ]) ->orderByDesc('total_saves') ->orderByDesc('total_likes') ->orderBy('total_dislikes') ->havingRaw('total_saves + total_likes + total_dislikes > 0'); }
之后调用就非常简洁:
$authorsOfTheWeek = User::topByRecipePopularity()->get();
兼容低版本Laravel的写法(8.37以下)
如果你的Laravel版本不支持关联的关联withSum,可以用子查询的方式来计算总和:
use Illuminate\Support\Facades\DB; $authorsOfTheWeek = User::select([ 'users.id', 'users.name', 'users.photo', // 子查询计算总点赞数 DB::raw('(SELECT COUNT(*) FROM votes WHERE votes.recipe_id IN (SELECT id FROM recipes WHERE recipes.user_id = users.id) AND votes.vote = 1) as total_likes'), // 子查询计算总点踩数 DB::raw('(SELECT COUNT(*) FROM votes WHERE votes.recipe_id IN (SELECT id FROM recipes WHERE recipes.user_id = users.id) AND votes.vote = -1) as total_dislikes'), // 子查询计算总保存数 DB::raw('(SELECT COUNT(*) FROM saved_recipes WHERE saved_recipes.recipe_id IN (SELECT id FROM recipes WHERE recipes.user_id = users.id)) as total_saves'), ]) ->orderByDesc('total_saves') ->orderByDesc('total_likes') ->orderBy('total_dislikes') ->havingRaw('total_saves + total_likes + total_dislikes > 0') ->get();
补充说明
- 排序优先级严格按照你需求的「保存数(权重最高)> 点赞数 > 点踩数(越少越靠前)」来设置
havingRaw用于过滤掉没有任何互动(点赞/点踩/保存全为0)的用户,如果你想保留这类用户可以去掉这个条件- 数据量大的话,推荐用
join的方式代替子查询(性能更优),如果需要可以再提,我给你写join版本的代码~
内容来源于stack exchange
相关产品推荐
相关产品推荐

