如何重构#filtered
方法?
在Hanami中,无法以ActiveRecord样式进行一系列查询(过滤器)。我想要一种类似ActiveRecord过滤器的方法。
现在:documents.filtered(genre: 'news', min_published_at: from, max_published_at: to, skip: 30)
我想要的是:documents.with_genre('news').published_between(from, to).skip(30)
class DocumentRepository < Hanami::Repository
GENRES = DbSchema.current_schema.enum(:document_genre).values.map(&:to_s)
DOCUMENTS_PER_PAGE = 30
associations do
has_many :boxes
has_many :urls
end
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/AbcSize
def filtered(params = {})
result = ordered.limit(DOCUMENTS_PER_PAGE)
result = result.where(genre: params[:genre]) if params.key?(:genre)
if params.key?(:min_created_at) && params.key?(:max_created_at)
date_range = params[:min_created_at]..params[:max_created_at]
result = result.where(created_at: date_range)
end
if params.key?(:min_published_at) && params.key?(:max_published_at)
date_range = params[:min_published_at]..params[:max_published_at]
result = result.where(published_at: date_range)
end
result = result.offset(params[:skip]) if params.key?(:skip)
result
end
# rubocop:enable Metrics/CyclomaticComplexity
# rubocop:enable Metrics/AbcSize
def ordered
documents.order { created_at.desc }
end
end
答案 0 :(得分:1)
按照这些方法行事可能会奏效,但不确定如何将它们链接在一起会严重影响性能或结果,但是您可以尝试一下,它可能会带您找到想要的答案
如果您真的想要链接,这很接近您想要的。
class DocumentRepository < Hanami::Repository
GENRES = DbSchema.current_schema.enum(:document_genre).values.map(&:to_s)
DOCUMENTS_PER_PAGE = 30
associations do
has_many :boxes
has_many :urls
end
attr_accessor :data
def initialize
@data = []
super
end
def data
@data.flatten!.uniq!
end
def with_genre(key)
@data << documents.where(genre: key)
self
end
def published_between(arr)
from, to = arr
@data << documents.where(created_at: [from..to])
self
end
def skip(num)
@data << documents.offset(num)
self
end
end
像这样调用它,假设它是DocumentRepository
的实例变量
document_repository.with_genre('news')
.published_between([from, to])
.skip(30)
.data
通过在每个实例方法中返回self
,您可以在实例上链接调用。
这种方法有效,但在当前通话中使用类似的语法。
class DocumentRepository < Hanami::Repository
GENRES = DbSchema.current_schema.enum(:document_genre).values.map(&:to_s)
DOCUMENTS_PER_PAGE = 30
associations do
has_many :boxes
has_many :urls
end
def hack_where(opts={})
data = []
opts.each do |i|
data << self.send(i[0],i[1]).call
end
data.flatten!.uniq!
end
def with_genre(key)
lambda { |key| documents.where(genre: key) }
end
def published_between(arr)
from = arr[0]
to = arr[1]
lambda { |from, to| documents.where(created_at: [from..to]) }
end
def skip(num)
lambda { documents.offset(num) }
end
end
您可以这样称呼它:
hack_where({with_genre: 'news', published_between: [from,to], skip: 30})
答案 1 :(得分:0)
引入查询对象:
class FilterDocuments
DOCUMENTS_PER_PAGE = 30
def initialize(documents)
@documents = documents
end
def filter(params = {})
result = apply_ordering(documents)
result = apply_limit_and_offset(result, params)
result = filter_by_genre(result, params)
result = filter_by_created_at(result, params)
result = filter_by_published_at(result, params)
result
end
private
attr_reader :documents
def apply_ordering(documents)
documents.order { created_at.desc }
end
def apply_limit_and_offset(documents, params)
if params.key?(:skip)
documents.offset(params[:skip])
else
documents
end.limit(DOCUMENTS_PER_PAGE)
end
def filter_by_genre(documents, params)
if params.key?(:genre)
documents.where(genre: params[:genre])
else
documents
end
end
def filter_by_created_at(documents, params)
if params.key?(:min_created_at) && params.key?(:max_created_at)
range = params[:min_created_at]..params[:max_created_at]
documents.where(created_at: range)
else
documents
end
end
def filter_by_published_at(documents, params)
if params.key?(:min_published_at) && params.key?(:max_published_at)
range = params[:min_published_at]..params[:max_published_at]
documents.where(published_at: range)
else
documents
end
end
end
使用方法:
def query
FilterDocuments.new(DocumentRepository.new.documents)
end
filtered_documents = query.filter(params)