我使用Rails 5 API构建了一个用于检索数据的应用。我想使用多个GET参数来提取数据。
示例GET请求格式
/users?company=Samsung&position=Engineer
单个参数的控制器代码: -
def index
client = User.where("company = ?", params[:company])
render json: client
end
多个参数的控制器代码: -
def index
client = User.where("company = ?", params[:company]).where("position = ?", params[:position])
render json: client
end
同样,GET请求中可能包含/可能不包含许多参数。
我想要什么
如果用户未指定任何参数,则应向他显示所有用户。 如果只指定了几个参数,则只应搜索那些参数。
发生了什么
如果用户在GET请求中仅指定位置参数,则Rails无法处理它&显示一个空白的json,因为没有为它指定规则。
请注意,有许多参数,并且无法为每个参数组合编写规则。
答案 0 :(得分:2)
您的解决方案的基本问题是它强制每个地方依次执行。这意味着如果company参数为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)
,这是因为我用来处理rails 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
中完成了所有过滤,但你可以将它们分解为方法,甚至可以使用一些聪明的元编程来调用过滤器名称,如果对象响应它。
编辑:
您也可以考虑寻找像ransack [1]这样的宝石解决方案,它允许您将参数直接传递给对象并进行搜索。有许多不同类型的搜索/过滤宝石可以满足您的需求。