Dado um conjunto de ActiveRecord objetos, eu posso facilmente recolher as suas relações através de uma chamada de método?
-
03-07-2019 - |
Pergunta
Vamos dizer que eu tenho o seguinte código:
@sites = Site.find(session[:sites]) # will be an array of Site ids
@languages = Language.for_sites(@sites)
for_sites é um named_scope no modelo de linguagem que retorna as línguas associados a esses locais e idiomas estão associados a sites usando has_many através. O objetivo é que @languages ??ter uma matriz distinta das línguas associados com os sites.
Em vez de chamar o objeto Idioma na segunda linha, eu idealmente gostaria de dizer
@sites.languages
e ter a mesma lista voltou para mim. Existe alguma maneira de fazer isso de forma limpa no Rails 2.1 (ou EDGE)? Eu sei associações e escopos nomeados pode estender o objeto de matriz para ter atributos, mas a menos que eu estou faltando alguma coisa que não se aplica aqui. Quaisquer plugins que fazem isso seria bem-vinda, ele não tem que estar no núcleo.
Solução
Você poderia estender a matriz retornada por 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
Outras dicas
Por que não usar named_scopes para ambos?
class Site
named_scope :sites, lambda{|ids| :conditions => "id in (#{ids.join(',')})"}
named_scope :languages, :include => :languages ... (whatever your named scope does)
end
chamada:
Site.sites(session[:sites]).languages
ou, se você quiser linguagem objetos de volta ??p>
Site.sites(session[:sites]).languages.collect{|site| site.languages}.flatten
Você também pode fazê-lo diretamente no objeto Idioma. Eu estou usando: junta porque Rails 2.1 divide e: incluem em duas consultas que significa que não pode usar sites nos: condições
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
chamada:
Language.for_sites(session[:sites])
Em ambos os exemplos que eu assumir que sessão [:] locais é totalmente controlado por você e não sujeito a injeção de SQL. Se não, certifique-se de lidar com a limpeza do ID
Seus @sites variável da instância é um objeto Array e não do site, então eu não acho que named_scope pode ser usado. Você pode abrir classe Array para conseguir este efeito embora (yikes)
class Array
def languages
...
end
end
Se você adicionou um has_many
ou has_and_belongs_to_many
ligando idiomas em sites, em seguida, você poderia usar um incluem e fazer algo como isto:
Site.find( :all, :conditions =>{:id => session[:sites]}, :include => :languages )
Você pode fazer um escopo nomeado para fazer o: id => session [: Locais] coisa, por exemplo:
class Site
named_scope :for_ids, lambda{ |x| {:conditions => {:id => x }
end
e, em seguida, fazer
Site.for_ids(session[:sites]).find(:all, :include => :languages)
Espero que isso lhe dá algumas idéias