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
相关产品推荐
相关产品推荐

