Вызов метода класса ActiveRecord для ActiveRecord_Relation в качестве получателя

Я хочу создать class method для класса наследует ActiveRecord:Base, Что метод должен сделать, это добавить предложения where, основанные на опциях, и он работает хорошо.

class Article < ActiveRecord::Base

  def self.list_by_params(params={})
    articles = self
    articles = articles.where(author_id: params[:author_id]) unless params[:author_id].blank?
    articles = articles.where(category_id: params[:category_id]) unless params[:category_id].blank?
    articles = articles.where("created_at > ?", params[:created_at].to_date) unless params[:created_at].blank?
    articles
  end

end

Этот код прекрасно работает в случае вызова, такого как:

articles = Article.list_by_params({author_id: 700})
#=> Works fine as I expected.

articles = Article.joins(:authors).list_by_params({author_id: 700})
#=> Works fine as I expected.

Однако проблема в том, что если я хочу позвонить list_by_params без фильтрации параметров он теряет прежние отношения. Например:

articles = Article.joins(:authors).list_by_params({})
#=> articles is just a `Article` (not an ActiveRecord_Relation) class itself without joining :authors.

Есть ли шанс, что я ошибся?

Заранее спасибо.

2 ответа

То, что вы ищете, это сфера.

Я бы сделал что-то подобное

scope :for_author,  lambda { |author| where(author_id: author) unless author.blank? }
scope :in_category, lambda { |category| where(category_id: category) unless category.blank? }
scope :created_after,  lambda { |date| where('created_at > ?', date.to_date) unless date.blank? }

scope :list_by_params, lambda do |params|
  for_author(params[:author_id])
  .in_category(params[:category_id])
  .created_after(params[:created_at])
end

Теперь вы можете повторно использовать компоненты вашего запроса. У всего есть имена, и читать код легче.

Для самостоятельного объяснения я решил проблемы с помощью where(nil),

На самом деле, Model.scoped возвратил анонимную область видимости, но этот метод устарел начиная с Rails версии 4. Теперь where(nil) может заменить функциональность.

class Article < ActiveRecord::Base

  def self.list_by_params(params={})
    articles = where(nil)  # <-- HERE IS THE PART THAT I CHANGED.
    articles = articles.where(author_id: params[:author_id]) unless params[:author_id].blank?
    articles = articles.where(category_id: params[:category_id]) unless params[:category_id].blank?
    articles = articles.where("created_at > ?", params[:created_at].to_date) unless params[:created_at].blank?
    articles
  end

end
Другие вопросы по тегам