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

基于食谱受欢迎程度排序顶级用户的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.07 07:40:27