【问题标题】:Best way to conditionally chain scopes有条件地链接范围的最佳方法
【发布时间】:2016-07-01 02:28:49
【问题描述】:

我正在尝试扩展我的服务器端数据表的功能。我将一些额外的过滤器传递给我的控制器/数据表,用于过滤结果。目前在我的模型中,我在应用我的范围之前测试参数是否存在,但我不相信这是最好的方法,因为当我的过滤器列表增长时,我会有很多 if/else 场景。我怎样才能以'rails方式'做到这一点?

if params[:store_id].present? && params[:status].present?
  Order.store(params[:store_id]).status(params[:status])
elsif params[:store_id].present? && !params[:status].present?
  Order.store(params[:store_id])
elsif !params[:store_id].present? && params[:status].present?
  Order.status(params[:status])
else
  Order.joins(:store).all
end

回答: 将答案合并到这个工作代码中:

query = Order.all
query = query.store(params[:store_id]) if params[:store_id].present?
query = query.status(params[:status]) if params[:status].present?
query.includes(:store)

【问题讨论】:

  • 为什么你需要使用一个 api 端点来处理 3 个不同的任务。为什么不使用 3 个端点呢?
  • 你这是什么意思?我正在按商店/状态过滤我的订单。
  • 啊,好吧,我的错……我以为你在做一些手术

标签: ruby-on-rails ruby rails-activerecord


【解决方案1】:

你可以这样做:

query = Order
query = query.store(params[:store_id]) if params[:store_id].present?
query = query.status(params[:status]) if params[:status].present?
query = Order.joins(:store) if query == Order

或者,您也可以仅重组 statusstore 范围以包含其中的条件:

scope :by_status, -> status { where(status: status) if status.present? }

那么你可以这样做:

query = Order.store(params[:store_id]).by_status(params[:status])
query = Order.joins(:store) unless (params.keys & [:status, :store_id]).present?

【讨论】:

    【解决方案2】:

    由于关系是可链接的,因此“构建”您的搜索查询通常很有帮助。这样做的确切模式差异很大,我会警告不要过度设计任何东西,但是在我工作过的大多数大型 Rails 代码库中,使用普通的 Ruby 对象 (PORO) 来构建查询是很常见的。在您的情况下,您可能只需像这样简化逻辑即可:

    relation = Order.join(:store)
    
    if params[:store_id]
      relation = relation.store(params[:store_id])
    end
    
    if params[:status]
      relation = relation.status(params[:status])
    end
    
    @orders = relation.all
    

    Rails 甚至提供ways to "undo" logic that has been chained previously,以防您的需求变得特别复杂。

    【讨论】:

    • 我相信它不需要加入商店,除非两个参数都不存在,对吧?
    • @HoMan:取决于用例,但我猜这两个范围都已经加入商店。如果不是,这两个分支可以指定.unscope(:joins),或者像你的答案那样使用条件。
    • 另外,您的评论让我想到了为什么要使用联接。毕竟它是不需要的。伙计们干杯!
    【解决方案3】:

    上面的最佳答案对我有用。以下是其实际实现的示例:

          lessons = Lesson.joins(:member, :office, :group)
          if @member.present?
            lessons = lessons.where(member_id: @member)
          end
          if @office.present?
            lessons = lessons.where(office_id: @office)
          end
          if @group.present?
            lessons = lessons.where(group_id: @group)
          end
          @lessons = lessons.all
    

    【讨论】:

      猜你喜欢
      • 2019-11-24
      • 1970-01-01
      • 2018-05-20
      • 1970-01-01
      • 1970-01-01
      • 2011-09-17
      • 1970-01-01
      • 1970-01-01
      • 2010-11-29
      相关资源
      最近更新 更多