1

我有一个模型的索引视图,我想通过模型属性的某种组合进行过滤。

例如,我有一个账单模型(不是鸭子上的那种,你必须支付的那种),我可能会根据收款人和/或状态进行过滤。

该模型对每个单独的属性都有一个范围,例如

scope :bill_status, lambda {|status| where("status = ?", status}
scope :bill_payee, lambda {|payee| where("payee_id = ?", payee.id}

该视图允许用户选择零个或多个选项——如果未选择一个选项,则表示“不按此过滤”。

在控制器中,我可以做一些像这样令人讨厌的事情:

def index
  status = params[:bill][:status]
  payee = params[:bill][:payee]
  if status.present? and payee.present?
    # chain scopes
    @bills = Bill.bill_status(status).bill_payee(payee)
  elsif status.present?
    @bills = Bill.bill_status(status)
  elsif payee.present?
    @bills = Bill.bill_payee(payee)
  else
    @bills = Bill.all
  end

  # rest of controller action
end

但是,虽然这可行,但它既不美观也不易于扩展——添加第三个过滤器意味着我现在有更多的可能性。我追求美丽和纯洁。

假设我的范围都是可链接的,看来我应该能够做类似的事情

def index
  @bills = Bill.all
  @bills = @bills.bill_status(params[:bill][:status]) if params[:bill][:status].present?
  @bills = @bills.bill_payee(params[:bill][:payee]) if params[:bill][:payee].present?

  # rest of controller code
end

'cept 它不起作用,因为它Bill.all是一个数组。另外,这并不好玩,因为Bill.all执行查询,由于 AREL 的魔法,我只想运行一次。我应该只定义一个范围all_bills(没有条件吗?)——ActiveRecord::Relation我猜那是...

有没有更优雅地解决这个问题的模式?每当我有代码来做一件依赖于模型、视图和控制器的事情时,我都会觉得我可能做错了什么。或者好像比我更聪明、更努力的人已经解决了它:-)

额外的问题:我希望这一切都能与我选择的分页器一起使用,这是最优秀的 Kaminari 宝石。

欢迎所有想法和想法。

4

2 回答 2

4

我会做这样的事情:

proxy = Bill.scoped
if status.present?
  proxy = proxy.bill_status(status)
end
if payee.present?
  proxy = proxy.bill_payee(payee)
end

@bills = proxy

你甚至可以做一些元编程:

@bills = [:status, :payee, ...].inject(Bill.scoped) do |proxy, param|
  val = params[:bill][param]
  val.present ? proxy.send("bill_#{param}", val) : proxy
end
于 2012-08-06T09:54:22.613 回答
1

当我寻找看似常见问题的解决方案时,我查看了 Ryan Bates 的RailsCast 并在 Ransack 上找到了 3 天前的一集。Ransack 是用于表单搜索和列排序的非常酷的宝石,我认为这就是我要走的路。

感谢这里的答案——我很高兴从那些花时间和精力回答问题的人那里学到了一些很棒的技巧。

于 2012-08-06T13:01:32.597 回答