My application is in RoR
I have an action/view called showsummary where the ID has been passed into the URL, and the controller has used that to instantiate @vendor wher
Ryan Bates has a great screencast on this very subject.
Basically you overload the to_param
method in the Vendor model.
def to_param
permalink
end
Then when you look up the resource in your controller you do something like this:
@vender = Vender.find_by_name(params[:id])
But the problem with this is that you'll have to make sure that the vendors' names are unique. If they can't be then do the other solution that Ryan suggests where he prepends the the id to the name and then parses the resulting uri to find the item id.
I thought I'd mention String#parameterize, as a supplement to the tagged answer.
def to_param
"#{id}-#{name.parameterize}"
end
It'll filter out hyphenated characters, replace spaces with dashes etc.
Friendly ID http://github.com/norman/friendly_id/blob/26b373414eba639a773e61ac595bb9c1424f6c0b/README.rdoc
You do this by modifying the routes that are used to access those URL's and changing them to use :name, rather than :id. This will probably mean that you have to write the routes yourself rather than relying on resources.
For instance add this to the routes.rb file:
map.with_options :controller => "vendor" do |vendor|
vendor.connect "/vendor/:name", :action => "show"
# more routes here for update, delete, new, etc as required
end
The other change that will be required is that now you'll have to find the vendor object in the database by the name not the id, so:
@vendor = Vendor.find_by_name(params[:name])
Internally (at least to my knowledge/experimentation) whatever parameter name is not specified in the URL part of the route (i.e. not within the "/Controller/Action/:id" part of it) is tacked on to the end as a parameter.
Take a look also at this quck start to SEO for Rails
All of these solutions use find_by_name
, which would definitely require having an index on that column and require they are unique. A better solution that we have used, sacrificing a small amount of beauty, is to use prefix the vendor name with its ID. This means that you dont have to have an index on your name column and/or require uniqueness.
vendor.rb
def to_param
normalized_name = name.gsub(' ', '-').gsub(/[^a-zA-Z0-9\_\-\.]/, '')
"#{self.id}-#{normalized_name}"
end
So this would give you URLs like
/1-Acme
/19-Safeway
etc
Then in your show action you can still use
Vendor.find(params[:id])
as that method will implicitly call .to_i
on its argument, and calling to_i
on such a string will always return the numerical prefix and drop the remaining text- its all fluff at that point.
The above assumes you are using the default route of /:controller/:action/:id
, which would make your URLs look like
/vendors/show/1-Acme
But if you want them to just look
/1-Acme
Then have a route like
map.show_vendor '/:id', :controller => 'vendors', :action => 'show'
This would imply that that it would pretty much swallow alot of URLs that you probably wouldnt want it too. Take warning.