SEO urls on the cheap

RESTful rails apps don’t necessarily make your urls that friendly if you end up with

http://www.zombification.net/posts/1

An id isn’t very descriptive of the content of the post. The quick fix for was is to add the method Post#to_param

class Post < ActiveRecord::Base

  validates_presence_of :title
  validates_presence_of :content

  def to_param
    return "#{self.id}-#{self.slug}" 
  end

  def slug
    return self.title.clone.downcase.gsub(' ','-')
  end

end

to_param is defined in ActionController::Routing and is used when generating urls with link_to, etc, but only when you specify the object and not the id itself.

link_to :controller => 'posts', :action => :show, id => @post

If you use name routes this is all taken care of for you. At this point all the links for @post will be the more meaningful URL

http://www.zombification.net/posts/1-nested-transactions

This is not the whole story however, this works with minimal effort because when we get our post object back Post#find(params[:id]), params[:id] will be ‘1-nested-transactions’. However, ActiveRecord::Base#find calls #to_i, on the single argument. So as long as the id of the post in params[:id] appears first the result from ActiveRecord will be the same.

rob@rob-laptop:~/src/zombification$ ./script/console 
Loading development environment (Rails 2.1.0)
>> Post.find('1-random-meaningless-string')
=> #<Post id: 1, title: "nested transactions", content: "After an hour of head banging, well....banging our ...", published_at: nil, created_at: "2008-07-29 16:40:35", updated_at: "2008-08-16 07:51:57">

Hey presto slightly more SEO urls. Ok you still have the id floating about which is a little ugly but its certainly better.