SpinBits is a Web 2.0 Consultancy company that focuses on delivering interactive and smart software to enhance the profitability and image of its customers.

Sunday, July 13th, 2008

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.

Leave a Reply