Основная проблема с вашим решением заключается в том, что он заставляет каждого из них выполнять в свою очередь. Это означает, что если параметр компании равен nil
, вы получаете пустое отношение, и все операторы where
после этого возвращают пустое отношение. Вы должны условно фильтровать только при условии предоставления параметров.
То, что вы пытаетесь сделать, лучше всего сделать внутри объекта запроса/поиска или фильтрации (запрос принимает параметры и возвращает набор результатов, фильтр принимает уже сгенерированный набор и отфильтровывает его). Мне нравится определять их в app/services/
, хотя некоторые люди помещают их в app/queries
.
Существуют некоторые несоответствия в вашем методе, однако конечная точка индекса должна возвращать список объектов. Даже если вы надеетесь вернуть один вариант, вы должны ожидать, что его может быть несколько. Предположим, мы исправим конечную точку вашего индекса и используем объект фильтра.
# app/controllers/users_controller.rb
def index
users = User.where(1=1)
users = Users::Filter.call(users, params)
render json: users
end
Вы заметите, я использую User.where(1=1)
, это потому, что я использовать для работы с рельсами 3, который возвращает массив вместо отношения, если вы используете User.all
.
# /app/services/users/filter.rb
class Users::Filter
def self.call(resources, options)
new(users, options).filter
end
private
attr_reader :resources, :options
def initialize(resources, options)
@resources = resources
@options = options
end
def filter
if options[:company]
@resources = resources.where(company: options[:company])
end
if options[:position]
@resources = resources.where(position: options[:position])
end
resources
end
end
Причина я использую call
, который затем вызывает частный initialize
и filter
метод так, что этот класс не используется неправильно. Он имеет единственную точку входа и возвращает результат. Это также позволяет мне разбить свои функции фильтрации на небольшие кусочки размера укуса, которые легко изменить.
Для этого примера я выполнил всю фильтрацию в filter
, но вы можете разбить их на методы и даже использовать какое-то умное мета-программирование для вызова имени фильтра, если объект отвечает на него.
EDIT:
Вы можете также рассмотреть вопрос найти драгоценный камень решения, как рыскать [1], который позволяет передавать параметры непосредственно к объекту и искать его. Там есть много разных типов поиска/фильтрации драгоценных камней, чтобы удовлетворить ваши потребности.
[1] https://github.com/activerecord-hackery/ransack