[英]How to refactor queries chain in Hanami?
How to refactor #filtered
method?如何重构#filtered
方法?
In Hanami there is no way to make a chain of queries (filters) in ActiveRecord-style.在 Hanami 中,无法以 ActiveRecord 样式进行查询链(过滤器)。 I would like to get a methods like ActiveRecord filters.我想获得像 ActiveRecord 过滤器这样的方法。
Now: documents.filtered(genre: 'news', min_published_at: from, max_published_at: to, skip: 30)
现在: documents.filtered(genre: 'news', min_published_at: from, max_published_at: to, skip: 30)
What I want: documents.with_genre('news').published_between(from, 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
Something along these lines might work, but not sure how chaining these will poorly effect performance or results, but you can try it and it may lead you to the answer you want沿着这些路线的一些东西可能会起作用,但不确定链接这些会如何影响性能或结果,但您可以尝试它,它可能会引导您找到您想要的答案
If you really want chaining this is close to what you want.如果你真的想要链接,这接近你想要的。
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
Call it like this assuming this is an instance variable of DocumentRepository
假设这是DocumentRepository
的实例变量,这样调用它
document_repository.with_genre('news')
.published_between([from, to])
.skip(30)
.data
By returning self
in each instance method you're able to chain the calls on the instance.通过在每个实例方法中返回self
,您可以链接实例上的调用。
This way works but uses similar syntax in your current call.这种方式有效,但在您当前的调用中使用类似的语法。
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
You can call it like:你可以这样称呼它:
hack_where({with_genre: 'news', published_between: [from,to], skip: 30})
Introduce query object:引入查询对象:
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
How to use:如何使用:
def query
FilterDocuments.new(DocumentRepository.new.documents)
end
filtered_documents = query.filter(params)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.