Reputation: 680
In my application, I have a Book
model. I have around 10000k book records in my database. Basically, the application works as the user can select options and get a list of books that matches their inserted credentials.
Each of these books
has type
, language
& genres
.
Each book can have several type
, language
& genres
(like an array)
title: 'Have fun book'
type: ['pdf', 'paper', 'website']
language: ['Egnlish', 'French', 'German', 'Spanish']
genres: ['comedy']
My BooksController
:
def book_params
params.require(:book).permit(type:[], language:[], genres:[])
end
The form where the user can insert their credentials and filter the books looks like the picture below:
Currently, this is what I'm doing in my BooksController
to filter the books:
@book_filter = Book
.where(type: @book.type)
.where(language: @book.language)
.where(genres: @book.genres)
This works kind of fine, but I have a few issues with it. If for example, the user doesn't select any Book type/ type
or any other option, instead of getting all
, I get nil
and for that reason, no books are shown to the user.
What I have in mind is that if the option wasn't selected, either where
for that options doesn't get affected or it passes all
.
I tried this with no luck:
@book_filter = Book
.where(type: @book.type || '')
.where(language: @book.language || '')
.where(genres: @book.genres || '')
My second issue is that I feel the filter could be written much smarter & Rails way.
Thanks in advance and any help is appreciated!
Rails 5.1
Upvotes: 2
Views: 2994
Reputation: 399
Based on https://stackoverflow.com/a/54401841/3058437 but replacing public_send
for where
worked for me. Rails 5
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
Upvotes: 0
Reputation: 680
In your models/concerns
create a 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
Include the module
in your model
as below:
class Product
include Filterable
...
end
Then in your controller do:
@products = Product.filter(params.slice(:status, :location, :starts_with))
private
# 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
The reason behind the module
is because this code is reusable and can be used in any part of your application.
PS: Please keep in mind variables or models are not the same as the question but the solution is a general solution that can be implemented to any application.
Upvotes: 3