簡體   English   中英

如何在 Hanami 中重構查詢鏈?

[英]How to refactor queries chain in Hanami?

如何重構#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

沿着這些路線的一些東西可能會起作用,但不確定鏈接這些會如何影響性能或結果,但您可以嘗試它,它可能會引導您找到您想要的答案

更新

如果你真的想要鏈接,這接近你想要的。

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})

引入查詢對象:

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)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM