【问题标题】:Convert fat controller to scope, Rails 4将胖控制器转换为范围,Rails 4
【发布时间】:2014-05-23 03:09:18
【问题描述】:

我在设置多参数范围时遇到问题,我不知道将提供多少参数,我已在“胖控制器”下方发布,并且想知道如何在适当的范围内进行操作....(或者我猜的替代方案)

def browse
    conditions = {}
    if params[:category].present?
        conditions[:category] = params[:category]
    end
    if params[:make].present?
      conditions[:make] = params[:make]
    end
    @cars = Car.where(conditions)
end

这是我能想到的最好的方法,确保如果只提供两个参数之一,那么它会正确处理它。我觉得上面的控制器代码是不言自明的,但很高兴提供更多信息。 (每个 present? 块中都有额外的逻辑,但我已将其删除以使代码更清晰)

【问题讨论】:

  • 为了完整起见,能不能不显示整个代码?
  • 我问的原因是因为你可以很容易地通过首先使用这个来删除你的存在检查器:params.keep_if { |k,v| v.present? }

标签: ruby-on-rails model-view-controller ruby-on-rails-4


【解决方案1】:

我建议尽一切可能使用 ARel。我的意思是,ARel 提供了可链接性,这在这里会很好用!以下是我的做法:

def browse
  arel_base = Car.all
  arel_base = arel_base.where(category: params[:category]) if params[:category].present?
  arel_base = arel_base.where(make: params[:make]) if params[:make].present?
  @cars = arel_base
end

我使用arel_base 只是因为我发现我经常想为其他查询(例如记录计数等)保留基本 arel 的副本。所以我可以使用它来做到这一点,@cars 可以是我的索引页的最终结果(通常分页)。如果您不需要这些,只需使用@cars 代替arel_base

以上是一种改进,但我实际上要做的是更进一步,为每个.where 条件定义范围。所以在 Car 模型上,添加如下范围:

scope :for_category, -> category { where(category: category) }
scope :for_make, -> make { where(make: make) }

然后你可以在减少耦合的同时进一步简化你的控制器:

def browse
  arel_base = Car.all
  arel_base = arel_base.for_category(params[:category]) if params[:category].present?
  arel_base = arel_base.for_category(params[:make]) if params[:make].present?
  @cars = arel_base
end

更新

使用 Rails 4 语法在 Car 模型上生成 ActiveRecord::Relation —— 这是 .all 而不是 Rails .scoped。

【讨论】:

  • 我看到了“Car.scope”示例,但它不在rails 4中?还是那是一颗宝石?
  • 好点。我认为 Rails scoped 而 4.1 使用 scope(或 spawn)。更新了我的答案以显示 .scoped
  • 实际上,Rails 4 似乎已弃用 .scoped 以支持使用 .all。 (据我了解,.to_a 旨在取代旧的 .all 行为。)
【解决方案2】:

您可以将当前的控制器缩减为:

def browse
  filter = [:category, :make]
  params.slice(*filter).keep_if { |k,v| v.present? }
  @cars = Car.where(params)
end

我建议在您的 ApplicationHelper 或自定义模块中构建一个 filter 帮助程序。

class ApplicationHelper
  def filter(model, params)
    params.keep_if { |k,v| v.present? }
    model.where(params)
  end
end

然后在你的控制器中:

class CarsController < ApplicationController
  def browse
    @cars = filter(Car, params_to_filter)
  end

  private

  def params_to_filter
    params.slice(:category, :make)
  end
end

您可以争辩 filter 助手属于模型层。这取决于你。这只是一个让您入门的一般想法。关键是,如果您的过滤代码已经变得复杂,那么缩小到范围只会让您走这么远。在适当的地方抽象过滤。

如果您使用具有复杂过滤功能的第三方库(例如 DataTables),这将特别有用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-10-12
    • 2013-07-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多