Creating SEO friendly URLs in Rails 3 - ruby-on-rails

I currently have URLs which look like this:
things?category_id=6&country_id=17
and I would like to have URLs which look like this:
/printer_cartridges/united_kingdom
Is there a way in Rails 3, without hard coding all of the categories and countries in the router to have the URLs as I would like above, perhaps using find_by_name or the such like? What is the best way to approach this?

match '/:category_slug/:country_slug', :to => 'things#index'
Then you'll need to update your action to look up everything using params[:category_slug] and params[:country_slug] instead of the ids. Look at the slugged gem to generate slugs.

In your category model add the method
def to_param
"#{category_name.parameterize}/#{location_name.parameterize}"
end
where category_name and location_name are where you input where you have have the names stored.

Related

Can I use Friendly Id on a Rails model without having to create a slug column on the model table?

On Rails 4.2, I would like to use Friendly Id for routing to a specific model, but dont wan't to create a slug column on the model table. I would instead prefer to use an accessor method on the model and dynamically generate the slug. Is this possible? I couldn't find this in the documentation.
You can not do this directly using friendly id because of the way it uses the slug to query the database (relevant source).
However it is not difficult to accomplish what you want. What you will need are two methods:
Model#slug method which will give you slug for a specific model
Model.find_by_slug method which will generate the relevant query for a specific slug.
Now in your controllers you can use Model.find_by_slug to get the relevant model from the path params. However implementing this method might be tricky especially if your Model#slug uses a non-reversible slugging implementation like Slugify because it simply get rids of unrecognized characters in text and normalizes multiple things to same character (eg. _ and - to -)
You can use URI::Escape.encode and URI::Escape.decode but you will end up with somewhat ugly slugs.
As discussed here I went with the following approach for custom routing based on dynamic slug.
I want custom route like this: /foos/the-title-parameterized-1 (where "1" is the id of the Foo object).
Foo model:
#...
attr_accessor :slug
#dynamically generate a slug, the Rails `parameterize`
#handles transforming of ugly url characters such as
#unicode or spaces:
def slug
"#{self.title.parameterize[0..200]}-#{self.id}"
end
def to_param
slug
end
#...
routes.rb:
get 'foos/:slug' => 'foos#show', :as => 'foo'
foos_controller.rb:
def show
#foo = Foo.find params[:slug].split("-").last.to_i
end
In my show view, I am able to use the default url helper method foo_path.

Add directory to URL in Rails Routes

For SEO purposes, I've appended a slug to an id, like so:
/browse/12885-evergreen-instrumental
by using this in my model:
def to_param
"#{id}-#{slug}" || id
end
Ideally, I'd like to have the structure as so:
/browse/12885/evergreen-instrumental
I've tried several things via routes.rb to accomplish this, but to no avail yet.
Essentially, I'm trying to do this:
match "/browse/:id/:slug" => "tracks#show", as: :track
and than will want a redirect:
get '/browse/:track_id', to: redirect('/browse/%{track_id}/%{slug}')
Any ideas on how to get this to actually work?
Thanks in advance.
I think what you are looking for is the FriendlyId gem. It does exactly what you are trying to do.

customized rails routing url

I am little bit confused in using rails route. I need some suggestion about customizing my url.
This is my current url
http://localhost:3000/posts/product/41?product_id=2
and
http://localhost:3000/posts/product/41?model_id=24&product_id=2
This is my link
<%= link_to product_model.name, controller: :posts,action: :product,product_id: params[:product_id],model_id: product_model.id
Logically product should come first in url. But why model prefers first here.
And i need my url something like this
http://localhost:3000/posts/product/41/mobile
and
http://localhost:3000/posts/product/41/mobile/nokia
Since i am not familiar with rails route i didn't write any special coding in my route
Here is the simple route exist
resources :posts
Ok your question here actually contains two different problems, so i will give suggestions to both.
1. Nested resources
Your first problem is to use "nested routes". Rails guide has a long and good article about routes and how to write and use them, including nested routes. You can check it out here: http://guides.rubyonrails.org/routing.html#nested-resources.
However in your situation would the solution look something like this:
resources :category do
resources :sub_category do
resources :products do
resources :models
end
end
end
You can now greate links like this
<%= link_to product_model.name, category_sub_category_product_model_path(#category, #sub_category, #product, product_model) %>
You can see that i have removed posts, see 3. Refactor design to see why. If you really want this as a action on posts, should you however do something like this (but would recommend this!):
get "posts/product/:category_id/:subcategory_id/:product_id/:model_id", to "posts#product", as: :posts_product
This would be used like this in your views:
<%= link_to product_model.name, posts_product_path(#category, #sub_category, #product, product_model) %>
2. Pretty URL's
Your second problem is to use model names instead of id's in your urls. The simpels solution for this is having a unique attribute on your model that you can use instead of id, and then just add a to_param method. Fx for product could we do something like this:
class Product < ActiveRecord::Base
def to_param
name
end
end
Ryan Bates have made a good screencast about this: http://railscasts.com/episodes/63-model-name-in-url-revised. If you want something more flexible should you use the gem Friendly Id. And again does Ryan comes to the rescue with another great RailsCast: http://railscasts.com/episodes/314-pretty-urls-with-friendlyid.
3. Change design
Ok well so this is just my opinion, feel free to ignore it. But their is some bad practices and signs in your examples, so let me just quickly go through what i think you should improve on.
Restful actions
You should, when possible always avoid creating controller actions that is not restful (simply put is the base actions index, show, new, create, edit, update and destroy the only restful actions). In your example does this mean that the product action of the posts controller should be changed to something restful. Why not move it to the product model controller and call it "show"?
Deeply nested resources
You should avoid nesting your routes to deeply. Is it really important to show both the category, the sub category, the product AND the model in your url? Maybe that's how your models are associated internally in your application but why should the user know this? If you don't have a list of subcategories at "/posts/product" and a list of products at "posts/product/41" is there no reason to have so long a route. A rule of thumb is "nest no deeper then two levels", ie. ":category/:sub_category". Further more does short routes mean better SEO.
As i said, feel free to ignore these suggestions, your application would work without these changes. However changing these things would greatly help you structure you code, and keep your codebase clean and maintainable. These rules and principles is not something i have just conjured out of nothing, but very accepted principles in the Rails community. You can google each of these principles or patterns and see a lot of articles and posts on why it's a good idea to follow them, especially when you work with Rails.
Resources
Rails Routing from the Outside In — Ruby on Rails Guides
norman/friendly_id - Github
#314 Pretty URLs with FriendlyId - RailsCasts
#63 Model Name in URL (revised) - RailsCasts
Take a look at the friendly-id gem
There's a great RailsCast about it
Add this to your model inmodel.rb
def to_param
name
end
and then add
#model = Model.find_by_name(params[:id]) to your show method, then you can get the url as you mentioned above.
PS: You Should have name field for Model table in your schema.
I think you are looking for nested routes. Please refer this link http://guides.rubyonrails.org/routing.html#nested-resources
and use to_param method in the model if you want to display model_name instead of id as explained by #Ajay Kumar
def to_param
name
end
where name is the model attribute for that specific model.
Why not a namespace?
namespace :posts do
resources :products
end
This should do I think..
Namespace does not include restful ids into the scope..

map.resources with alternate primary key(s)

I have a Rails model Object that does not have an ID column. It instead uses a tuple of primary keys from two other models as its primary key, dependency_id and user_id.
What I want to do is be able to do something like this in routes.rb:
map.resources :object, :primary_key => [:dependency_id, :user_id]
And for it to magically generate URLs like this:
/objects/:dependency_id/:user_id
/objects/:dependency_id/:user_id/1
/objects/:dependency_id/:user_id/1/edit
...Except that I just made that up, and there is no such syntax.
Is there a way to customize map.resources so I can get the RESTful URLs, without having to make custom routes for everything? Or am I just screwed for not following the ID convention?
The :path_prefix option looks somewhat promising, however I would still need a way to remove the id part of the URL. And I'd like to still be able to use the path helpers if possible.
You should override Object model's method to_param to reflect your primary key. Something like this:
def to_param
[dependency_id, user_id].join('-')
end
Then when you'll be setting urls for these objects (like object_path(some_object)) it will automatically gets converted to something like /objects/5-3. Then in show action you'd have to split the params[:id] on dash and find object by dependency_id and user_id:
def show
dep_id, u_id = params[:id].split('-').collect(&:to_i)
object = Object.find_by_dependency_id_and_user_id(dep_id, u_id)
end
You can also look at find_by_param gem for rails.

Ruby on rails to_param with multiple fields for SEO

I am trying to make my urls prettier and still use restful resources. I understand that you can override the to_param method if you object has a name property like this:
def to_param
self.name
end
which will give you the route /:model/:name. This is all straightforward, but I have to be capable of having the same name with multiple different languages. I haven't been able to find a blog entry on how to do this, so how can i override the to_param method to provide me a route similar to /:model/:language/:name ?
You could always do:
/language/:language/model/:name
You'd do this with nested routes:
map.resources :languages do |l|
l.resources :profiles
end
Then your route would be:
langauge_profile_url('spanish', #profile)
However...
Depending on what you're trying to do you might be better of using the built in rails i18n stuff. Is this so users can browse the site in different languages??

Resources