ActiveRecordオブジェクトの配列が与えられた場合、メソッド呼び出しを通じてそれらの関係を簡単に収集できますか?
-
03-07-2019 - |
質問
次のコードがあるとしましょう:
@sites = Site.find(session[:sites]) # will be an array of Site ids
@languages = Language.for_sites(@sites)
for_sitesは、それらのサイトに関連付けられた言語を返す言語モデルのnamed_scopeであり、言語はhas_many throughを使用してサイトに関連付けられます。目標は、@ languagesがサイトに関連付けられた言語の明確な配列を持つことです。
2行目のLanguageオブジェクトを呼び出す代わりに、理想的には言いたいです
@sites.languages
同じリストが返されます。 Rails 2.1(またはエッジ)でそれをきれいに行う方法はありますか?アソシエーションと名前付きスコープは、配列オブジェクトを拡張して属性を持たせることができることを知っていますが、ここで当てはまらないものがない場合は除きます。これを行うプラグインは歓迎されます。コアにある必要はありません。
解決
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
他のヒント
なぜ両方にnamed_scopesを使用しないのですか
class Site
named_scope :sites, lambda{|ids| :conditions => "id in (#{ids.join(',')})"}
named_scope :languages, :include => :languages ... (whatever your named scope does)
end
call:
Site.sites(session[:sites]).languages
または、言語オブジェクトを戻す場合
Site.sites(session[:sites]).languages.collect{|site| site.languages}.flatten
言語オブジェクトで直接行うこともできます。 Rails 2.1が:includesを2つのクエリに分割し、:conditionsでサイトを使用できないことを意味するため、私は:joinsを使用しています
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
call:
Language.for_sites(session[:sites])
どちらの例でも、session [:sites]は完全に制御され、SQLインジェクションの対象ではないと想定しました。そうでない場合は、IDのクリーンアップに対処してください
インスタンス変数@sitesはSiteではなくArrayオブジェクトなので、named_scopeを使用できるとは思いません。ただし、Arrayクラスを開いてこの効果を得ることができます(いいね)
class Array
def languages
...
end
end
言語をサイトにリンクする has_many
または has_and_belongs_to_many
を追加した場合、インクルードを使用して次のような操作を実行できます。
Site.find( :all, :conditions =>{:id => session[:sites]}, :include => :languages )
名前付きスコープを作成して、:id =>を実行できます。 session [:sites]の例、例:
class Site
named_scope :for_ids, lambda{ |x| {:conditions => {:id => x }
end
してから
Site.for_ids(session[:sites]).find(:all, :include => :languages)
これがあなたにいくつかのアイデアを与えることを願っています