Dato un array di oggetti ActiveRecord, posso facilmente raccogliere le loro relazioni tramite una chiamata di metodo?
-
03-07-2019 - |
Domanda
Diciamo che ho il seguente codice:
@sites = Site.find(session[:sites]) # will be an array of Site ids
@languages = Language.for_sites(@sites)
for_sites è un nome_scope nel modello Lingua che restituisce le lingue associate a quei siti e le lingue sono associate ai siti che usano has_many attraverso. L'obiettivo è che @languages ??abbia una matrice distinta delle lingue associate ai siti.
Invece di chiamare l'oggetto Language sulla seconda riga, idealmente vorrei dire
@sites.languages
e mi viene restituito lo stesso elenco. C'è un modo per farlo in modo pulito in Rails 2.1 (o edge)? So che le associazioni e gli ambiti denominati possono estendere l'oggetto array per avere attributi, ma a meno che non manchi qualcosa che non si applica qui. Qualsiasi plugin che lo faccia sarebbe il benvenuto, non deve essere nel core.
Soluzione
È possibile estendere l'array restituito da Site.find.
class Site
def find(*args)
result = super
result.extend LanguageAggregator if Array === result
result
end
end
module LanguageAggregator
def languages
Language.find(:all, :conditions => [ 'id in (?)', self.collect { |site| site.id } ])
end
end
Altri suggerimenti
Perché non usare named_scopes per entrambi?
class Site
named_scope :sites, lambda{|ids| :conditions => "id in (#{ids.join(',')})"}
named_scope :languages, :include => :languages ... (whatever your named scope does)
end
chiamata:
Site.sites(session[:sites]).languages
oppure, se si desidera ripristinare gli oggetti linguaggio
Site.sites(session[:sites]).languages.collect{|site| site.languages}.flatten
Puoi anche farlo direttamente sull'oggetto Lingua. Sto usando: si unisce perché Rails 2.1 si divide e include in due query, il che significa che non possiamo usare i siti nelle condizioni:
class Language
named_scope :for_sites, lambda{|site_ids| :joins => 'inner join sites on languages.site_id = sites.id' :conditions => "sites.id in (#{site_ids.join(',')})"}
end
chiamata:
Language.for_sites(session[:sites])
In entrambi gli esempi ho ipotizzato che la sessione [: siti] sia completamente controllata da te e non soggetta all'iniezione SQL. In caso contrario, assicurati di occuparti della pulizia dell'ID
La variabile di istanza @sites è un oggetto Array e non Site, quindi non credo che si possa usare named_scope. Puoi aprire la classe Array per ottenere questo effetto (yikes)
class Array
def languages
...
end
end
Se hai aggiunto un has_many
o has_and_belongs_to_many
che collega le lingue ai siti, puoi utilizzare un include e fare qualcosa del genere:
Site.find( :all, :conditions =>{:id => session[:sites]}, :include => :languages )
Puoi creare un ambito denominato per eseguire: id = > sessione [: siti], ad esempio:
class Site
named_scope :for_ids, lambda{ |x| {:conditions => {:id => x }
end
e poi
Site.for_ids(session[:sites]).find(:all, :include => :languages)
Spero che questo ti dia alcune idee