Можно ли получить два внешних ключа из одной таблицы в другую, чтобы получить два столбца поля? - PullRequest
1 голос
/ 21 апреля 2020

У меня есть две таблицы: один игрок, другой результат. Могу ли я установить два внешних ключа для результата, связанных с игроком, чтобы я мог изменить значение двух игроков с результатом? Я работаю над Ruby веб-приложением, в котором я буду обновлять поля для каждого игрока в соответствии с результатами, которые я собираюсь добавить, которые связаны с футбольной игрой. Например, если счет Player1 3 - 0 Player2, Player1 получит 3 очка, а остальные 0, поэтому в основном из одного результата мне придется изменить два поля из таблицы Player. Я также должен добавить GoalsScored и GoalsTaken для каждого игрока. Могу ли я разобраться с этим, присваивая, например, два имени в результате, я проверю имя в таблице Player и затем изменю поля, используя два внешних ключа, или мне просто нужно правильно настроить методы?

Вот таблицы:

Player
t.string :name
      t.integer :win
      t.integer :draw
      t.integer :lose
      t.integer :gs
      t.integer :gt
      t.integer :dr
      t.integer :points

Result
t.string :name1
      t.integer :goal1
      t.string :name2
      t.integer :goal2
      t.datetime :date
      t.references :player, null: false, foreign_key: true

Можно ли добавить две ссылки на Result?

1 Ответ

1 голос
/ 21 апреля 2020

Да, вы можете. И иногда это хорошая идея, но не здесь. Вы хотите объединить таблицу вместо этого. И вот почему.

Вам нужно использовать два разных имени столбца и сообщить Rails, на какую таблицу они ссылаются.

Result
  t.string :name1
  t.integer :goal1
  t.string :name2
  t.integer :goal2
  t.datetime :date
  t.references :player1,
    null: false,
    foreign_key: { to_table: :players }
  t.references :player2,
    null: false,
    foreign_key: { to_table: :players }

И вам нужно сделать их явными в вашем коде.

class Result < ApplicationRecord
  belongs_to :player1, class_name: 'Player'
  belongs_to :player2, class_name: 'Player'
end

Связать их вместе в Player - маленький обманщик. Наивное, что нужно сделать, это:

class Player < ApplicationRecord
  has_many :player1_results,
    class_name: 'Result',
    foreign_key: :player1_id
  has_many :player2_results,
    class_name: 'Result',
    foreign_key: :player2_id
end

Что если вы хотите получить все результаты игрока? Что делать, если вы хотите, чтобы все игроки в результате? В этом проблема. Вам нужно сделать лишние запросы или добавить дополнительные предложения, такие как where player1_id = :player_id or player2_id = :player_id. Точно так же name1, name2, goal1, goal2.


Если вы хотите сохранить более одной связанной вещи, вам нужна таблица соединений. Даже если это только два. Это делает жизнь намного проще.

У вас есть таблица результатов, но результат чего? Матч! Где хранится информация о матче? В таблице результатов. Это должна быть собственная таблица.

У нас есть три таблицы. Игроки, матчи и стол для хранения результатов, полученных игроком в матче (результат).

create_table :players do
  t.string :name, null: fase
  ...
  t.timestamps
end

create_table :matches do
  t.string :name, null: false
  t.datetime :date, null: false
  ...
  t.timestamps
end

create_table :results do
  t.references :player, foreign_key: true, null: false
  t.references :match, foreign_key: true, null: false
  t.integer :goals
end

Теперь со всеми тремя фигурами мы можем собрать их вместе. Матчи и игроки связаны с до Результаты.

class Matches < ApplicationRecord
  has_many :results
  has_many :players, through: :results
end

class Players < ApplicationRecord
  has_many :results
  has_many :matches, through: :results
end

class Results < ApplicationRecord
  belongs_to :match
  belongs_to :player
end

Теперь, если вы хотите найти игроков в вашем матче ...

players = match.players

Это будет выполнено объединение результатов для вас.

Если вам необходимо отследить, кто является игроком 1 и игроком 2, добавьте это в таблицу результатов с уникальным ограничением.

create_table :results do
  t.references :player, foreign_key: true, null: false
  t.references :match, foreign_key: true, null: false
  t.integer :goals, null: false, default: 0
  t.integer :player_number, null: false, default 1

  # Can't have two player 1s for the same match.
  # :player_number is deliberately first so this index also serves
  # to index player_number.
  t.index [:player_number, :match_id], unique: true
end

Тогда вы можете получить игрока 1 вот так:

player = match.players.find_by!(player_number: 1)

И вы можете добавить несколько удобных методов в отношения.

class Matches < ApplicationRecord
  has_many :results
  has_many :players, through: :results do
    def player(num)
      match.players.find_by!(player_number: num)
    end
  end
end

player = match.players.player(1)
...