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

Laravel实现按动态对手字段条件排序玩家游戏列表

解决Laravel中按动态对手分组游戏列表的问题

问题背景

我有两个Laravel模型:Player(玩家)和Game(游戏)。Game模型包含两个BelongsTo关联:homePlayer(主场玩家)和awayPlayer(客场玩家),数据库games表字段包括id、home_player_id、away_player_id、score等。

需求是在玩家个人页面的游戏列表中,按对手分组展示——同一个对手的游戏放在一起。但对手可能是主场玩家也可能是客场玩家,无法直接通过单一关联字段排序,需要根据当前玩家动态获取对手ID来实现分组排序。

给出的Game模型代码:

class Game extends Model {
  protected $with = ['homePlayer', 'awayPlayer'];
  public function homePlayer(){
    return $this->belongsTo(Player::class, 'home_player_id');
  }
  public function awayPlayer(){
    return $this->belongsTo(Player::class, 'away_player_id');
  }
}

方案一:数据库层面生成对手ID再分组(推荐)

先在数据库查询阶段动态计算出对手ID,排序后再通过集合分组,这种方式性能更优,适合数据量较大的场景。

1. 封装查询作用域

在Game模型中添加作用域,封装“查询当前玩家参与的所有游戏并生成对手ID”的逻辑:

use Illuminate\Database\Eloquent\Builder;

class Game extends Model {
    // 原有代码...

    public function scopeForPlayer(Builder $query, int $playerId): Builder
    {
        return $query->where(function (Builder $q) use ($playerId) {
            // 筛选当前玩家参与的所有游戏
            $q->where('home_player_id', $playerId)
              ->orWhere('away_player_id', $playerId);
        })
        // 动态生成opponent_id:如果当前玩家是主场,对手为客场ID;反之则为主场ID
        ->selectRaw("CASE WHEN home_player_id = ? THEN away_player_id ELSE home_player_id END as opponent_id", [$playerId])
        // 按对手ID排序
        ->orderBy('opponent_id');
    }
}

2. 查询并分组

在控制器中调用作用域获取游戏列表,再通过集合的groupBy方法分组:

// 获取当前玩家ID(登录用户用auth()->id(),否则传入指定玩家ID)
$currentPlayerId = auth()->id();

// 查询当前玩家的所有游戏,已按对手ID排序
$games = Game::forPlayer($currentPlayerId)
    ->with('homePlayer', 'awayPlayer') // 预加载关联,避免N+1查询
    ->get();

// 按对手ID分组
$groupedGames = $games->groupBy('opponent_id');

3. 视图展示

在Blade视图中遍历分组后的集合,展示对手信息和对应游戏:

@foreach($groupedGames as $opponentId => $games)
    <h3>对手:{{ $games->first()->opponent->name }}</h3>
    <div class="game-list">
        @foreach($games as $game)
            <div class="game-item">
                比分:{{ $game->score }} | 
                @if($game->home_player_id == auth()->id())
                    你作为主场对阵 {{ $game->awayPlayer->name }}
                @else
                    你作为客场对阵 {{ $game->homePlayer->name }}
                @endif
            </div>
        @endforeach
    </div>
@endforeach

方案二:集合层面动态获取对手(适合小数据量)

如果数据量不大,可以直接查询所有游戏后,在集合层面通过闭包获取对手ID并分组:

1. 查询游戏列表

$currentPlayerId = auth()->id();

$games = Game::where(function ($query) use ($currentPlayerId) {
        $query->where('home_player_id', $currentPlayerId)
              ->orWhere('away_player_id', $currentPlayerId);
    })
    ->with('homePlayer', 'awayPlayer')
    ->get();

2. 集合分组

通过闭包动态判断每个游戏的对手ID,再分组:

$groupedGames = $games->groupBy(function ($game) use ($currentPlayerId) {
    return $game->home_player_id == $currentPlayerId ? $game->away_player_id : $game->home_player_id;
});

视图展示逻辑和方案一一致。


优化:添加对手访问器

在Game模型中添加访问器,快速获取对手模型,简化视图代码:

class Game extends Model {
    // 原有代码...

    // 默认使用登录用户ID,若需指定玩家可修改为接收参数
    public function getOpponentAttribute()
    {
        $playerId = auth()->id();
        return $this->home_player_id == $playerId ? $this->awayPlayer : $this->homePlayer;
    }
}

之后在视图中可直接通过$game->opponent获取对手模型,简化代码:

@foreach($groupedGames as $opponentId => $games)
    <h3>对手:{{ $games->first()->opponent->name }}</h3>
    <div class="game-list">
        @foreach($games as $game)
            <div class="game-item">
                比分:{{ $game->score }} | 你对阵 {{ $game->opponent->name }}
            </div>
        @endforeach
    </div>
@endforeach

内容的提问来源于stack exchange,提问作者gnireeG

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 13:35:28