According to the JSON API specification, we should use a filter query parmeter to filter our records in a controller. What the filter parameter actually is isn't really specified, but since it should be able to contain multiple criteria for searching, the obvious thing to do would be to use a hash.
根据JSON API规范,我们应该使用筛选查询解析器来筛选控制器中的记录。实际上,过滤器参数实际上并不是指定的,但是由于它应该能够包含搜索的多个标准,显然要做的事情就是使用散列。
The problem is, it seems like I'm repeating myself quite often in controller actions for different types of records.
问题是,我似乎经常在不同类型的记录的控制器操作中重复我自己。
Here's what things look like for just a filter that includes a list of ids (to get multiple specific records).
以下是一个包含ids列表(获取多个特定记录)的过滤器的情况。
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
For nested property checks, I guess I could use fetch
or andand
but it still doesn't look dry enough and I'm still doing the same thing across different controllers.
对于嵌套属性检查,我想我可以使用fetch or andand但是它看起来还是不够干,我仍然在不同的控制器之间做同样的事情。
Is there a way I could make this look better and not repeat myself that much?
有没有一种方法可以让它看起来更好,而不会重复那么多?
3 个解决方案
#1
2
Rather than using concerns to just include the same code in multiple places, this seems like a good use for a service object.
与使用关注点在多个地方包含相同的代码相比,这似乎是一个很好的服务对象使用。
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
You could write a generic CollectionFilter
as above, then subclass to add functionality for specific use cases.
您可以像上面一样编写一个通用的集合过滤器,然后子类为特定的用例添加功能。
class VideoFilter < CollectionFilter
def results
super.where(name: name)
end
def name
@filters[:name]
end
def model_class
Video
end
end
You would use this in your controller as below;
您将在您的控制器中使用如下所示;
def index
videos = VideoFilter.new(params[:filter]).results
render json: videos
end
#2
1
you can use Rails Concerns to drying up ...
您可以使用Rails关注点来烘干……
##================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
#3
1
Here is my take on this, somewhat adapted from 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
You would then filter the results by issuing a GET /users?filter[username]=joe
. This works with no filters (returns User.all
) or filters that have no value (they are simply skipped) also.
然后,您可以通过发出GET /users?filter(用户名)=joe来过滤结果。这与没有过滤器(返回用户.all)或没有任何值的过滤器(它们只是跳过)一起工作。
The filter
is there to comply with JSON-API. By having a model concern you keep your code DRY and only include it in whatever models you want to filter. I've also used strong params to enforce some kind protection against "the scary internet".
该过滤器是为了遵从JSON-API。通过关注模型,您可以使代码保持干燥,并且只将其包含在您想要筛选的任何模型中。我还使用了强大的params来加强对“恐怖网络”的保护。
Of course you can customize this concern and make it support arrays as values for filters.
当然,您可以定制这个关注点,并使它支持数组作为过滤器的值。
#1
2
Rather than using concerns to just include the same code in multiple places, this seems like a good use for a service object.
与使用关注点在多个地方包含相同的代码相比,这似乎是一个很好的服务对象使用。
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
You could write a generic CollectionFilter
as above, then subclass to add functionality for specific use cases.
您可以像上面一样编写一个通用的集合过滤器,然后子类为特定的用例添加功能。
class VideoFilter < CollectionFilter
def results
super.where(name: name)
end
def name
@filters[:name]
end
def model_class
Video
end
end
You would use this in your controller as below;
您将在您的控制器中使用如下所示;
def index
videos = VideoFilter.new(params[:filter]).results
render json: videos
end
#2
1
you can use Rails Concerns to drying up ...
您可以使用Rails关注点来烘干……
##================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
#3
1
Here is my take on this, somewhat adapted from 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
You would then filter the results by issuing a GET /users?filter[username]=joe
. This works with no filters (returns User.all
) or filters that have no value (they are simply skipped) also.
然后,您可以通过发出GET /users?filter(用户名)=joe来过滤结果。这与没有过滤器(返回用户.all)或没有任何值的过滤器(它们只是跳过)一起工作。
The filter
is there to comply with JSON-API. By having a model concern you keep your code DRY and only include it in whatever models you want to filter. I've also used strong params to enforce some kind protection against "the scary internet".
该过滤器是为了遵从JSON-API。通过关注模型,您可以使代码保持干燥,并且只将其包含在您想要筛选的任何模型中。我还使用了强大的params来加强对“恐怖网络”的保护。
Of course you can customize this concern and make it support arrays as values for filters.
当然,您可以定制这个关注点,并使它支持数组作为过滤器的值。