如何借助模型关联通过多态表计算社交网络中的karma值?
问题描述
我正在开发类Reddit的社交应用,用户发布的Message和Comment会收到点赞(vote值为1)和点踩(vote值为-1),以此计算用户的karma值。已建立User、Message、Comment、Vote模型,所有关联关系配置正常(如User HasMany Message、Comment BelongsTo Message等)。其中Vote为多态表,包含vote、user_id等字段,关联关系已正确设置。
最初尝试用以下代码计算用户karma值,但无法正常运行:
$user = User::find(...); $karma = $user->messages()->votes()->sum('vote');
目前通过以下方式实现了功能,但觉得不够简洁,想请教是否有仅通过模型关联实现的更优写法(后续还要对Comment做同样处理):
$user = User::find(...); $ids = $user->messages->pluck('id')->toArray(); $karma = Vote::where('voteable_type', 'App\Models\Message') ->whereIn('voteable_id', $ids) ->sum('vote');
优化方案
可以通过Eloquent的关联特性实现更简洁的写法,以下是几种基于模型关联的解决方案:
1. 单独计算Message的Karma值
在User模型中定义hasManyThrough关联,直接关联到用户所有Message的投票记录:
// app/Models/User.php public function messageVotes() { return $this->hasManyThrough( Vote::class, Message::class, 'user_id', // Message表关联User的外键 'voteable_id', // Vote表关联Message的外键 'id', // User表主键 'id' // Message表主键 )->where('voteable_type', Message::class); }
之后计算Message的karma值只需一行代码:
$user = User::find(...); $messageKarma = $user->messageVotes()->sum('vote');
2. 同时计算Message和Comment的总Karma值
先在User模型中添加Comment Votes的关联,再新增一个计算总Karma的访问器:
// app/Models/User.php public function commentVotes() { return $this->hasManyThrough( Vote::class, Comment::class, 'user_id', 'voteable_id', 'id', 'id' )->where('voteable_type', Comment::class); } // 定义总Karma访问器 public function getTotalKarmaAttribute() { return $this->messageVotes()->sum('vote') + $this->commentVotes()->sum('vote'); }
使用时直接访问属性即可:
$user = User::find(...); $totalKarma = $user->total_karma; // 支持蛇形命名访问
3. 统一关联所有内容的投票(更灵活)
如果需要一次性关联用户发布的Message和Comment的所有投票,可以用闭包筛选多态类型:
// app/Models/User.php public function contentVotes() { return $this->hasManyThrough( Vote::class, \Illuminate\Database\Eloquent\Model::class, 'user_id', 'voteable_id', 'id', 'id' )->where(function ($query) { $query->where('voteable_type', Message::class) ->orWhere('voteable_type', Comment::class); }); }
计算总Karma时直接调用:
$totalKarma = $user->contentVotes()->sum('vote');
这些写法完全依赖模型关联,避免了手动处理ID数组,代码更简洁且符合Eloquent的设计规范。
内容的提问来源于stack exchange,提问作者luisfer
相关产品推荐
相关产品推荐

