【问题标题】:Rails 5 and ActiveRecord: reusable filters for listing resourcesRails 5 和 ActiveRecord:用于列出资源的可重用过滤器
【发布时间】:2019-07-12 16:51:27
【问题描述】:

在我的应用程序中,我有许多页面需要显示人员列表并允许用户使用表单过滤他们。这些页面通常看起来相似。过滤器共享部分但仍不相同。

我想知道如何避免为不同的控制器重复几乎相同的代码?我尝试了范围,但我仍然需要解析参数并在视图中填充表单。

谢谢!

【问题讨论】:

  • 听起来你在找partials
  • 我当然知道 partials,但它们并没有真正完全解决我的问题。正如我所提到的,在控制器中我需要解析参数并构建查询。
  • 也许可以解释一下“解析参数并构建查询”的含义(代码示例总是有用的)。听起来像服务对象最好处理这个过程

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


【解决方案1】:

免责声明:https://github.com/dubadub/filtered 的作者在这里。

ActiveRecord 为关系提供merge 方法。它与两个查询部分相交,这允许将查询逻辑分成几部分。

基于这个想法,我创建了一个 gem https://github.com/dubadub/filtered

在你的情况下,它可能是这样的:

# app/controllers/people_controller.rb
class PeopleController < ApplicationController

  before_action :set_filter

  def index
    @people = People.all.merge(@filter)
  end

  private

  def set_filter
    @filter = PersonFilter.new(filter_params)
  end

  def filter_params
    params.fetch(:filter, {}).permit(:age, :active, :sorting)
  end

end
# app/filters/person_filter.rb
class PersonFilter < ApplicationFilter

  field :age

  field :active do |active|
    -> { joins(:memberships).merge(Membership.where(active: active)) }
  end

  field :sorting do |value|
    order_by, direction = value.values_at("order", "direction")

    case order_by
    when "name"
      -> { order(name: direction) }
    when "age"
      -> { order(age: direction) }
    else
      raise "Incorrect Filter Value"
    end
  end
end
# app/views/people/index.slim

  = form_for(@filter, url: search_path, method: "GET", as: :filter) do |f|
    .fields
      span Age
       = f.select :age, (18..90).map { |a| [ a, a ] }

    .fields
      span Active
        = f.check_box :active

    .fields
      span Sorting

      span Name
        = f.radio_button :sorting, "name asc"
        = f.radio_button :sorting, "name desc"

      span Age
        = f.radio_button :sorting, "age asc"
        = f.radio_button :sorting, "age desc"      

    .actions
      = f.submit "Filter"

希望对你有帮助!

【讨论】:

  • 谢谢,我看看能不能解决我的问题。如果我在使用您的 gem 时遇到任何问题,我可以在这里寻求帮助吗?
【解决方案2】:

你看过查询对象吗?

https://mkdev.me/en/posts/how-to-use-query-objects-to-refactor-rails-sql-queries

它们允许您在许多地方重用代码,您可以简单地传递 params.permit(...) 并获得 AR 输出。

# app/queries/user_query.rb
class UserQuery
  attr_accessor :initial_scope
  def initialize(scoped = User.all)
    @initial_scope = initial_scope
  end

  def call(params) # is what you pass from your controller
    scoped = by_email(@initial_scope, params[:email]
    scoped = by_phone(scoped, params[:phone]
    # ...
    scoped
  end

  def by_email(scoped, email = nil)
    email ? where(email: email) : scoped
  end
  def by_phone(scoped, phone = nil)
    phone ? where(phone: phone) : scoped
  end
end

# users_controller.rb
class UsersController < ApplicationController
  def index
    @users = UserQuery.new(User.all)
      .call(params.permit(:email, :phone))
      .order(id: :desc)
      .limit(100)
  end
end

# some other controller
class RandomController < ApplicationController
  def index
    @users = UserQuery.new(User.where(status: 1))
      .call(params.permit(:email))
      .limit(1)
  end
end

您可能可以重构此示例以减少为更丰富的对象编写这些查询的前期投资,如果您想出替代方案,请在此处发布,以便其他人可以学习如何使用查询对象。

【讨论】:

    猜你喜欢
    • 2016-06-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-08
    • 2019-03-14
    • 1970-01-01
    • 2016-11-07
    相关资源
    最近更新 更多