Search code examples
ruby-on-railsruby-on-rails-3urlroutestagging

How to create user freindly routes for tagging


I have a model Image:

class Image < ActiveRecord::Base
  attr_accessible :description, :name, :size, :image, :tag_ids

  has_many :taggings, :dependent => :destroy
  has_many :tags, :through => :taggings
end

Then I have my Tag model:

class Tag < ActiveRecord::Base
  attr_accessible :name
  has_many :taggings, :dependent => :destroy
  has_many :images, :through => :taggings

end

My routes.rb is currently:

resources :images do
    get 'confirm_destroy', :on => :member
end
resources :tags

Now let's say I created a few tags "blue", "red" and "yellow" for the images. On some page I want to show a list of tags and then link them to e.g. www.example.com/yellow where all the images tagged as yellow shall be shown. The view (haml) for this tag list is currently:

- @tags.each do |tag|
  = link_to(tag.name, tag)

But it generates a link to www.example.com/tags/2 (with 2 being the tag_id).

How can I create the correct resources in order to link to www.example.com/yellow instead of www.example.com/tags/2. Will the view with "link_to" be the same in this case?


Solution

  • You won't be able to create a route to www.example.com/yellow because this doesn't reference a specific resource, and subsequently could create conflicts. Imagine if you had a tag called 'images', Rails wouldn't know if the url to www.example.com/images referred to a specific tag, or the images resource.

    The best we can do is create a resource which uses the name as the identifier in the URL, such that www.example.com/tags/yellow would show the tag with 'yellow' as its name attribute.

    To do this, you need to define the following to_param method in the model for Tag.

    class Tag < ActiveRecord::Base
      attr_accessible :name
      has_many :taggings, :dependent => :destroy
      has_many :images, :through => :taggings
    
        def to_param
            name
        end
    end
    

    This will tell Rails to use the name attribute for routing instead of the default id. Your link_to won't need to be updated, however, your Tag controller will need to now find the Tag by name as opposed to ID, like so:

    class TagsController < ApplicationController
    
        def show
            @tag = Tag.find_by_name(params[:id])
        end
    
        ...
    
    end