Search Engine Friendly URL’s with Ruby on Rails
March 27th, 2007
Sometimes it is nice to be able to show meaningful text in your URLs
URLs like: http://www.site.com/sports/1/teams/1;results
Although relatively short, don't have a lot of meaning
http://www.site.com/sports/cricket/teams/first-11;results
Has more meaning, but is not ideal from the web applications point of view.
http://www.site.com/sports/1-cricket/teams/1-first-11;results
Is the best of both worlds, and surprisingly easy to achieve in ruby.
In all the models you want a descriptive id, just overwrite the to_params method. By default model.to_params returns the value of the models primary key.
In my case the name field of all my models is the field i want to appear in the URL.
The following will do that. Sets the name to lowercase and replaces spaces with -
def to_param
"#{id}-#{name.downcase.gsub(/ /, '-'))}"
end
Unfortunately this is not ideal, what if you had non ascii characters in your name, you would end up with escaped characters and an ugly URL
So i chose to make use of the PermalinkFu plugin from Rick Olson
def to_param
"#{id}-#{PermalinkFu.escape(name)}"
end
The PermalinkFu plugin will convert the string so it is suitable to be placed in the url, it uses Iconv to do a unicode translation to ascii. This means strings such as café get translated to cafe
You might think that by overwriting to_params rails would get confused, but it dosn't. Well as long as you observe a simple rule:
- When generating a url pass the model in where it expects an id
:id => modelNOT:id => model.idThis works as rails automatically callsto_paramsto get the id, if you callmodel.idit will bypassto_params
After writing this i noticed SEO on Rails posted about Even better looking URLs with permalink_fu. I don’t think this is ideal since if you update the name/permalink anyone linking to your URLs will end up with a broken link. Also there is no way to ensure unique permalinks (unless you add extra code into your model which i will cover in a future post).
4 Responses to “Search Engine Friendly URL’s with Ruby on Rails”
Sorry, comments are closed for this article.
April 14th, 2007 at 12:29 PM
Good work on the combination, but I'm assuming this is for things without unique strings for their names/titles/slugs. Otherwise using a unique slug and making an index on the slug column would be fast, and good for readability and parsing and speed.
Even if slugs are only unique within the scope of their parents, using the same "unique slug with no id and db index" concept to get the parent first (via a before_filter etc), then get the child is still perfectly readable and speedy, surely...
April 14th, 2007 at 06:52 PM
Adam:
Well if you use this method (the to_param) without prepending the object id, it will break record finding.
When you do Model.find(param[:id]) the non integer part of the string will get stripped off so it is just like searching by an id number.
The other issue is if you ever change the field the to_param uses it is going to break any external links.
April 20th, 2007 at 01:25 PM
Just a note - I hit a bug using the combo {id}-{slug} method when doing popups. It fails to generate the URL.
eg <%= linkto('Preview', newsletterprevieweditionpath(@newsletter,@newsletteredition), :title => 'Preview this edition', :popup => ['plaintext_email', 'height=600,width=810']) -%>
I get an error of newsletterprevieweditionurl failed to generate from {:action=>"preview", :newsletterid=>"1-school", :controller=>"admin/newslettereditions", :id=>nil}, expected: {:action=>"preview", :controller=>"admin/newslettereditions"}, diff: {:newsletter_id=>"1-school", :id=>nil}
Any pointers on this issue? or is it actually a Rails issue with how popups hand to_params (or lack thereof)?
April 20th, 2007 at 02:03 PM
My bad, scrap last comment.