Étant donné un tableau d'objets ActiveRecord, puis-je facilement collecter leurs relations via un appel de méthode?

StackOverflow https://stackoverflow.com/questions/220126

Question

Disons que j'ai le code suivant:

@sites = Site.find(session[:sites]) # will be an array of Site ids
@languages = Language.for_sites(@sites)

for_sites est un named_scope dans le modèle de langage qui renvoie les langues associées à ces sites, et les langues sont associées aux sites à l'aide de has_many. L’objectif est que @languages ??ait un tableau distinct des langues associées aux sites.

Au lieu d'appeler l'objet Language sur la deuxième ligne, j'aimerais idéalement dire

@sites.languages

et que la même liste me soit renvoyée. Existe-t-il un moyen de le faire proprement dans Rails 2.1 (ou edge)? Je sais que les associations et les portées nommées peuvent étendre l’objet tableau à des attributs, mais à moins que je manque quelque chose qui ne s’applique pas ici. Tous les plugins faisant cela seraient les bienvenus, il n’a pas besoin d’être dans le noyau.

Était-ce utile?

La solution

Vous pouvez étendre le tableau renvoyé par 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

Autres conseils

Pourquoi ne pas utiliser named_scopes pour les deux?

class Site
  named_scope :sites, lambda{|ids| :conditions => "id in (#{ids.join(',')})"}
  named_scope :languages, :include => :languages ... (whatever your named scope does)
end

appeler:

Site.sites(session[:sites]).languages

ou, si vous souhaitez récupérer des objets langage

Site.sites(session[:sites]).languages.collect{|site| site.languages}.flatten

Vous pouvez également le faire directement sur l'objet Langage. J'utilise: join car Rails 2.1 est scindé en deux requêtes, ce qui signifie que nous ne pouvons pas utiliser de sites dans les: conditions

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

appeler:

Language.for_sites(session[:sites])

Dans les deux exemples, j'ai supposé que la session [: sites] est entièrement contrôlée par vous et n'est pas sujette à une injection SQL. Sinon, assurez-vous de nettoyer les identifiants

Votre variable d'instance @sites est un objet Array et non un site. Je ne pense donc pas que named_scope puisse être utilisé. Vous pouvez cependant ouvrir la classe Array pour obtenir cet effet (bravo)

class Array

  def languages
    ...
  end

end

Si vous avez ajouté un has_many ou un has_and_belongs_to_many reliant des langues à des sites, vous pouvez utiliser une inclusion et procéder de la manière suivante:

Site.find( :all, :conditions =>{:id => session[:sites]}, :include => :languages )

Vous pouvez créer une portée nommée pour: id = > session [: sites] chose, par exemple:

class Site
  named_scope :for_ids, lambda{ |x| {:conditions => {:id => x }
end

et ensuite

Site.for_ids(session[:sites]).find(:all, :include => :languages)

J'espère que cela vous donne quelques idées

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top