假设我有以下代码:
@sites = Site.find(session[:sites]) # will be an array of Site ids
@languages = Language.for_sites(@sites)
for_sites 是语言模型中的named_scope,它返回与这些网站关联的语言,并且语言与使用 has_many 通过的网站相关联。目标是让@languages拥有与网站关联的不同语言数组。
理想情况下,与其在第二行调用 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
叫:
Site.sites(session[:sites]).languages
或者,如果要恢复语言对象
Site.sites(session[:sites]).languages.collect{|site| site.languages}.flatten
您也可以直接在 Language 对象上执行此操作。我正在使用 :joins,因为 Rails 2.1 拆分和 :include 分为两个查询,这意味着我们不能在 :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
叫:
Language.for_sites(session[:sites])
在这两个例子中,我假设session[:sites]完全由你控制,不受SQL注入的影响。如果没有,请确保您处理清理 ID 的
您的实例变量@sites是数组对象而不是站点,因此我认为named_scope不能使用。你可以打开数组类来实现这个效果(yikes)
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)
希望这能给你一些想法