10
类轨多态性

比方说,我们有这些模型与包括基于类型

class Message 
    belongs_to :messageable, polymorphic: true 
end 

class Ticket 
    has_many :messages, as: :messageable 
    has_many :comments 
end 

class User 
    has_many :messages, as: :messageable 
    has_many :ratings 
end 

class Rating 
    belongs_to :user 
end 

class Comment 
    belongs_to :ticket 
end 

现在我想要加载的所有邮件(已关联ticketsusers),并渴望负载取决于类的类型,无论是commentsticketsratingsusers

当然Message.includes(:messageable).order("created_at desc")将仅包括直接相关联的对象,但问题是如何包括从每个模型推导出不同的关联类型类型(即在这个例子中,如何急于加​​载comments for ticketsratings for users)?

这只是一个简单的例子,但更复杂的情况下,我想为user包含其他关联,以及如果该关联需要更多内容呢?

+0

你找到一个解决办法? – 2013-03-26 08:25:41

+1

我其实是手动做的。获取消息,并为每个消息类型提取与相应包含的关联,然后将它们重新加入消息数组中。这不是很漂亮,但它的工作 – 2013-03-26 08:57:58

+0

分享你的答案? – coderVishal 2016-02-02 12:24:15

回答

2

我能想到的要做到这一点的唯一方法是复制在每个模型的关联与一个共同的名字:

class Ticket 
    has_many :messages, as: :messageable 
    has_many :comments 
    has_many :messageable_includes, class_name: "Comment" 
end 

class User 
    has_many :messages, as: :messageable 
    has_many :ratings 
    has_many :messageable_includes, class_name: "Rating" 
end 

Message.includes(:messageable => :messageable_includes) ... 

我不知道我会用这个策略作为一种普遍的解决方案,但如果这是一个复杂的情况,它可能适用于你。

0

我用下面的辅助方法,在我自己的项目:

def polymorphic_association_includes(association, includes_association_name, includes_by_type) 
    includes_by_type.each_pair do |includes_association_type, includes| 
    polymorphic_association_includes_for_type(association, includes_association_name, includes_association_type, includes) 
    end 
end 

def polymorphic_association_includes_for_type(association, includes_association_name, includes_association_type, includes) 
    id_attr = "#{includes_association_name}_id" 
    type_attr = "#{includes_association_name}_type" 

    items = association.select {|item| item[type_attr] == includes_association_type.to_s } 
    item_ids = items.map {|item| item[id_attr] } 
    items_with_includes = includes_association_type.where(id: item_ids).includes(includes).index_by(&:id) 

    items.each do |parent| 
    parent.send("#{includes_association_name}=", items_with_includes[parent[id_attr]]) 
    end 
end 

这将允许你说:

messages = Message.all 
polymorhpic_association_includes messages, :messageable, { 
    Ticket => :comments, 
    User => :ratings 
} 

不是特别流畅的界面,但它一般的作品。

0

放置包括对每个模型的默认范围:

class Ticket 
    has_many :messages, as: :messageable 
    has_many :comments 
    default_scope -> { includes(:comments).order('id DESC') } 
end 

class User 
    has_many :messages, as: :messageable 
    has_many :ratings 
    default_scope -> { includes(:ratings).order('id DESC') } 
end 

然后,每当你叫Message.all每个多态性协会将包括它自己的资源。

此外,如果你需要调用的类,而不范围只是使用unscoped或创建一个不同的范围:

class Ticket 
    has_many :messages, as: :messageable 
    has_many :comments 
    has_many :watchers 
    default_scope -> { includes(:comments).order('id DESC') } 
    scope :watched -> {includes(:watchers)} 
end 

Ticket.unscoped.all # without comments or watchers (or order) 
Ticket.watched.all # includes watchers only