Rails& ActiveRecord - 重构多个相同的where子句

时间:2017-12-13 09:13:23

标签: mysql ruby-on-rails activerecord

我在Rails 5上。

我必须处理一个非常复杂的查询,其中包含多个相同的where子句:

::AllocatedBudget.joins(:account_code, :budget, account_code: [:place],
                          budget: [:fiscal_year, :budget_state])
                    .where(immeuble:            { id: place.id })
                    .where(situation_budget:    { codesituation: ['A', 'V']})
                    .where(plan_comptable:      { supprime: 'false' })
                    .where(budget:              { supprime: 'false'})
                    .where(situation_budget:    { supprime: 'false' })
                    .where(budget_previsionnel: { supprime: 'false' })
                    .where(exercice_comptable:  { supprime: 'false' })

首先,您必须知道我的模型已连接到具有丑陋名称的旧数据库。我注意到ActiveRecord需要自定义名称而不是模型的名称来执行查询。我不知道为什么,但它只是那样工作......如果有人能解释它会很好;)

我真正的问题是:我能以更好的方式写出来吗?有很多时间相同的where子句“supprime ='false'”。

非常感谢! :)

2 个答案:

答案 0 :(得分:1)

我会将问题分解为两个步骤

  1. 使用纯ruby代码
  2. 创建所有条件的哈希值
  3. 将整个哈希值传递给where
  4. 所以第一步是这样的。

    same_conditions_list = [
      :plan_comptable, 
      :budget, 
      :situation_budget, 
      :budget_previsionnel, 
      :exercice_comptable
    ]
    
    same_conditions_key_values = same_conditions_list.inject({}) do |conditions, condition|
      conditions[condition] = { supprime: 'false' }
      conditions
    end   
    
    same_conditions = Hash[same_conditions_key_values]      
    
    all_conditions = same_conditions.merge({
      immeuble: { id: "place.id" }
    })       
    

    之后all_conditions将等于此

    {
      :plan_comptable=>{:supprime=>"false"},
      :budget=>{:supprime=>"false"},
      :situation_budget=>{:supprime=>"false"},
      :budget_previsionnel=>{:supprime=>"false"},
      :exercice_comptable=>{:supprime=>"false"},
      :immeuble=>{:id=>"place.id"}
    }
    

    然后第二步就是

    ::AllocatedBudget.joins(:account_code, :budget, account_code: [:place],
                          budget: [:fiscal_year, :budget_state])
                      .where(all_conditions)
    

答案 1 :(得分:0)

最后我尝试了设计模式"查询对象"混合使用AndrewSwerlick的解决方案

module QueryObject
  class AllocatedBudgetQuery

    def self.call(filters = {}, relation = ::AllocatedBudget.all)
      new(filters, relation).tap(&:call)
    end

    # filter can have the key/value as follow (key are symbols) :
    # place_id/string
    # current_fiscal_year/boolean
    # budget_state/['A', 'V', '*']
    def initialize(filters = {}, relation = ::AllocatedBudget.all)
      @relation     = relation
      @filters      = filters
    end

    def call
      conditions = {
        budget_previsionnel: { supprime: 'false' },
        budget: { supprime: 'false' }
      }

      # place filter
      conditions = conditions.merge(immeuble: { id: @filters[:place_id] }) if @filters.key?(:place_id)

      # situation budget filter
      conditions = conditions.merge(situation_budget: { codesituation: @filters[:budget_state] }) if @filters.key?(:budget_state)

      # main extract
      @relation = @relation.joins(:account_code,
                                  account_code: [:place],
                                  budget: %i[fiscal_year budget_state])
                           .where(conditions)

      # current fiscal year filter
      @relation = @relation.where("#{Date.today.to_s} between exercice_comptable.datedebutexercice and exercice_comptable.datefinexercice") if @filters.key?(:current_fiscal_year)
    end
  end
end