Search engine friendly URLs, or slugs, or permalinks, are not as straightforward as one might think. In this lesson, I am going to cover two different methods for creating readable, search friendly URLs in rails. There are some limitations to each of these methods, which will be discussed throughout the lesson.
Lesson Resources
- Rails 4.2
- Rails Routing Guide
Lesson Checkpoints
A Video Tutorial for SEO Friendly Rails Routing
The Easy Way
In the model, we can override the default parameters with the to_param method. We call the parameterize method on the object’s name attribute, which converts “My Pet Turtle!!!” to “my-pet-turtle”. The end result is a url that is formatted like: /123-my-pet-turtle
.
def to_param
"#{id}-#{name.parameterize}"
end
That’s it, there’s no other code that needs to be changed in the application. Your existing controller methods that use Model.find(params[:id])
will still work because they will only extract the initial id number. This is decent, but it would still be nice to have the URL completely free of the ID number. Let’s start over to make this happen.
The Hardish Way Part 1
I call this the hardish way because it’s relatively complicated compared to the easy way, but it’s still quite easy overall. We’re going to create a new permalink attribute for the Post model that will serve as our URL.
$ rails g migration AddPermalinkToPost permalink:string
$ rake db:migrate
The Hardish Way Part 2
In the model, we are going to use an ActiveRecord callback to set the permalink before the record is created. First, we need to define a generate_permalink
method, which uses a Post object's title and converts it to a url friendly format. Then we override the to_param
method and set it equal to the object's permalink attribute.
It is important to only run the callback on the create action, otherwise the permalink will change if the title attribute is updated. If Google has already indexed that URL, it will direct visitors to a 404 error page.
models/post.rb
before_validation :generate_permalink, on: :create
def generate_permalink
self.permalink = self.title.parameterize
end
def to_param
permalink
end
The Hardish Way Part 3
Now that the ID is totally stripped from the URL, we need to tell rails to search for a different parameter. In the controller, we will need to use the find_by_permalink helper method. Note, the params hash will still have a key of “id”, such as {:id => “my-pet-turtle” }
controllers/posts_controller.rb
def set_post
@post = Post.find_by_permalink(params[:id])
end
Dealing with Duplicate Permalinks
There is one problem with this implementation — duplicate permalinks. We could just run a uniqueness validation, but this would be confusing for end users, as it would raise a validation error for a seemingly valid title. If any duplicates exist, we can refactor our generate permalink method to check for duplicates, then append a number to the end of the URL based on the number of existing duplicates. For example, if there are three other Posts titled “My Post”, you would end up with the following permalinks: “my-post”, “my-post–2”, and “my-post–3”.
The method below defines a pattern variable, then searches the database for other rows that contain that same pattern. It will count the matching patterns, then append the count to the end of of permalink.
def generate_permalink
pattern = self.title.parameterize
duplicates = Post.where("permalink LIKE ?", "#{pattern}%")
unless duplicates.empty?
self.permalink = "#{pattern}-#{duplicates.count+1}"
else
self.permalink = pattern
end
end
Using a Gem for Permalinks
There are several gems to help you build SEO friendly URLs. I am not recommending a particular gem, but wanted to include them for completeness.