ユーザープロフィールなどを表示するときに、URLにはユーザーを識別するためのidが表示されます。
このidを、以下のようにアカウント名(ユーザー名)に変更して表示する方法についてまとめました。
http://localhost:3000/users/123
↓
http://localhost:3000/users/sample
開発環境
- Ruby 3.1.2
- Ruby on Rails 7.0.3
- Bootstrap 5.1.3
- M1 Macbook Air 2020
- mac OS Monterey (ver. 12.4)
- ターミナル bash (Rosetta 2 使用)
出来上がりのイメージ
当記事では、以下のような出来上がりイメージを想定して進めていきます。
- 新規アカウント登録直後は、プロフィールのURLには自動で生成されたアカウント名(ランダムな文字列)を表示する
- アカウント設定画面で任意のアカウント名に変更する
- 更新するとプロフィールのURLに設定したアカウント名が表示される
まず、ユーザーが新規アカウント登録した直後にログインしてプロフィール画面を開くと、以下のようにURLには自動で生成されたアカウント名が表示されるようにします。
次に、アカウント設定画面にて任意のアカウント名を変更できるようにします。
(アカウント設定画面を開くと、フォームにデフォルトのアカウント名が表示される)
任意のアカウント名を入力して更新。
更新後は、プロフィール画面およびアカウント設定画面のURLに任意で設定したアカウント名が表示されるようにします。
URLのidを任意のアカウント名に変更する流れ
- Userモデルにアカウント名を格納するカラム
account_name
を用意する - アカウント設定専用のコントローラー
account_controller
を用意する - account_nameカラムにランダムな文字列を保存するメソッドを用意する
- プロフィール画面およびアカウント設定画面のURLにユーザー
id
ではなくaccount_name
を表示するようルーティングの設定を行う - アカウント設定用のフォームを用意する
- アカウント設定画面にて任意のアカウント名に変更できるようにする(URLの表示も変わる)
今回は、プロフィール画面とアカウント設定画面を別々で作成するために、アカウント設定専用のコントローラーを追加して進めていきます。
Userモデルにaccount_nameカラムを追加
アカウント名を保存するためのaccount_name
カラムを追加します。
$ rails g migration add_account_name_to_users account_name:string
作成されたマイグレーションファイルを確認。
class AddAccountNameToUsers < ActiveRecord::Migration[7.0]
def change
add_column :users, :account_name, :string
end
end
内容に間違いがなければマイグレーションを実行します。
$ rails db:migrate
アカウント設定用のコントローラー(account_controller.rb)を追加
アカウント設定専用のコントローラーaccount_controller.rb
を追加します。
(必要となるテンプレートは編集画面用のedit
のみ)
$ rails g controller account edit
アカウントの編集はusers_controller.rbだけでも可能ですが、今回はプロフィール用の編集画面とアカウント用の編集画面を別々で用意したいので、アカウント編集用に新たにコントローラーを追加して管理することにしました。
account_nameカラムにランダムな文字列を保存するメソッドを追加
account_name
カラムにランダムな文字列(アカウント名)を保存するメソッドをuser.rb
に追加します。
class User < ApplicationRecord
before_create :set_account_name
・・・
private
# ランダムなアカウント名を返す
def set_account_name
while self.account_name.blank? || User.find_by(account_name: self.account_name).present? do
self.account_name = SecureRandom.base36
end
end
追加する文字列のメソッドはSecureRandom.base36
としました。
ユーザーが新規アカウント登録するタイミングでaccount_name
に文字列を追加したいので、先ほど追加したメソッドをbefore_create
でユーザー登録時に実行します。
URLにidではなくaccount_nameを表示するようルーティングを設定
通常、ユーザープロフィールのshowページのURLはユーザー識別のためにid
を表示する設定になっていますが、param: :account_name
を追記することでURLにid
の代わりにaccount_name
を表示できるようになります。
Rails.application.routes.draw do
・・・
get "signup", to: "users#new"
resources :users, except: [:new], param: :account_name
resources :account, only: [:edit, :update, :destroy], param: :account_name
end
users
とaccount
の全ルーティングは以下の通りです。
Prefix Verb URI Pattern Controller#Action
signup GET /signup(.:format) users#new
users GET /users(.:format) users#index
POST /users(.:format) users#create
edit_user GET /users/:account_name/edit(.:format) users#edit
user GET /users/:account_name(.:format) users#show
PATCH /users/:account_name(.:format) users#update
PUT /users/:account_name(.:format) users#update
DELETE /users/:account_name(.:format) users#destroy
edit_account GET /account/:account_name/edit(.:format) account#edit
account PATCH /account/:account_name(.:format) account#update
PUT /account/:account_name(.:format) account#update
DELETE /account/:account_name(.:format) account#destroy
また、URLにaccount_name
を表示するためのメソッドをuser.rb
に記述します。
class User < ApplicationRecord
before_create :set_account_name
・・・
#---- 追記 -----
def to_param
account_name
end
#---------------
private
# ランダムなアカウント名を返す
def set_account_name
while self.account_name.blank? || User.find_by(account_name: self.account_name).present? do
self.account_name = SecureRandom.base36
end
end
これで、プロフィール画面を開いたときに、URLにid
ではなくてaccount_name
(ランダムな文字列)が表示されればOKです。
account_nameカラムのバリデーションを追加
account_name
カラムのバリデーションをuser.rb
に追記します。
class User < ApplicationRecord
before_create :set_account_name
・・・
#----------------------- 追記 ----------------------------
VALID_ACCOUNT_NAME_REGEX = /\A[a-zA-Z0-9]+\z/ # 半角英数字のみ受け付ける正規表現
validates :account_name, uniqueness: true,
length: { minimum:3, maximum: 50 },
format: { with: VALID_ACCOUNT_NAME_REGEX },
on: :update # updateアクションにのみバリデーションを適用する
#---------------------------------------------------------
・・・
def to_param
account_name
end
private
# ランダムなアカウント名を返す
def set_account_name
while self.account_name.blank? || User.find_by(account_name: self.account_name).present? do
self.account_name = SecureRandom.base36
end
end
バリデーションにon: :update
と記述することで、ここで設定したバリデーションをupdate
アクションにのみ適用することができます。
(create
アクションにも適用してしまうと、バリデーションエラーでaccount_name
が自動で生成されなくなってしまうため)
account_name編集用のフォームを作成する
account_name編集用のテンプレートを作成します。
<h1 class="text-center">アカウント設定</h1>
<div class="row justify-content-center">
<!------------- url: account_path(params[:account_name]) を追記 ------------->
<%= form_with(scope: :account, model: @user, url: account_path(params[:account_name]), local: true, class: "mt-4 form-column-width") do |f| %>
・・・
<!----- アカウント名 ------>
<div class="mb-3 <%= @user.errors.include?(:account_name) ? "validation_errors" : "" %>">
<%= f.label "アカウント名変更", class: "form-label fw-bold"%>
<div class="input-group shadow-sm rounded">
<span class="input-group-text" id="basic-addon1">
<i class="bi bi-at"></i>
</span>
<%= f.text_field :account_name, class: "form-control", placeholder: "アカウント名を入力してください" %>
</div>
<div id="emailHelp" class="form-text text-primary">※3文字以上のユニークなアカウント名(半角英数字)を入力してください</div>
<%= render 'layouts/error_messages', obj: @user, key: :account_name %>
</div>
・・・
<% end %>
</div>
必要に応じてパスワードの入力欄を設けても良いでしょう。
最後に、account_controller.rb
のupdate
アクションに以下のように記述します。
class AccountController < ApplicationController
before_action :set_user
・・・
def update
if params[:account][:account_name].blank?
@user.errors.add(:account_name, :blank)
render "edit", status: :unprocessable_entity
elsif @user.update(user_params)
flash[:notice] = "アカウント情報が更新されました"
redirect_to @user
else
render "edit", status: :unprocessable_entity
end
end
・・・
private
def set_user
@user = User.find_by(account_name: params[:account_name])
end
def user_params
params.require(:account).permit(:account_name, ...)
end
end
これで、アカウント設定画面から任意のアカウント名に変更できるようになりました。
以上です。
コメント