Обычный совет - перенести логику в модель и поддерживать контроллер как можно более скудным. Существуют разные подходы к методу фильтрации, первый:
class Record < ActiveRecord::Base
def self.filter(attributes)
attributes.select { |k, v| v.present? }.reduce(all) do |scope, (key, value)|
case key.to_sym
when :id, :zip # direct search
scope.where(key => value)
when :city, :state # regexp search
scope.where(["#{key} ILIKE ?", "%#{value}%"])
when :order # order=field-(ASC|DESC)
attribute, order = value.split("-")
scope.order("#{self.table_name}.#{attribute} #{order}")
else # unknown key (do nothing or raise error, as you prefer to)
scope
end
end
end
end
Второй подход, написать голый filter
, который использует только существующие области:
class Record < ActiveRecord::Base
SUPPORTED_FILTERS = [:id, :city, ...]
scope :id, ->(value) { where(id: value) }
scope :city, ->(value) { where(city: "%#{value}%") }
...
def self.filter(attributes)
attributes.slice(*SUPPORTED_FILTERS).reduce(all) do |scope, (key, value)|
value.present? ? scope.send(key, value) : scope
end
end
end
Для Rails 5, который теперь использует ActionController :: Parameters, синтаксис для метода фильтра:
def self.filter(attributes)
attributes.permit(SUPPORTED_FILTERS).to_hash.reduce(all) do |scope, (key, value)|
value.present? ? scope.send(key, value) : scope
end
end
Модели можно вызывать из любого места в вашем приложении, поэтому их можно повторно использовать и проще тестировать. Теперь контроллер выглядит так просто:
class RecordsController < ApplicationController::Base
respond_to :html, :xml
def index
@records = Record.filter(params)
end
end