Красноречивая связь Laravel — несколько столбцов таблицы ссылаются на один и тот же внешний ключ

#php #laravel #eloquent #eloquent-relationship

Вопрос:

Ларавель/Красноречивый новичок здесь. Я внедряю простую настольную игру. В каждой игре участвуют 4 игрока. Структура таблиц состоит из таблицы игроков и игрового стола:

 SELECT * FROM players;

id    | name        |
---------------------
1     | John        |
2     | Mary        |
3     | Linda       |
4     | Alex        |
5     | Chris       |
6     | Ron         |
7     | Dave        |
 
 SELECT * FROM games;

id    | player1_id  | player2_id  | player3_id    player4_id  
---------------------------------------------------------------------
1     | 1           | 2           | 3           | 4
2     | 3           | 5           | 6           | 7
3     | 2           | 3           | 5           | 6
4     | 2           | 4           | 5           | 7
 

Цель: Я хочу иметь возможность получить все игры, в которых участвовал игрок.

Для этого я пытаюсь написать функцию games() в модели плеера. Для игрока с идентификатором 2 это должно возвращать игры 1, 3, 4 / для игрока с идентификатором 3 это должно возвращать игры 1, 2, 3 и так далее.

С помощью необработанного SQL я бы сделал что-то вроде этого:

 SELECT * FROM games WHERE 
  (player1_id = 2 OR player2_id = 2 OR player3_id = 2 OR player4_id = 2)
 

Но с Красноречивым мне трудно понять, как нужно выстраивать эти отношения, чтобы достичь этого.

Эквивалентно, я также хотел бы иметь возможность сделать обратное — вернуть всех игроков в игру — с помощью функции players() в Game модели.

Модели:

 // Models/Player.php

//...

class Player extends Model
{
    public function games(){

        //?

    }
}
 
 // Models/Game.php

//...

class Game extends Model
{
    public function players(){

        //?

    }
}
 

Комментарии:

1. Это отношение N к N, вы должны нормализовать.

2. DB::table('games')->where('player1_id', '=', ID )->orwhere('player2_id', '=', 'id')->orwhere('player3_id', '=', 'id')->orwhere('player4_id', '=', 'id')->get();

3. @HassaanAli Нет !!!!!! Используйте отношения, остановитесь DB::something , подумайте, что DB::something их не существует для простых операций !!!!!!!!!

4. Я согласен. Это не конвенция. Но для простоты решение есть. 🙂

Ответ №1:

Без изменения структуры базы данных вы можете неправильно hasMany использовать объявление, чтобы получить всех 4 игроков.

 class Game extends Model
{
    public function players()
    {
        return $this->hasMany(Player::class, 'id', 'player1_id')
                    ->orWhere('id', $this->player2_id)
                    ->orWhere('id', $this->player3_id)
                    ->orWhere('id', $this->player4_id);
    }
}
 
 class Player extends Model
{
    public function games()
    {
        return $this->hasMany(Game::class, 'player1_id', 'id')
                    ->orWhere('player2_id', $this->id)
                    ->orWhere('player3_id', $this->id)
                    ->orWhere('player4_id', $this->id);
    }
}
 

Однако это не идеально.

У вас должна быть третья таблица, чтобы правильно сопоставить это отношение «многие ко многим».

 table 1 - players:     id (pk), name
table 2 - games:       id (pk)
table 3 - game_player: id (pk), game_id (fk), player_id (fk), unique([game_id, player_id])
 
 class Game extends Model
{
    public function players()
    {
        return $this->belongsToMany(Player::class, 'game_player');
    }
}
 
 class Player extends Model
{
    public function games()
    {
        return $this->belongsToMany(Game::class, 'game_player');
    }
}
 

Комментарии:

1. На самом деле это именно то, что я искал. По общему признанию, более подходящим решением (а также лучшим дизайном) действительно была бы нормализация базы данных с помощью сводной таблицы. Однако в интересах простоты дизайна с одной записью за игру я, скорее всего, выберу этот подход. Большое спасибо.