Учитывая массив объектов ActiveRecord, могу ли я легко собрать их взаимосвязи с помощью вызова метода?

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

Вопрос

Допустим, у меня есть следующий код:

@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)

Надеюсь, это натолкнет вас на некоторые идеи

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top