Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails Search with optional parameters?

Tags:

How would I do a search on a database when the search can provide many optional parameters such as ID, Zip, City, and State? These can either have values or be blank entirely. How would I make a rails query like that?

like image 522
TheDudeGuy15 Avatar asked Dec 18 '10 20:12

TheDudeGuy15


1 Answers

The usual advice is to move logic to the model and keep the controller as lean as possible. There are different approaches for the filter method, the first one:

class Record < ActiveRecord::Base   def self.filter(attributes)     attributes.select { |k, v| v.present? }.reduce(all) do |scope, (key, value)|       case key.to_sym       when :id, :zip # direct search         scope.where(key => value)       when :city, :state # regexp search         scope.where(["#{key} ILIKE ?", "%#{value}%"])       when :order # order=field-(ASC|DESC)         attribute, order = value.split("-")          scope.order("#{self.table_name}.#{attribute} #{order}")       else # unknown key (do nothing or raise error, as you prefer to)         scope       end      end     end end 

A second approach, write a bare filter that only uses existing scopes:

class Record < ActiveRecord::Base   SUPPORTED_FILTERS = [:id, :city, ...]   scope :id, ->(value) { where(id: value) }   scope :city, ->(value) { where(city: "%#{value}%") }   ...    def self.filter(attributes)     attributes.slice(*SUPPORTED_FILTERS).reduce(all) do |scope, (key, value)|       value.present? ? scope.send(key, value) : scope     end     end end 

For Rails 5, which now uses ActionController::Parameters, the syntax for the filter method is:

def self.filter(attributes)   attributes.permit(SUPPORTED_FILTERS).to_hash.reduce(all) do |scope, (key, value)|     value.present? ? scope.send(key, value) : scope   end   end 

Models can be called from anywhere in your app, so they are re-usable and easier to test. Now the controller looks as simple as:

class RecordsController < ApplicationController::Base   respond_to :html, :xml    def index     @records = Record.filter(params)   end end 
like image 183
20 revs, 4 users 93% Avatar answered Sep 19 '22 06:09

20 revs, 4 users 93%