railsのroutesのid以外にする方法

railsはdefaultのresourcesは:idになっています。

config/routes.rb

  resources :users

URIのパターンです。

                          Prefix Verb     URI Pattern                                            Controller#Action
                        new_user GET      /users/new(.:format)                                   users#new
                       edit_user GET      /users/:id/edit(.:format)                              users#edit
                            user GET      /users/:id(.:format)                                   users#show
                                 PATCH    /users/:id(.:format)                                   users#update
                                 PUT      /users/:id(.:format)                                   users#update
                                 DELETE   /users/:id(.:format)                                   users#destroy

ただ、id以外にしたい場合があります。

routesにparamをつけてid以外に変更する

その場合はparamsを付け足せばいいです。

config/routes.rb

  resources :users, param: :name
                           users GET      /users(.:format)                                       users#index
                                 POST     /users(.:format)                                       users#create
                        new_user GET      /users/new(.:format)                                   users#new
                       edit_user GET      /users/:name/edit(.:format)                            users#edit
                            user GET      /users/:name(.:format)                                 users#show
                                 PATCH    /users/:name(.:format)                                 users#update
                                 PUT      /users/:name(.:format)                                 users#update
                                 DELETE   /users/:name(.:format)                                 users#destroy

https://github.com/rails/rails/blob/b5db73076914e7103466bd76bec785cbcfe88875/actionpack/lib/action_dispatch/routing/mapper.rb#L469

linkを貼る場合に楽をする

そして、この時のlinkを貼る場合にdefaultでnameにしたい場合があります。

@user = User.first
user_path(@user)

通常だと、この場合はidになるので、/users/1になります。

これをnameに変更します。

model/User.rb

def to_param
  name
end

これで/users/fooになります。

注意点

ただし、これをやる場合はnameがuniqにしておけないとバグの温床になります。 例えば、nameにfooが二人いた場合です。 だいたいcontrollerに書くのは下記のようなコードだと思います。

def show
  @user = User.find_by(params[:name])
  ...
end

find_byは最初の一つしか取得しないので、二つ以上あると予期せぬ挙動になります。

    def find_by(arg, *args)
      where(arg, *args).take
    rescue ::RangeError
      nil
    end

https://github.com/rails/rails/blob/c6f12715f1887c06f778a32330f59822ca77df20/activerecord/lib/active_record/relation/finder_methods.rb#L77

そのため、DB/モデルにuniq制約をつけるのを忘れないようにしましょう。

以上です。