Reputation: 131112
I have an index action in rails that can handle quite a few params eg:
params[:first_name] # can be nil or first_name
params[:age] # can be nil or age
params[:country] # can be nil or country
When finding users I would like to AND all the conditions that are not nil. This gives me 8 permutations of the find conditions.
How can I can I keep my code DRY and flexible and not end up with a bunch of if
statements just to build the conditions for the find. Keep in mind that if no conditions are specified I just want to return User.all
Upvotes: 5
Views: 3296
Reputation: 1631
If you happen to be on an ancient project (Rails 2.x) and very messy, you could do something like the following for adding new fields to the original query.
Original code:
User.find(:all,
:conditions => ['first_name LIKE ? AND age=? AND country=?',
"#{name}%", age, country]
Adding a new dynamic condition on zip_code
field:
zip_code = params[:zip_code] # Can be blank
zip_query = "AND zip_code = ?" unless zip_code.blank?
User.find(:all,
:conditions => ['first_name LIKE ? AND age=? AND country=? #{zip_query}',
"#{name}%", age, country, zip_code].reject(&:blank?)
Adding a reject(&:blank?)
to the conditions
arrays will filter the nil
value.
Note: The other answers are much better if you are coding from zero, or refactoring.
Upvotes: 0
Reputation: 611
Using James Healy answer, I modify the code to be used in Rails 3.2 (in case anyone out there need this).
conditions = params.slice(:first_name, :age, :country)
conditions = conditions.delete_if {|key, value| value.blank?}
@users = User.where(conditions)
Upvotes: 1
Reputation: 1013
This works for me too
conditions = params[:search] ? params[:search].keep_if{|key, value| !value.blank?} : {}
User.all(:conditions => conditions)
Upvotes: 0
Reputation: 16515
I would normally use named scopes for something like this:
class User < ActiveRecord::Base
named_scope :name_like, lambda {|name| {:conditions => ["first_name LIKE ?", "#{name}%"]}}
named_scope :age, lambda {|age| {:conditions => {:age => age}}}
named_scope :in_country, lambda {|country| {:conditions => {:country => country}}}
end
class UsersController < ActionController
def index
root = User
root = root.name_like(params[:first_name]) unless params[:first_name].blank?
root = root.age(params[:age]) unless params[:age].blank?
root = root.country(params[:country]) unless params[:age].blank?
@users = root.paginate(params[:page], :order => "first_name")
end
end
That's what I normally do.
Upvotes: 3
Reputation: 16515
You could try Ambition, or a number of other ActiveRecord extensions.
Upvotes: 0
Reputation: 131112
This seems to work quite nicely:
conditions = params.slice(:first_name, :age, :country)
hash = conditions.empty? ? {} : {:conditions => conditions}
@users = User.all hash
Upvotes: 1
Reputation: 15168
How about something like:
conditions = params.only(:first_name, :age, :country)
conditions = conditions.delete_if {|key, value| value.blank?}
if conditions.empty?
User.all
else
User.all(:conditions => conditions)
end
Upvotes: 3