Railsを使ったWebアプリの開発中、「マイページのURL末尾を、ユーザーIDじゃなくて"ユーザー名"にできたらかっけぇ...」と思い、実際にその機能をつけました。
マイページ(ユーザー詳細ページ:users/show.html.erb)のURL末尾をユーザーIDから「ユーザー名」に変更することは、とてつもなく重要です。なぜなら、ユーザー情報を共有する/されるときに、ユーザーを一瞬で識別できるようになるからです。実際、X(旧Twitter)やInstagram、そしてQiita、といった超人気WebサービスのマイページURLの末尾は、自身で設定したユーザー名になっています。
しかし、問題点があります。それは「何の追記もないまま(=リソースフルにしたがったまま)usersのshowアクションをルーティングに設定すると、マイページのURL末尾がユーザーIDになってしまう」という問題です。
そこで、「マイページのURL末尾をユーザーIDから"ユーザー名"に変更する」ためのRails実装手順をまとめました。
マイページのURL末尾をユーザーIDから"ユーザー名"に変更する手順説明は、次の内容でまとめました。
- 開発環境
- 実装の準備
- 具体的な実装内容 《本編》
「開発環境」では、本記事で解説する機能が正しく挙動したことを実際に視認できた環境を整理しました。「実装の準備」では、マイページのURL末尾をユーザー名に変更する前段階で必要になるライブラリの導入・ルーティングの設定を整理しました。「具体的な実装内容 《本編》」では、ルーティング・コントローラー・ビューに分けて、本記事のテーマとなる実装を、なるべく噛み砕いて解説しました。
開発環境
Category | Value,Version |
---|---|
OS | MacOS Sonoma 14.1.2 |
Ruby | 3.2.0 |
Ruby on Rails | 7.0.8 |
Gem | devise |
*Gem:Ruby専用拡張機能(Ruby用ライブラリ)
実装の準備
マイページのURL末尾を「ユーザー名」にするために、必要な準備内容をまとめました。
- 【devise】 ユーザー新規登録時のユーザー名を半角英数字にする
- users へ showアクションの設定
【devise】 ユーザー新規登録時のユーザー名を半角英数字にする
まず、Railsでのユーザー管理機能を簡単に実現するために、『devise』というGemを、以下の手順でインストールします。
# Gemfile:アプリケーションで使用するGemの「名前」と「バージョン」の情報を管理するファイル
gem 'devise' # Gemfile末尾に、この一行を記述
# ターミナル上で、deviseを導入したいアプリケーションのディレクトリへ移動
% bundle install # Gemfile.lockというファイルでインストール済みのGem情報を記録するためのコマンド
# サーバーが起動中の場合、サーバーを停止
% rails s # インストールしたGemを反映させるために、サーバーを再起動
% rails g devise:install # deviseの設定ファイルを作成するためのコマンド
次に、以下の操作で User
モデルを作成します。
% rails g devise user
このコマンド操作で、app/modelsディレクトリに user.rb
というモデル用のファイルが生成されれば成功です。
usersテーブルに :user_name
カラムを用意し、NOT NULL制約を付与します。
class DeviseCreateUsers < ActiveRecord::Migration[7.0]
def change
create_table :users do |t|
t.string :user_name, null: false # NOT NULL制約を付与
t.string :email, null: false, default: ""
t.string :encrypted_password, null: false, default: ""
end
end
end
NOT NULL制約を付与しておくべき理由は、テーブルの属性値に空の値(NULL)が入らない(NOT)ように制限するためです。
続いて、「ユーザーを識別する」=「同じユーザー名が存在しないようにする」という目的を達成するために、 :user_name
カラムへ add_index
を使って一意性制約を付与します。
class DeviseCreateUsers < ActiveRecord::Migration[7.0]
def change
create_table :users do |t|
t.string :user_name, null: false
t.string :email, null: false, default: ""
t.string :encrypted_password, null: false, default: ""
end
add_index :users, :user_name, unique: true # 一意性制約を付与
add_index :users, :email, unique: true
add_index :users, :reset_password_token, unique: true
end
end
usersテーブルの設計が完了したので、マイグレーションを実行します。
% rails db:migrate
次に、Userモデルの :user_name
カラムに、「ユーザーはアカウント作成時にユーザー名を入力する際、半角英数字でないとアカウントを新規作成できない」というバリデーションを付与します。
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
validates :user_name, presence: true, uniqueness: true, format: { with: /\A[a-zA-Z0-9]+\z/ }
end
ユーザー名を半角英数字に限定する理由は、日本語だとURLの見た目が悪くなってしまう(可能性が高い)からです。
なぜ日本語だとURLの見た目が悪くなってしまうのかというと、UTF-8 エンコードをはじめとする技術によって、文字列が冗長になってしまうからです。
実際、GoogleはURL構造に対して、ASCIIに含まれない文字がURLに入る場合、UTF-8エンコードを使用することを推奨しています。
つまり、Google Chromeなどのブラウザに表示したいURLに日本語が含まれていた場合、その文字列は「日本語以外の・パッと見て意味不明な・長〜い」文字列に置き換えられてしまう、ということです。
置き換える目的は、HTTPリクエストの解釈をより正確におこなうためです。
...そもそも、マイページのURL末尾をユーザー名にする目的は、「ユーザー情報を共有する/されるときに、ユーザーを一瞬で識別できるようにするため」です。
なので、日本語など半角英数字以外でのユーザー名入力を許してしまうと「URLからユーザーを瞬時に識別することができない(できる方も少数いるかもですが)」ので、本末転倒になってしまいます...
したがって、ユーザー新規登録時のユーザー名を半角英数字に制限するバリデーションを準備しておくことは、大切です。
users へ showアクションの設定
マイページを表示するため、usersコントローラーにshowアクションを定義する必要があります。
なので、先にルーティングの設定をします。
Rails.application.routes.draw do
devise_for :users # deviseコマンドでUserモデルを作成したときに自動で追記された行
resources :users, only: :show
end
ただし、この時点ではブラウザのアドレスバーに表示されるURL末尾が /users/:id
つまり「~/users/1」のように、ユーザーIDになってしまいます(この様子は、resourcesを宣言したら自動的に構成されるため「リソースフル」と表現されます)
# rails routes というコマンドで表示可能
Prefix Verb URI Pattern Controller#Action
user GET /users/:id(.:format) users#show
ということで、この :id
部分を先ほど用意したユーザー名(カラム) :user_name
に変更します。
具体的な実装内容 《本編》
それでは、マイページのURL末尾をデフォルトの(リソースフルな)ユーザーIDからユーザー名に変更する方法を、次の順番で説明します。
- ルーティング
- コントローラー
- ビュー
ルーティング
まず、ルーティングに次の行を追加します。
Rails.application.routes.draw do
devise_for :users
resources :users, only: :show
get '/users/:user_name', to: 'users#show' # 追加
end
この追記内容は、ルーティング内で一番下に書いた方がいいかもしれません。理由は、一番下に書かないと、他のURLも同様に get '/users/:user_name'
と処理されてしまう可能性があるからです。
ここで、改めて rails routes
というコマンドを叩くと、/users/:user_name
のパスでリクエストした際に users_controller.rb
のshowアクションを実行するルーティングが設定できていることがわかります。
Prefix Verb URI Pattern Controller#Action
user GET /users/:id(.:format) users#show
GET /users/:user_name(.:format) users#show # ルーティングが追加された!
しかし、このままではURL末尾をユーザーIDにして直接打ち込んで遷移しようとすると、遷移できてしまう可能性があります。
コントローラー
ということで次に、コントローラーを実装します。
class UsersController < ApplicationController
def show
user = User.find_by(user_name: params[:id])
@user_name = user.user_name
end
end
user = User.find_by(user_name: params[:id])
では、ユーザーidをparamsで取得し、そのユーザーのレコード(DBの横列)の中から user_name:
カラム(DBの縦列)を見つけ、それを変数userに代入しています。
@user_name = user.user_name
では、user_name情報をインスタンス変数として取り出しています。
ビュー
最後に、マイページつまりusers/show.html.erb
を表示させるためのリンクを組んで完成です。
<div> <%= link_to 'マイページ', "/users/#{current_user.user_name}" %> </div>
current_userメソッドは、事前にdeviseを導入しておいた場合、使えるメソッドです。現在ログインしているユーザーの情報を取得できます。
オマケで、他人のユーザーページを表示させるためのリンクの組み方も紹介します。
<div> <%= link_to "#{user.user_name}さんのページ", "/users/#{user.user_name}" %> </div>
プログラミング学習のアウトプット、以上になります。
ご指摘あれば、お気軽にコメントください。
参考 + 感謝:【Rails】ユーザーページのURLをユーザーネームにする(twitter.com/hanamichi10みたいにする)