Filtering and sorting with named_scope
A common feature in most web apps is filtering and sorting items, doing this in Rails 2.1 got really simple and clean with named_scope. Here is a quick tutorial on how we do it in our apps:
We start by defining the filters and sorting options as scopes:
# Types
named_scope :rentals,
:conditions => ["class_name = ?", TYPES[:rental]]
named_scope :sales,
:conditions => ["class_name = ?", TYPES[:sales]]
# Sorting
named_scope :by_date,
:order => 'created_at DESC'
named_scope :by_popularity,
:order => 'views_count DESC'
named_scope :by_rating,
:order => 'rating_avg DESC'
# Filters and conditions
named_scope :where_category,
lambda { |category_id| { :conditions => sanitize_sql_array(["category_id = ?", category_id]) } }
Then we define the magic ‘list’ method that joins those scopes depending on the passed params:
def self.list(options={})
listings = self.send(options[:type]) if !options[:type].blank? and ['sales', 'rentals'].include?(options[:type])
listings = (listings || self).send(:where_category, options[:category_id]) unless options[:category_id].blank?
listings = (listings || self).send(options[:sort]) unless options[:sort].blank?
# support will_paginate
return (listings || self).paginate(:page => options[:page])
end
That’s it, now let’s call ‘list’ in the controller:
def index
@properties = Property.list(params)
respond_to do |format|
format.html
format.js
format.xml { render :xml => @properties }
end
end
Now with a single line we have a single database query that retrieves the records we want, filtered and sorted the way we want them.
Property.list(:type => 'sales', :sort => 'by_date')
will query:
SELECT * FROM `properties` WHERE (class_name = 'Sale') ORDER BY created_at DESC LIMIT 0, 10
This structure is really helpful for adding more filters in the future, we just need to define the new scope and use it in the ‘list’ method and we’re ready to go.