1.deviseの導入及びユーザーのデフォルトでのログインが可能になるまで
以下の手順で導入
gem 'devise'
rails g devise:install
任意のコントローラー(***)を用意して、ルーティングの設定をします。
この場合は、トップページに当たる部分のルーティングの設定をしています。
Rails.application.routes.draw do
root to: "***#index"
end
rails g controller ***
今回はindexアクションにおいて、特にモデルとのやり取りなどは行わないので、コントローラ内の記述はしません。(任意でトップページのビューは作成したものを使用)
次いでdeviseにおけるデフォルトのログイン機能を実装します。
rails g devise user
今回は、deviseのデフォルトで用意されているemailとpasswordを最初のビュー場で登録させてから、次のページにてuserのprofileを登録するようにしたいと思います。
その為、そのまま下記のコマンドを実行していきます。
rails db:migrate
※ただし、今回とは異なりuserモデルの方に名前等を追加したい場合には、下記のコマンド実行前にマイグレーションファイルの編集をしてカラムの追加の必要性が発生します。
その場合には下記のようにコントローラー及びモデルのバリデーションの記述を一部追加する必要性がある
class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:追加カラム名, :追加カラム名])
end
end
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
validates :追加カラム名, :追加カラム名 ,presence: true
end
上記を任意の状況に応じて実行後、追加したカラムを入力できるように、新規登録画面のビューを編集する必要があります。デフォルトではdeviseのビューファイルは隠れているので、以下のコマンドを実行します。
rails g devise:views
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= render "devise/shared/error_messages", resource: resource %>
<div class="field">
<%= f.label :追加カラム名 %><br />
<%= f.text_field :追加カラム名 %>
</div>
<div class="field">
<%= f.label :追加カラム名 %><br />
<%= f.number_field :追加カラム名 %>
</div>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
</div>
<%# 以下省略 %>
※追加するカラムがない場合にはrails db:migrateを実行後にここまでの操作を省略して、以下から続きを実行
任意のビューに下記の記述を追加してログインしていない場合には新規登録またはログインを実行させる画面に移行させる記述を追記していきます。
<% if user_signed_in?%>
<h2>ログインしています</h2>
<%= link_to "ログアウト", destroy_user_session_path, method: :delete %>
<% else %>
<h2>ログインしていません</h2>
<%= link_to "新規登録", new_user_registration_path %>
<%= link_to "ログイン", new_user_session_path %>
<% end %>
2.ウィザード形式でのユーザーの新規登録を実行出来るようにする
今回やっていくこととしては、ユーザー登録の画面でユーザー情報を入力させ、それをsessionに保持させておきます。そして次のプロフィール情報を登録する画面で名前や年齢、性別を入力させ、最後のステップでsessionに保持していたユーザー情報と、それに関連するプロフィール情報をテーブルに保存する流れで行なっていきます。
まず、まだ作成していないプロフィールモデルの作成を実行します。
rails g model profile
次いで、Userモデルとのリレーションのために、外部キーとしてuser_idが入るようにして、コマンドを実行します。
class CreateProfiles < ActiveRecord::Migration[5.2]
def change
create_table :profiles do |t|
t.string :name, null: false
t.integer :age, null: false
t.string :gender, null: false
t.references :user
t.timestamps
end
end
end
rails db:migrate
マイグレートを実行後、テーブルやカラム作成されているか確認して、モデルのバリデーションおよびアソシエーションを設定します。
Userモデルに対してoptional: trueを設けています。optional: trueは外部キーがnullであることを許可するオプションです。
同様にUserモデルについてもアソシエーションを設定します。
class Profile < ApplicationRecord
belongs_to :user, optional: true
validates :name, :age, :gender ,presence: true
end
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_one :profile
end
次いでdeviseのコントローラを作成し、編集できる形にします。
rails g devise:controllers users
現状rake routesを行うとdevise管理化のコントローラーが呼ばれてしまっているということが確認できます。
そこで以下のようにroutes.rbを編集して、どのコントローラを参照するのか明示してあげます。
Rails.application.routes.draw do
devise_for :users, controllers: {
registrations: 'users/registrations',
}
root to: "***#index"
end
編集後に再度rake routesをすると、参照するコントローラが変更されていることが確認できます。(今回はユーザ新規登録に必要なregistrationsコントローラのみに適用)
そしてここからの流れを再度確認すると
●userモデルのインスタンスの作成
●バリデーションチェックしたユーザー情報をsessionに保持してprofileモデルのインスタンスの生成
●バリデーションチェックしたバリデーションチェックしたプロフィール情報とsessionで保持していたユーザー情報を保存
という流れで実装していく必要があります。
userモデルのインスタンスの作成
class Users::RegistrationsController < Devise::RegistrationsController
# 省略
def new
@user = User.new
end
# 省略
end
newアクションに対応するフォーム形成
<h2>ユーザー情報登録</h2>
<%= form_for(@user, url: user_registration_path) do |f| %>
<%= render "devise/shared/error_messages", resource: @user %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
</div>
<div class="field">
<%= f.label :password %>
<% if @minimum_password_length %>
<em>(<%= @minimum_password_length %> characters minimum)</em>
<% end %><br />
<%= f.password_field :password, autocomplete: "new-password" %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "new-password" %>
</div>
<div class="actions">
<%= f.submit "Next" %>
</div>
<% end %>
<%= render "devise/shared/links" %>
上記で元のところからの変更点は、resourceという文字列部分(このdeviseのログイン機能で実装されるモデルが入るresourceとは、仮引数のようなもので実装する状況に応じたモデル名に変更する必要がありそうです)やボタンをnextにするなど細かい部分を修正しています。
変更後にビューが反映されているかを確認します。
確認後はユーザー登録フォーム上で"Next"をクリックすると、次の情報を登録するページに遷移しますが、その前にcreateアクション内で追記する必要があるので下記のように編集を実施します。
createアクションの編集
class Users::RegistrationsController < Devise::RegistrationsController
before_action :configure_sign_up_params, only: [:create]
# 省略
def create
@user = User.new(sign_up_params)
unless @user.valid?
flash.now[:alert] = @user.errors.full_messages
render :new and return
end
session["devise.regist_data"] = {user: @user.attributes}
session["devise.regist_data"][:user]["password"] = params[:user][:password]
@profile = @user.build_profile
render :new_profiles
end
# 省略
protected
def configure_sign_up_params
devise_parameter_sanitizer.permit(:sign_up, keys: [:attribute])
end
end
上記の編集によってどのようになったかを項目ごとに確認していきます。
1ページ目で入力した情報のバリデーションチェック
まず、Userモデルのインスタンスを生成し、最初の画面から送られてきたパラメータをインスタンス変数@userに代入します。そのインスタンス変数に対してvalid?メソッドを適用することで送られてきたパラメータが指定されたバリデーションに違反しないかどうかチェックしています。falseになった場合は、エラーメッセージとともにnewアクションへrenderさせます。
1ページで入力した情報をsessionに保持させること
次いで入力した情報をsessionに保持させます。
今回のようにウィザード形式にする場合には最後のページまで遷移した後に保存するというようにするために、今回はsessionという機能を用います。
バリデーションチェックが完了したら、session["devise.regist_data"]に値を代入します。この時、sessionにハッシュオブジェクトの形で情報を保持させるために、attributesメソッドを用いてデータを整形しています。また、paramsの中にはパスワードの情報は含まれていますが、attributesメソッドでデータ整形をした際にパスワードの情報は含まれていません。そこで、パスワードを再度sessionに代入する必要があります。
次画面にてプロフィール情報登録で使用するインスタンスを生成、当該ページへ遷移すること
次画面では、このユーザーモデルに紐づくプロフィール情報を入力させるため、該当するインスタンスを生成しておく必要があります。そのために、build_profileで今回生成したインスタンス@userに紐づくProfileモデルのインスタンスを生成します。ここで生成したProfileモデルのインスタンスは、@profileというインスタンス変数に代入します。そして、プロフィール情を登録させるページを表示するnew_profileアクションのビューへrenderしています。
次にnew_profileアクションとcreate_profileアクションのルーティングを設定します。
Rails.application.routes.draw do
devise_for :users, controllers: {
registrations: 'users/registrations'
}
devise_scope :user do
get 'profiles', to: 'users/registrations#new_profile'
post 'profiles', to: 'users/registrations#create_profile'
end
root to: "***#index"
end
<h2>プロフィール情報登録</h2>
<%= form_for @profile do |f| %>
<%= render "devise/shared/error_messages", resource: @profile %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :age %><br />
<%= f.text_field :age %>
</div>
<div class="field">
<%= f.label :gender %><br />
<%= f.text_field :gender %>
</div>
<div class="actions">
<%= f.submit "Sign up" %>
</div>
<% end %>
<%= render "devise/shared/links" %>
ルーティング先のビューも下記のように作成して、画面遷移の確認をします。
Rails.application.routes.draw do
devise_for :users, controllers: {
registrations: 'users/registrations'
}
devise_scope :user do
get 'profiles', to: 'users/registrations#new_profile'
post 'profiles', to: 'users/registrations#create_profile'
end
root to: "***#index"
end
最後にcreate_profileアクションで、ユーザー情報とプロフィール情報全てをテーブルに保存するように実装します。
create_profileアクションの再編集
#省略
def create_profile
@user = User.new(session["devise.regist_data"]["user"])
@profile = Profile.new(profile_params)
unless @profile.valid?
flash.now[:alert] = @profile.errors.full_messages
render :new_profile and return
end
@user.build_profile(@profile.attributes)
@user.save
sign_in(:user, @user)
end
#省略
protected
def profile_params
params.require(:profile).permit(:name, :age, :gender)
end
上記の編集によってどのようになったかを項目ごとに確認していきます。
2ページ目で入力したプロフィール情報のバリデーションチェック
最初の入力画面でのcreateアクションと同様に、valid?メソッドを用いて、バリデーションチェックを行います。
バリデーションチェックが完了した情報と、sessionで保持していた情報とあわせ、ユーザー情報として保存すること
build_profileを用いて送られてきたparamsを、保持していたsessionが含まれる@userに代入します。そしてsaveメソッドを用いてテーブルに保存します。
ログインをすること
ユーザーの新規登録ができても、ログインができているわけではありません。それをsign_inメソッドを利用してログイン作業を行いましょう。
上記が完了したらprofile_createに対応するビューを作成します。
<h2>登録が完了しました</h2>
<%= link_to "トップへ戻る", root_path%>
sessionを削除すること
このようにすることでユーザー登録画面をウィザード形式にすることが可能となります。
次の記事ではSNS認証をこちらに追加していきたいと思います。