如何构建我的位置模型和我的播放器模型之间的关系?

时间:2016-09-14 05:51:52

标签: ruby-on-rails ruby-on-rails-5

我有一个Player模型,has_many :positions。把球员想象成一个体育运动的人,他们可以在球场上打多个位置(即前锋,左翼,防守型中场)。

从DB的角度来看,我无法弄清楚如何对此进行建模。

最初我的Position.rb看起来像这样:

# == Schema Information
#
# Table name: positions
#
#  id            :integer          not null, primary key
#  position_type :integer
#  player_id    :integer
#  created_at    :datetime         not null
#  updated_at    :datetime         not null
#

class Position < ActiveRecord::Base
  belongs_to :player

  enum position_type: { goalkeeper: 0, center_back: 1, left_back: 2, right_back: 3, left_wing_back: 4, right_wing_back: 5, defending_midfielder: 6, central_midfielder: 7, attacking_midfielder: 8, left_midfield: 9, right_midfield: 10, left_wing: 11, right_wing: 12, center_forward: 13 }
end

但这感觉不对。

我应该做一些像常规Position模型那样的事情,然后只为每个职位创建唯一的记录,然后在两者之间建立HABTM关系吗?

这是HABTM合适的一种情况吗?我有一段时间没有使用这种关系。

3 个答案:

答案 0 :(得分:1)

您可以使用Rolify gem ,这有助于管理多个角色,因为相同型号的人称为播放器。定义角色并与玩家相关联。参考官方文档。

如果有一些限制,比如特定玩家只能在2或3个位置玩。 您可以使用Cancan来处理授权。

如果您不想使用宝石,请继续为位置和玩家关联创建新模型。类似于下面的代码,可以在同一个地方进行进一步的优化。

  class Position < ActiveRecord::Base
    #Define a table with the values present in enum which should be mostly one time operation.
  end

  class Player < ActiveRecord::Base
    has_many :player_positions, :dependent => :destroy
    has_many :positions, :through => :player_positions
  end

  class PlayerPosition < ActiveRecord::Base
    belongs_to :player
    belongs_to :position
    #this table will have player_id and position_id as attributes
    #Also have a active_position_id since one player can have only one position at a certain time when the game is played.
  end

您将结束让一名玩家在玩家位置表中有多个与位置相关的条目。为了管理玩家的实时位置,有一个active_position_id,因为一个玩家在玩游戏的某个时间只能有一个位置

答案 1 :(得分:1)

您可以使用多对多联接表:

class Player < ActiveRecord::Base
  has_many :player_postitions
  has_many :postitions, through: :player_postitions
end

class PlayerPostition < ActiveRecord::Base
  belongs_to :player
  belongs_to :position
end

class Position < ActiveRecord::Base
  enum position_type: { goalkeeper: 0, center_back: 1, left_back: 2, right_back: 3, left_wing_back: 4, right_wing_back: 5, defending_midfielder: 6, central_midfielder: 7, attacking_midfielder: 8, left_midfield: 9, right_midfield: 10, left_wing: 11, right_wing: 12, center_forward: 13 }
  has_many :player_postitions
  has_many :players, through: :player_postitions
end

我们将has_manythrough选项一起使用,而不是has_and_belongs_to_many

has_manyhas_and_belongs_to_many之间的核心差异在于,后者是直接的,没有介入模型。这是非常有限的,因为您无法直接查询连接表或将元数据附加到关系。例如,您无法创建三向连接:

class Player < ActiveRecord::Base
  has_many :player_postitions
  has_many :postitions, through: :player_postitions
  has_many :games, through: :player_postitions
end

class PlayerPostition < ActiveRecord::Base
  belongs_to :player
  belongs_to :position
  belongs_to :game
end

class Game < ActiveRecord::Base
  has_many :player_postitions
  has_many :players, through: :player_postitions
  has_many :positions, through: :player_postitions
end

另一个重要区别是连接表的命名。对于HABTM,您可以为players_postions命名联接表player_postionshas_many through:,因为ActiveRecord基于关系名称进行常量查找。除非您指定类名,否则has_many :players_postitions会导致AR查找Players::Postitions

答案 2 :(得分:1)

你已经有了两个很好的答案,但我会添加一个替代方案。如果您不需要Position模型中的任何代码,则以下方法可以正常工作。在这种情况下,你根本不需要这个模型。

您可以在数据库中添加额外的positions列来存储每个玩家的位置。根据您使用的数据库服务器,您可以使用本机数组列类型(和索引),还可以编写专门的查询。例如,PostgreSQL支持array columns并具有特殊的array functions,允许您查找所需的行。我不确定其他数据库服务器是否支持这些功能。

使用pg数据库适配器时,

Rails支持array columns。以下是创建玩家记录的方法:

Player.create(name: 'John Doe', positions: %w(center_back left_back))

以下是您如何找到可以成为中后卫(以及任何其他位置)的球员:

Player.where("'center_back' = ANY(positions)")

如果您想进一步了解,还可以向Player模型添加范围,以隐藏所需的SQL函数:

class Player < ActiveRecord::Base
  scope :for_position, -> (position) { where("'#{position}' = ANY(positions)") }
end

Player.for_position('center_back')