Учитывая массив объектов 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 имел отдельный набор языков, связанных с сайтами.
Вместо вызова Языкового объекта во второй строке, я бы в идеале хотел сказать
@sites.languages
и пусть мне вернут тот же список.Есть ли какой-нибудь способ сделать это чисто в Rails 2.1 (или edge)?Я знаю, что ассоциации и именованные области могут расширять объект массива, чтобы иметь атрибуты, но если я не упускаю чего-то, что здесь неприменимо.Любые плагины, которые делают это, приветствовались бы, это не обязательно должно быть в ядре.
Решение
Вы могли бы расширить массив, возвращаемый 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
Вы также можете сделать это непосредственно на Языковом объекте.Я использую:joins, потому что Rails 2.1 разбивает и:include на два запроса, что означает, что мы не можем использовать сайты в :условиях
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-инъекции.Если нет, убедитесь, что вы справились с очисткой идентификаторов
Ваша переменная экземпляра @sites - это объект массива, а не Site, поэтому я не думаю, что named_scope можно использовать.Однако вы можете открыть класс Array для достижения этого эффекта (yikes)
class Array
def languages
...
end
end
Если вы добавили has_many
или has_and_belongs_to_many
связывая языки с сайтами, вы могли бы использовать include и сделать что-то вроде этого:
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)
Надеюсь, это натолкнет вас на некоторые идеи