Laravel中如何实现Eloquent关联仅返回每个玩家的最新Turn记录?
在Laravel中实现返回每个玩家最新Turn记录的Eloquent关联
这是一个很常见的需求——要在关联中返回分组后的最新记录,我来帮你把你已经验证过的原生SQL转换成Eloquent关联实现:
首先,在你的Game模型中,这样定义currentTurns关联方法:
use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Support\Facades\DB; // ... public function currentTurns(): HasMany { // 构建子查询:获取当前游戏下每个玩家的最新turn的时间戳 $latestTurnsSubquery = \App\Models\Turn::select('player_id', DB::raw('MAX(created_at) as created_at')) ->whereColumn('game_id', 'games.id') // 关联当前Game模型的id ->groupBy('player_id'); return $this->hasMany(\App\Models\Turn::class) // 关联子查询结果作为临时表 ->joinSub($latestTurnsSubquery, 'latest_turns', function ($join) { $join->on('turns.player_id', '=', 'latest_turns.player_id') ->on('turns.created_at', '=', 'latest_turns.created_at'); }) // 确保只返回turns表的字段,避免子查询字段干扰 ->select('turns.*'); }
代码解释:
- 子查询部分:
$latestTurnsSubquery会动态匹配当前游戏的id(通过whereColumn('game_id', 'games.id')关联父模型的主键),然后按player_id分组,取出每个玩家在该游戏中的最新created_at时间。 - 关联子查询:用
joinSub方法将子查询结果作为临时表和turns表关联,通过player_id和created_at两个字段匹配,精准定位到每个玩家的最新那条Turn记录。 - 字段选择:最后用
select('turns.*')确保返回的是turns表的完整字段,避免子查询中的重复字段混入结果集。
使用示例:
你可以像调用普通Eloquent关联一样使用它:
$game = \App\Models\Game::find(1); $currentTurns = $game->currentTurns->all(); // 结果会包含:玩家1的id=4记录、玩家2的id=2记录、玩家3的id=3记录
额外优化建议:
如果你的turns表的id是自增主键,且最新的Turn记录一定对应最大的id,可以把子查询中的MAX(created_at)换成MAX(id),这样查询性能可能会更好(因为主键通常有索引):
$latestTurnsSubquery = \App\Models\Turn::select('player_id', DB::raw('MAX(id) as id')) ->whereColumn('game_id', 'games.id') ->groupBy('player_id'); // 关联时改成匹配id ->joinSub($latestTurnsSubquery, 'latest_turns', function ($join) { $join->on('turns.id', '=', 'latest_turns.id'); })
内容的提问来源于stack exchange,提问作者Jan Richter
相关产品推荐
相关产品推荐

