是否有在 RAILS API 控制器索引操作中应用过滤器哈希的 DRY 方法?
Is there a DRY approach to applying a filter hash in a RAILS API controller index action?
根据JSON API 规范,我们应该使用过滤器查询参数来过滤我们在控制器中的记录。过滤器参数实际上是什么并没有真正指定,但由于它应该能够包含多个搜索条件,所以显而易见的事情是使用散列。
问题是,我似乎经常在针对不同类型记录的控制器操作中重复自己。
下面是包含 ID 列表(以获取多个特定记录)的过滤器的情况。
def index
if params[:filter] and params[:filter][:id]
ids = params[:filter][:id].split(",").map(&:to_i)
videos = Video.find(ids)
else
videos = Video.all
end
render json: videos
end
对于嵌套的 属性 检查,我想我可以使用 fetch
或 andand
但它看起来仍然不够干而且我仍然在不同的地方做同样的事情控制器。
有什么方法可以使它看起来更好并且不再重复我自己的话吗?
可以用Rails Concerns来晒...
##================add common in app/models/concerns/common.rb
module Common
extend ActiveSupport::Concern
# included do
##add common scopes /validations
# end
##NOTE:add any instance method outside this module
module ClassMethods
def Find_using_filters (params)
Rails.logger.info "calling class method in concern=======#{params}=="
##Do whatever you want with params now
#you can even use switch case in case there are multiple models
end
end
end
##======================include the concern in model
include Common
##=======================in your controller,call it directly
Image.Find_using_filters params
与其使用关注点在多个地方包含相同的代码,这似乎是服务对象的一个很好的用途。
class CollectionFilter
def initialize(filters={})
@filters = filters
end
def results
model_class.find(ids)
end
def ids
return [] unless @filters[:id]
@filters[:id].split(",").map(&:to_i)
end
def model_class
raise NotImplementedError
end
end
您可以像上面那样编写通用的 CollectionFilter
,然后子类化以针对特定用例添加功能。
class VideoFilter < CollectionFilter
def results
super.where(name: name)
end
def name
@filters[:name]
end
def model_class
Video
end
end
您可以在您的控制器中使用它,如下所示;
def index
videos = VideoFilter.new(params[:filter]).results
render json: videos
end
这是我对此的看法,改编自 Justin Weiss' method:
# app/models/concerns/filterable.rb
module Filterable
extend ActiveSupport::Concern
class_methods do
def filter(params)
return self.all unless params.key? :filter
params[:filter].inject(self) do |query, (attribute, value)|
query.where(attribute.to_sym => value) if value.present?
end
end
end
end
# app/models/user.rb
class User < ActiveRecord::Base
include Filterable
end
# app/controllers/users_controller.rb
class UsersController < ApplicationController
# GET /users
# GET /users?filter[attribute]=value
def index
@users = User.filter(filter_params)
end
private
# Define which attributes can this model be filtered by
def filter_params
params.permit(filter: :username)
end
end
然后您可以通过发出 GET /users?filter[username]=joe
来过滤结果。这也适用于没有过滤器(returns User.all
)或没有值的过滤器(它们被简单地跳过)。
filter
是为了遵守 JSON-API。通过关注模型,您可以保持代码干爽,并且只将其包含在您要过滤的任何模型中。我还使用强参数来针对 "the scary internet".
实施某种保护
当然你可以自定义这个关注点,让它支持数组作为过滤器的值。
根据JSON API 规范,我们应该使用过滤器查询参数来过滤我们在控制器中的记录。过滤器参数实际上是什么并没有真正指定,但由于它应该能够包含多个搜索条件,所以显而易见的事情是使用散列。
问题是,我似乎经常在针对不同类型记录的控制器操作中重复自己。
下面是包含 ID 列表(以获取多个特定记录)的过滤器的情况。
def index
if params[:filter] and params[:filter][:id]
ids = params[:filter][:id].split(",").map(&:to_i)
videos = Video.find(ids)
else
videos = Video.all
end
render json: videos
end
对于嵌套的 属性 检查,我想我可以使用 fetch
或 andand
但它看起来仍然不够干而且我仍然在不同的地方做同样的事情控制器。
有什么方法可以使它看起来更好并且不再重复我自己的话吗?
可以用Rails Concerns来晒...
##================add common in app/models/concerns/common.rb
module Common
extend ActiveSupport::Concern
# included do
##add common scopes /validations
# end
##NOTE:add any instance method outside this module
module ClassMethods
def Find_using_filters (params)
Rails.logger.info "calling class method in concern=======#{params}=="
##Do whatever you want with params now
#you can even use switch case in case there are multiple models
end
end
end
##======================include the concern in model
include Common
##=======================in your controller,call it directly
Image.Find_using_filters params
与其使用关注点在多个地方包含相同的代码,这似乎是服务对象的一个很好的用途。
class CollectionFilter
def initialize(filters={})
@filters = filters
end
def results
model_class.find(ids)
end
def ids
return [] unless @filters[:id]
@filters[:id].split(",").map(&:to_i)
end
def model_class
raise NotImplementedError
end
end
您可以像上面那样编写通用的 CollectionFilter
,然后子类化以针对特定用例添加功能。
class VideoFilter < CollectionFilter
def results
super.where(name: name)
end
def name
@filters[:name]
end
def model_class
Video
end
end
您可以在您的控制器中使用它,如下所示;
def index
videos = VideoFilter.new(params[:filter]).results
render json: videos
end
这是我对此的看法,改编自 Justin Weiss' method:
# app/models/concerns/filterable.rb
module Filterable
extend ActiveSupport::Concern
class_methods do
def filter(params)
return self.all unless params.key? :filter
params[:filter].inject(self) do |query, (attribute, value)|
query.where(attribute.to_sym => value) if value.present?
end
end
end
end
# app/models/user.rb
class User < ActiveRecord::Base
include Filterable
end
# app/controllers/users_controller.rb
class UsersController < ApplicationController
# GET /users
# GET /users?filter[attribute]=value
def index
@users = User.filter(filter_params)
end
private
# Define which attributes can this model be filtered by
def filter_params
params.permit(filter: :username)
end
end
然后您可以通过发出 GET /users?filter[username]=joe
来过滤结果。这也适用于没有过滤器(returns User.all
)或没有值的过滤器(它们被简单地跳过)。
filter
是为了遵守 JSON-API。通过关注模型,您可以保持代码干爽,并且只将其包含在您要过滤的任何模型中。我还使用强参数来针对 "the scary internet".
当然你可以自定义这个关注点,让它支持数组作为过滤器的值。