我有一个搜索/筛选表单,用户可以从中选择一个到多个参数来筛选记录。过滤器可以包括特定的user_id、company_id或project_id等。这些参数的任何组合都可以由.提交给过滤器记录
我要做的是创建尽可能简单的查询,而不需要重新查询子集。
我可以通过在查询中使用更多的逻辑来实现这一点。。。但是,似乎应该有一条铁路。
Thing.where( params[:user_id].present? ? user_id: params[:user_id] : "user_id IS NOT NULL" ).
where( params[:company_id].present? ? company_id: params[:company_id] : "company_id IS NOT NULL" )
我追求的是更干净的东西。。。类似:
Thing.where( user_id: params.fetch(:user_id, '*') )
然后,我可以像这样链接所有可用的搜索参数:
Thing.where(
user_id: params.fetch(:user_id, '*'),
company_id: params.fetch(:company_id, '*'),
project_id: params.fetch(:project_id, '*')
)
另一种方法
Things.在哪里(''(将返回所有Things。所以,我可以做一些类似的事情:
Thing.where( params[:user_id].present? ? { user_id: params[:user_id] } : '' )
但是,这似乎不像轨道。
有办法做到这一点吗?谢谢
只需创建一个以参数为输入并创建范围的模型:
class ThingFilter
include ActiveModel::Model
include ActiveModel::Attributes
attribute :user_id
attribute :company_id
attribute :project_id
def resolve(scope = Thing.all)
attributes.inject(scope) do |filtered, (attr_name, value)|
if !value.present?
scope.merge(Thing.where.not(attr_name => nil))
else
scope.merge(Thing.where(attr_name => value))
end
end
end
end
class Thing < ApplicationRecord
def self.filter(**kwargs)
ThingFilter.new(**kwargs).resolve(self.where)
end
end
@things = Thing.filter(
params.permit(:user_id, :company_id, :project_id)
)
这非常容易测试,如果需要,可以添加验证等功能,而不会弄乱控制器。您也可以将其绑定到表单。
在这种情况下可以使用一个名为#merge
的方法。
@things = Thing.all
@things = @things.merge(-> { where(user_id: params[:user_id]) }) if params[:user_id].present?
@things = @things.merge(-> { where(company_id: params[:company_id]) }) if params[:company_id].present?
@things = @things.merge(-> { where(project_id: params[:project_id]) }) if params[:project_id].present?
虽然这不是最简洁的方法,但在我看来,它可读性很强。
找到了一种简洁的方法,但要根据您对可读性的看法使用它。
@things =
params.slice(:user_id, :company_id, :project_id).
reduce(Thing.all) do |relation, (column, value)|
relation.where(column => value)
end
感觉您可能正在对Ransack gem已经提供的内容进行编码。
它接受搜索参数,如user_id_eq
和company_name_present
,以及排序参数,并将它们转换为所需的SQL。
https://github.com/activerecord-hackery/ransack