问题描述
|
我不小心注意到我的两个模型有些相似之处。它们的名称是GameItem和OwnedItem。 GameItem只是游戏中的一个项目,而OwnedItem则表示玩家是否拥有该项目,是否在他/她的库存或仓库中等等。我的模型现在就像(为简单起见,我删除了验证和一些无关的代码):
class OwnedItem < ActiveRecord::Base
belongs_to :user
belongs_to :game_item
belongs_to :ownable,:polymorphic => true # [warehouse|inventory]
scope :equipped,where(:is_equipped => 1).includes(:game_item)
scope :item,lambda { |item_type|
joins(:game_item).
where(\"game_items.item_type = ?\",item_type ).
limit(1)
}
scope :inventory,where(:ownable_type => \'Inventory\')
scope :warehouse,where(:ownable_type => \'Warehouse\')
end
class GameItem < ActiveRecord::Base
scope :can_be_sold,where(:is_sold => 1)
scope :item_type,lambda { |item_type|
where(\"game_items.item_type = ?\",item_type )
}
scope :item_types,lambda { |item_types|
where(\"game_items.item_type IN (?)\",item_types )
}
scope :class_type,lambda { |class_type|
where(\"game_items.class_type = ?\",class_type )
}
scope :grade,lambda { |grade|
where(\"game_items.grade = ?\",grade )
}
end
请注意game_item.item_type的问题。我在owner_item模型中引用了它,从而破坏了封装并重复了我自己。我实际上如何能够做类似的事情:
user.inventory.owned_items.item_type(\'Weapon\').equipped
就是说,实际上没有在OwnedItem模型中添加重复的代码,而是从GameItem模型中获取了这些信息?
解决方法
我认为您在此处定义的关系会给您带来麻烦。您可能会发现最好使用一个简单的用户进行项目加入模型,如下所示:
class User < ActiveRecord::Base
has_many :owned_items
has_many :game_items,:through => :owned_items
end
class OwnedItem < ActiveRecord::Base
belongs_to :user
belongs_to :game_item
# Has \'location\' field: \'warehouse\' or \'inventory\'
end
class GameItem < ActiveRecord::Base
has_many :owned_items
has_many :users,:through => :owned_items
end
这是一种常见的模式,您有用户以及他们将拥有其实例的某种东西。中间的关系表OwnedItem用于建立GameItem特定实例的任何唯一特征以及它相对于用户的位置。
通常,这种结构避免使用多态关联,如果太随意使用,可能会造成麻烦。尽可能避免多态关联,除非它们处于您关系的最边缘。将它们放在中间会使连接复杂化,并使索引更难调优。
作为原始文档的注释,您可以将其汇总为一个简单的范围,该范围使用用于where
的哈希方法:
scope :with_item_type,lambda { |types|
where(\'game_items.item_type\' => types)
}
这将采用数组或字符串参数,并相应地使用IN
或=
。用这种方法实际上非常方便,因为您无需记住要使用哪个。