2016-09-14 91 views
0

我有一个Player模型,谁has_many :positions。把玩家想象成有人在运动,他们可以在场上打多个位置(即前锋,左翼,防守中场)。如何构建我的Position模型和我的Player模型之间的关系?

我不能完全弄清楚如何从数据库的角度对此进行建模。

起初我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适合的情况吗?我有一段时间没有使用过这种关系。

回答

1

您可以使用Rolify宝石这有助于管理多个角色为同一型号说球员。定义角色并与玩家相关联。请参阅官方文档。

如果有特定玩家的限制,只能在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

您可以使用一个多对多的连接表:

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,由于ActiveRecord基于关系名称进行常量查找的方式,因此您将为has_many through:命名连接表players_postionsplayer_postions。除非您指定类名称,否则has_many :players_postitions会导致AR查找Players::Postitions

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')