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