Hongbo Wu
2 min readJun 17, 2021

--

Filtering models in ActionController

Today, I encountered a challenge of listing models and filtering them with some request parameters in ActionController of Rails. Initially, I thought about chaining the where clauses of ActiveRecord in the controller.

class EventsController
def index
@events = Event.where(nil)
@events = @events.where(request_id: params[:request_id]) if params[:request_id].present?
@events = @events.where('created_at > ?', params[:start_date]) if params[:start_date].present?
@events
end
end

But this looks a bit tedious with all the chaining queries and if check. So with the pubic_send method in Ruby, I could do something better with a loop and custom method. To do that, I used scope in the model:

class Event
scope :filter_by_request_id, ->(request_id) { where(request_id: request_id }
scope :filter_by_start_date, ->(start_date) { where('created_at > ?', start_date }
end
class EventsController
def index
@events = Event.where(nil)
filtering_params(params).each do |key, value|
@events = @events.public_send("filter_by_#{key}") if value.present?
end
@events
end
def filtering_params(params)
params.slice(:request_id, :start_date)
end
end

This looks better and the thing is I will need to create this code segment for each model and controller which needs filtering. So a even reusable solution is to use concern of ActiveSupport in model and put the logic in it.

module Filterable
extend ActiveSupport::Concern
def ClassMethods
def filter(params)
results = where(nil)
params.each do |key, value|
results = results.public_send("filter_by_#{key}") if value.present?
end
results
end
end
class Event
include Filterable
scope :filter_by_request_id, ->(request_id) { where(request_id: request_id }
scope :filter_by_start_date, ->(start_date) { where('created_at > ?', start_date }
end

So in this way, in the controller, I can just use this class method defined in the concern and used in the other controllers too.

class EventsController
def index
@events = Event.filter(params.slice(:request_id, :start_date))
end
end

Thanks for the reading and this is it.

All the credit to the original author:
https://www.justinweiss.com/articles/search-and-filter-rails-models-without-bloating-your-controller/

--

--