Ruby on Rails - 根据用户选择的选项过滤记录

Ruby on Rails - Filter records based on users selected options

在我的应用程序中,我有一个 Book 模型。我的数据库中有大约 10000k 本书记录。基本上,该应用程序的工作原理是用户可以 select 选项 并获取与他们插入的凭据 匹配 的图书列表。

每个 books 都有 typelanguagegenres

每本书可以有几个type, language & genres (像一个数组)

title: 'Have fun book' 
type: ['pdf', 'paper', 'website']
language: ['Egnlish', 'French', 'German', 'Spanish']
genres: ['comedy']

我的BooksController:

def book_params
  params.require(:book).permit(type:[], language:[], genres:[])
end

用户可以在其中插入凭据并过滤书籍的表单如下图所示:

目前,这就是我在 BooksControllerfilter 书中所做的:

@book_filter = Book
                .where(type: @book.type)
                .where(language: @book.language)
                .where(genres: @book.genres)

这工作得很好,但我遇到了一些问题。例如,如果用户没有 select 任何 书籍类型/ type 或任何其他选项,而不是 all,我得到 nil 因此,不会向用户显示任何书籍。

我的想法是,如果选项未被 selected,则该选项的 where 不会 受到影响 或它通过 all.

我试过了,但没有成功:

@book_filter = Book
                .where(type: @book.type || '')
                .where(language: @book.language || '')
                .where(genres: @book.genres || '')

我的第二个问题是我觉得过滤器可以写得更聪明 & Rails .

在此先致谢,如有任何帮助,我们将不胜感激!

Rails 5.1

在你的 models/concerns 中创建一个 module:

module Filterable
  extend ActiveSupport::Concern

  module ClassMethods
    def filter(filtering_params)
      results = self.where(nil)
      filtering_params.each do |key, value|
        results = results.public_send(key, value) if value.present?
      end
      results
    end
  end
end

在您的 model 中包含 module,如下所示:

class Product
  include Filterable
  ...
end

然后在你的控制器中执行:

@products = Product.filter(params.slice(:status, :location, :starts_with))

私人

# A list of the param names that can be used for filtering the Product list
def filtering_params(params)
  params.slice(:status, :location, :starts_with)
end

module 背后的原因是因为这段代码是可重用的,可以在您的应用程序的任何部分使用。

PS:请记住变量或模型与问题不同,但解决方案是可以对任何应用程序实施的通用解决方案。

基于 但将 public_send 替换为 where 对我有用。 Rails5

module Filterable
  extend ActiveSupport::Concern

  module ClassMethods
    def filter(filtering_params)
      results = self.where(nil)
      filtering_params.each do |key, value|
        results = results.where("#{key} = ?", value) if value.present?
      end
      results
    end
  end
end