05 March 2011

SEO friendly URL in ruby on rails and friendly_id

By default Rails generate URL with ID. In short to_param method returns a string, which Action Pack uses for constructing an URL to this object. The default implementation returns records’ id as a string.
Suppose, we have a Category model. The routes.rb will look like:
map.resources :categories
In categories.rb controller:
@category = Category.find(1)
In view index.html.erb:
This will generate the URL: /category/1

While working on building a website Khan Academy In Bangla as a part of the translation project of Khan Academy , I realized that a SEO friendly URL is mandatory. In this website, I have categories like physics, biology or math and tutorials under each category. In order to make the user friendly URL I needed to overwrite to_param method in model.

In order to make it SEO friendly, in Category.rb model I added the line:
def to_param
  "#{id}-#{title.downcase.gsub(/[^a-zA-Z0-9]+/, '-').gsub(/-{2,}/, '-').gsub(/^-|-$/, '')}"
The regular expression will remove any special character. It generates the url: /category/1-biology. Looks better, right?
Although the URL has become SEO friendly now but this method has some drawbacks. To summarize some:

1. It appends the id with the name. If we remove ID, then maintaining uniqueness will be a problem
2. If the title field is edited then the URL will be changed causing SEO to break.
3. For a large application, it will not be feasible to overwrite to_param in tons of model.

In this point, I found a nice plugin/gem to make all these pretty simple and it is friedly_id . It will do all the work for you with some simple steps. For my system what I did were:
1. Install the gem:
gem install friendly_id
2. In environment.rb include the gem.
require 'friendly_id'
3. Generate friendly_id by going to my application directory:
cd khanacademybangla
script/generate friendly_id
4. Run the migration
rake db:migrate
5. Add has_friendly_id to the models which I am looking for to generate user friendly id
class Category < ActiveRecord::Base
has_friendly_id :title, :use_slug => true
6. Run a rake task to generate slugs for existing records:
rake friendly_id:make_slugs MODEL=ModelName
For my example it was:
rake friendly_id:make_slugs MODEL=category
That’s it! Now we will be able to see a completely SEO friendly url: /category/biology

I am yet to push my code to production! You need to wait till tomorrow to see it live.


Sohan said...

Some months ago I wrote a little plugin that would do the same - if you are interested, you can take a look at https://github.com/smsohan/acts_as_permalinkable

Fuad Omar said...

@Sohan, great to hear that, I will check it out.

Abdul barek said...

Fuad vai,
It can be easily done using Rails route and even you have need no gem or plugin.

How I did it like:

I disabled project resource
#resources :projects

In route.rb
match 'project/:category/:name/:id', :to => 'projects#show', :as => "project"

In view:
<%= link_to 'Show', project_path(:category => project.category.name, :name => project.name.gsub(' ','-'), :id => project.id) %>

And the SEO friendly URL becomes:

Anonymous said...

Great posting.It is very informative.Thanks for sharing your information.
Joomla developer

Kota Saikrishna said...

rake friendly_id:make_slugs MODEL=ModelName

when i run this command to generate slugs for existing records getting error
rake aborted
dont know how to build task 'friendly_id:make_slugs'

can u help me on this