Reputation: 28783
In my Rails app I have posts and they have a route like so:
match '/posts/:id' => 'posts#show', :via => :get, :as => :post
In my model I have:
def to_param
"#{id}/#{slug}"
end
So that I can have a pretty URL like:
/posts/1/This-is-a-post
However the first issue I have is that the /
between the id
and slug
gets encoded! So I end up with: /posts/1%2FThis-is-a-post
The second issue I have, is that it doesn't matter what I put as the slug, it will ALWAYS show the post. While this isn't bad, it means the URL integrity is lost as the same post can have many variations.
What I'd like to do is make it work like on Stack Overflow, where if a user hits any of these URLs
/posts/1
/posts/1/This-is-a
/posts/1/this_is-A-PoSt
/posts/1/sifiusfheud
it will auto-redirect to
/posts/1/This-is-a-post
How can I do this in Rails? And can I do it using the to_param
? Or will I have to do something custom?
Upvotes: 1
Views: 716
Reputation: 28783
This is what I have come up with (inspired by Iceman's comments).
Two routes:
match '/posts/:id' => 'posts#show', :via => :get, :as => :post
match '/posts/:id/:slug' => 'posts#show', :via => :get, :as => :post_with_slug
And then use the post_with_slug
for the links:
<%= link_to post.title, post_with_slug_path(id: post.id, slug: post.slug) %>
But then took it further with a helper:
def post_with_slug(post)
"/posts/#{[post.id, post.slug].join('/')}"
end
So I could just pass the object:
<%= link_to post.title, post_with_slug(post) %>
Finally redirect if the slug param doesn't match.
def show
@post = Post.find(params[:id])
redirect_to post_with_slug_path(id: @post.id, slug: @post.slug), :status => 301 if @post.slug != params[:slug]
end
This looks to work really well, and even handles if the slash between the slug and ID is missing or different and always redirects the user to the correct url. I added a 301 status, just in case a user had used in an incorrect or old slug for SEO purposes.
Upvotes: 0
Reputation: 14890
How about having a route like this?
get '/posts/:id/:slug' => 'posts#show', as: :post_with_slug
and then use it in a view like
= link_to 'Post...', post_with_slug_path(:id @post.id, slug: @post.slug)
You could then do some checking of the spelling in the controller and route to the correct spelling of the post.
Then there is also the friendly_id
gem that is quite useful.
Upvotes: 1