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

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.*');
}

代码解释:

  1. 子查询部分:$latestTurnsSubquery会动态匹配当前游戏的id(通过whereColumn('game_id', 'games.id')关联父模型的主键),然后按player_id分组,取出每个玩家在该游戏中的最新created_at时间。
  2. 关联子查询:用joinSub方法将子查询结果作为临时表和turns表关联,通过player_id和created_at两个字段匹配,精准定位到每个玩家的最新那条Turn记录。
  3. 字段选择:最后用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 00:47:37