10

Lassen Sie uns sagen, dass wir diese Modelle habenSchienen polymorphen mit auf Klassentyp basiert umfasst

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 

Jetzt möchte ich alle Nachrichten laden (die tickets oder users zugeordnet haben), und eifrig Last abhängig von der Art der Klasse entweder, comments für tickets und ratings für users

natürlich Message.includes(:messageable).order("created_at desc") enthält nur die direkt zugeordnete Objekt, aber die Frage wäre, wie die verschiedenen Zuordnungstypen umfassen, die von jedem Modell ableiten Typ (d.h. in diesem Beispiel, wie man eifrig lädt comments for tickets und ratings for users)?

Dies ist nur ein einfaches Beispiel, aber was ist mit noch komplizierteren Fällen, wo ich gerne etwas anderes für die user, eine andere Assoziation, und was, wenn diese Assoziation mehr beinhaltet?

+0

Haben Sie gefunden, eine Lösung? –

+1

Ich tat es tatsächlich manuell. Rufen Sie die Nachrichten ab, und ziehen Sie für jeden Nachrichtentyp die Zuordnungen mit den entsprechenden Includes, und fügen Sie sie dann wieder in das Nachrichtenarray ein. Es ist nicht schön, aber es funktioniert –

+0

teilen Sie Ihre Antwort? – coderVishal

Antwort

2

Der einzige Weg, ich denke, dies zu tun kann, ist die Verbände für jedes Modell mit einem gemeinsamen Namen zu duplizieren:

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

Ich bin nicht sicher, ich würde diese Strategie als weit verbreitete Lösung verwenden, aber Wenn dies für Ihren Fall kompliziert ist, kann es für Sie funktionieren.

0

Ich habe die folgenden Hilfsmethoden in meinem eigenen Projekt verwendet:

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 

Dies erlaubt es dir zu sagen:

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

Keine besonders fließend Schnittstelle, aber es funktioniert im Allgemeinen.

0

Legen Sie das umfasst auf einem Standardbereich für jedes Modell:

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 

Dann, wann immer Sie Message.all jede polymorphe Vereinigung nennen sind es eigene Ressourcen ist.

Auch wenn Sie die Klasse ohne den Umfang nennen müssen nur unscoped verwenden oder einen anderen Bereich erstellen:

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