Laravel列访问器sum()求和实现用户得分排行榜遇阻
嘿,我来帮你搞定这个Laravel用户得分排行榜的问题!你的表结构逻辑很清晰,核心就是要统计每个用户所有成功挑战的得分总和对吧?咱们一步步来解决:
第一步:先确保模型关联正确
首先在User模型里定义和Answer的关联关系,让Eloquent能正确关联用户的挑战记录:
// app/Models/User.php public function answers() { return $this->hasMany(Answer::class); }
第二步:用「访问器」实现单用户总得分
如果你只是想在单个用户实例里获取总得分,列访问器确实好用,直接在User模型里添加这个访问器:
public function getTotalScoreAttribute() { // 只统计成功挑战的得分,默认如果没有得分返回0 return $this->answers()->where('success', true)->sum('score') ?? 0; }
之后你就可以直接通过$user->total_score拿到这个用户的总得分啦~
第三步:高效制作排行榜(重点!避免N+1查询)
但如果是做排行榜,直接用上面的访问器会有N+1查询问题(每查一个用户就单独查一次得分),这时候用Laravel的withSum方法来预加载统计数据才是最优解:
$leaderboard = User::withSum( // 给统计结果起别名total_score,只统计成功的挑战 ['answers as total_score' => function ($query) { $query->where('success', true); }], 'score' // 要求和的字段是answers表的score ) ->selectRaw('users.*, COALESCE(total_score, 0) as total_score') // 把null得分转成0 ->orderByDesc('total_score') // 按总得分降序排列 ->get(['id', 'name', 'mail']); // 只取需要的字段,提升性能
这样执行完,$leaderboard里的每个用户对象都自带total_score字段,而且全程只需要2次数据库查询,非常高效!
备选方案:用DB门面直接写原生查询
如果你更喜欢用原生SQL风格,也可以这么写:
$leaderboard = DB::table('users') ->leftJoin('answers', 'users.id', '=', 'answers.user_id') ->where('answers.success', true) ->select( 'users.id', 'users.name', 'users.mail', DB::raw('COALESCE(SUM(answers.score), 0) as total_score') ) ->groupBy('users.id', 'users.name', 'users.mail') ->orderByDesc('total_score') ->get();
这样不管是用Eloquent还是原生查询,都能轻松实现你要的排行榜功能啦~
内容的提问来源于stack exchange,提问作者T0wn3R
相关产品推荐
相关产品推荐

