やりたいこと
ユーザーのマイページやアカウント設定ページのルーティングをしたいです。
普通にRailsのresources
メソッドに任せてしまうと、my_app.com/users/:id
てな具合になってしまいます。
でも、ナウいQiitaとかnoteとかだとこんな感じになっています。
https://qiita.com/kakudaisuke
https://qiita.com/settings/account
https://note.com/kakudaisuke
https://note.com/settings/account
シンプルでかっこいいじゃないですか!
僕のアプリもこうしたい!
実装してみて、簡単にできたものの、細かいところの調整がやや面倒だったのでメモを。
環境
ruby 2.7.2
Rails 6.1.4.1
実装!
routes
Rails.application.routes.draw do
root 'posts#index'
#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# users
devise_for :users,
only: %i[session password registration],
controllers: { sessions: 'users/sessions',
password: 'users/passwords',
registrations: 'users/registrations' }
get '/users', to: 'users#index'
get '/settings', to: 'users#edit'
resources :users, param: :account_name, only: %i[show new create update destroy], path: '/'
end
(ユーザーに関わる部分なので、なんとなくdeviseのルーティングも併せて載せておきましたが、今回の実装とは全く関係ないので無視で。)
resources
メソッドのparam
やpath
というオプションを駆使すると簡単にやりたいことは大体できてしまいます。
いくつか注意点があります。
index
をresouces
に含めてしまうと、ルートmyapp.com/
がユーザー一覧になってしまうんですよね。ルートpathは別に設定したいので分けました。これ、順番が大事で、resources :usersを一番最後に書かないと、
/posts
、/users
などもaccount_name
だと判断されてしまいます。予期していないルーティングがされ「postsなんてユーザーいないぞ」とエラーに。それでも例えば
posts
なんていうアカウント名のユーザーがいたら、やはり、マイページではなく、posts一覧にルーティングしてしまいます。そこで、今回はモデルのバリデーションで使われたくない名前を定義してブロックしました↓。
reserved_words = %w(posts users my_app)
validates :account_name, exclusion: { in: reserved_words }
Model
param: :account_name
を有効にするには、モデル側でパラメーターにidじゃないのを使うよーと宣言してあげないといけません。
def to_param
account_name
end
ドキュメント→https://railsdoc.com/page/to_param
Views
こんな感じで設定してあげます。
<%= link_to User.model_name.human, '/users' %>
<%= link_to 'アカウント設定', '/settings' %>
<%= link_to 'マイページ', user_path(current_user) %>
<%#= postのユーザー名からユーザーページへのリンク %>
<%= link_to(post.user.account_name, "/#{post.user.account_name}") %>
最後に
これでとりま自分がやりたいことは実現できました!
ただ、もっとなんかスマートな方法がある気もしています。。
何かご指摘などあればバシバシよろしくお願いします。