How to automagically get friendly URLs in Rails 3

in Ruby on Rails Tips

By default, Rails uses ID’s in URLs. For example, let’s say we have a list of categories stored in the categories table of the database. The “Super Cool” category is stored with categories#id = 5. To view that category our URL will look like:

http://yourAwesomeDomain.com/category/5

That works great, but it’s not very user friendly. It’s also not very good for SEO purposes. A better URL would use a human-readable and search-engine-decipherable slug instead of an ID. For example:

http://yourAwesomeDomain.com/category/super-cool

How do we get that? Easy!

First, add a column named “slug” to the categories table:

# \db\migrate\20120402020611_create_categories.rb

class CreateCategories < ActiveRecord::Migration
  def change
    create_table :categories do |t|
      t.string :name
      t.string :slug
      t.timestamps
    end
  end
end

Next, add these lines to your category model:

# \app\models\category.rb

class Category < ActiveRecord::Base
  before_create :generate_slug
  attr_protected :slug

  def generate_slug
    self.slug = name.parameterize
  end

  def to_param
    slug
  end

end

Bam! You are done. Your rails helpers and other logic will now automagically use slugs instead ID’s. For example:

@my_new_cat =  Category.create(:name => 'Super Cool')
=>  #<Category id: 3, name: "Super Cool", slug: "super-cool", created_at: "2012-04-02 18:43:08", updated_at: "2012-04-02 18:43:08">

category_path(@my_new_cat)
=> /category/super-cool

link_to(@my_new_cat.name, @my_new_cat)
=> <a href="/category/super-cool">Super Cool</a>

And in your controller, you can find the category by searching with the slug:

# \app\controllers\category_controller.rb

class CategoryController < ApplicationController
  def show
    @category = Category.find_by_slug(params[:id])
  end
end

Pretty cool, huh?

Payel June 21, 2012 at 12:31 pm

Thanks for the post deleting aoitcsasions is also not made very clear by the rails API.@remkade just to add that before_destroy { |post| oy_all} isn’t really necessary if you use the :dependent option when you first associate the objects.Like so: has_many :feed_entries, :dependent => :destroy # which will destroy all associated objects whenever the feed itself is destroyed.:dependent also has a bunch of options besides destroy.

Comments on this entry are closed.

Previous post:

Next post: