pwz2000
pwz2000

Reputation: 1395

Changing user params to include their username

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

Answers (4)

Richard Peck
Richard Peck

Reputation: 76774

Slugged Routes

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:

  1. It "upgrades" the ActiveRecord find method to use the slug column, as well as the primary key
  2. 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

SteveTurczyn
SteveTurczyn

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

Xavier
Xavier

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

usha
usha

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

Related Questions