Reputation: 1395
To view a user page on my app you have to enter their id /user/2
.
How can I make it so that it uses their username
in the params instead of id value for user show page? I would like it to be /user/username
or /username
.
Any help would be appreciated. Thanks!
Routes:
get 'signup' => 'users#new'
get 'login' => 'sessions#new'
get 'logout' => 'sessions#destroy'
get 'edit' => 'users#edit'
get "/profile/:id" => "users#show"
get "profile/:id/settings" => 'users#edit'
get 'settings/:id' => 'users#settings'
resources :users do
resources :messages do
post :new
collection do
get :askout
end
end
collection do
get :trashbin
post :empty_trash
end
end
Users controller:
def show
@user = User.find(params[:id])
end
Upvotes: 3
Views: 808
Reputation: 76774
What you're asking about is something called "slugged" routes.
These are when you use a slug
in your application to determine which objects to load, rather than using a primary_key
(usually an id
)
To handle this in Rails, you'll need to be able to support the slug
in the backend, and the best way to do this is to use the friendly_id
gem:
friendly_id
Id highly recommend using the friendly_id
gem for this:
#app/models/user.rb
Class User < ActiveRecord::Base
friendly_id :username, use: [:slugged, :finders]
end
The friendly_id
gem does 2 things extremely well:
- It "upgrades" the ActiveRecord
find
method to use theslug
column, as well as the primary key- It allows you to reference the slugged object directly in your link helpers
It basically means you can do this:
<%= link_to user.name, user %>
If using the friendly_id
gem, this will automatically populate with the slug
attribute of your table
Further, it allows you to do something else - it gives you the ability to treat the params[:id]
option in the backend in exactly the same way as before - providing the functionality you require.
Routes
You should clear up your routes as follows:
#config/routes.rb
get "/profile/:id" => "users#show"
get "profile/:id/settings" => 'users#edit'
get 'settings/:id' => 'users#settings'
resources :sessions, only: [:new, :destroy], path_names: { new: "login", destroy: "logout" }
resources :users, path_names: { new: "signup" } do
resources :messages do
post :new
collection do
get :askout
end
end
collection do
get :trashbin
post :empty_trash
end
end
Upvotes: 1
Reputation: 36860
Make a new attribute on your User model. You can call it what you want, but usually it's called "slug".
rails g migration AddSlugToUser slug:string
rake db:migrate
Store in it a "url friendly" version of the username. the parameterize method is good for that.
class User < ActiveRecord::Base
before_save :create_slug
def create_slug
self.slug = self.name.parameterize
end
In the same model create a to_param
method which will automatically include slug in links (instead of the user id)
def to_param
slug
end
Finally, where you do User.find
replace it with find_by_slug
@user = User.find_by_slug(params[:id])
Upvotes: -1
Reputation: 3513
In my experience the easiest way I've found to do this is to use the friendly_id gem. There is a to_param
method in ActiveRecord that you can set to define what a model's route id is going to be, but if the attribute you want to use is not already URL friendly it will become very complicated. Assuming your usernames contain no spaces or other URL "unfriendly" characters, you could do something like this:
class User < ActiveRecord::Base
...
def to_param
username
end
...
end
But make sure in your controller you then find users by their username.
class UsersController < ApplicationController
...
def set_user
@user = User.find_by(username: params[:id])
end
end
Note that if the value in to_param
is not EXACTLY what the value in the database is, finding your object again is going to be more difficult. For example, if you wanted to use name.parameterize
to set have URLs like /users/john-doe
when your actual name attribute is John Doe
, you'll have to find a way to consistently "deparameterize" your parameter. Or, you can create a new database column that contains a unique parameterized string of the attribute you want in the url (called a slug). That's why I use friendly_id. It handles your slugs semi-automatically.
As for routing your users to /username
, you have several options:
get ':id', to: 'users#show', as: 'show'
resources 'users', path: '/'
Just make sure you put these routes at the end of your routes file. That way if you try to get to your index
action on SomeOtherModelsController
by going to /some_other_model
it's not going to try to find you a user with username "some_other_model".
Upvotes: 2
Reputation: 29349
There are two options:
i. You can use the gem friendly_id
ii. Add to_param method to your user model and return username
class User < ActiveRecord::Base
def to_param
username
end
end
With this option you will have to replace
User.find(params[:id])
with
User.find_by_username(params[:id])
Upvotes: 1