#そもそもウィザード形式フォームとは
❌縦に長いフォーム画面
⭕️複数画面に順に遷移していくフォーム画面
#実装方法
###開発環境
Ruby 2.5.1
Rails 5.2.3
###結論
以下の記事を参考にして実装しました
https://qiita.com/NT90957869/items/56ca4101d7ba37778076
https://qiita.com/NT90957869/items/614842934feff9812203
上記の記事との違いは、**Deviseとsessionを組合わせて実装(バリデーションはRailsのモデルに記載)**したことです。
記事では、Deviseが使えないとなっていますが、コントローラをDeviseから完全分離させて実装し、ログイン・ログアウト・カレントユーザーのデータ取得などはDeviseを利用するといういいとこ取りをしています。
sessionを用いることで、全ての情報をユーザーが入力した後に、一括でDBに保存することができます。
逆にいうと、途中でユーザーがフォームの入力を辞めた時には、入力データがDBに保存されないので、中途半端なデータが残らない仕様にできます。
###補足
他にも以下のような実装方法もあるようです
- gem wickedを利用する(ただし、画面遷移ごとにDBにデータが保存される模様)
- JSで画面を切り替える(つまり、画面遷移ではなく、見た目を変える)
#コード
下記の記事がわかりやすいです。
https://qiita.com/NT90957869/items/614842934feff9812203
今回は、全体の流れと、上記の記事と違う部分をピックアップして説明していきます。
- Deviseの導入
- Deviseとは関係のないコントローラの作成
- ルーティングの設定
- フォームのマークアップ
- sessionを使ったデータの保持
- 最後にsessionのデータを使って、一括登録
- 一括登録後、自動的にサインインさせる
###Deviseの導入
Gemfileに下記を記載
gem 'devise'
以下をコンソールで実行
$ bundle install #gemのインストール
$ rails g devise:install #deviseを使うのに必要なファイルの生成
$ rails g devise user #deviseでUserモデルの生成
マイグレーションファイルが生成されるので、Usersテーブルに必要なカラムを指定しマイグレートを実行。
※もともと記載されているdevise関連の記述は変更しなくて大丈夫です。
※passwordカラム、password_confirmationカラムは作成しないでください。左記の記述はビューのフォームで使用しますが、実際のパスワードはdeviseで暗号化され、encrypted_passwordカラムに自動的に保存されます。
(具体例)
# frozen_string_literal: true
class DeviseCreateUsers < ActiveRecord::Migration[5.2]
def change
create_table :users do |t|
## Database authenticatable
t.string :nickname, null: false
t.string :email, null: false, unique: true
t.string :last_name, null: false
t.string :first_name, null: false
t.string :last_name_kana, null: false
t.string :first_name_kana, null: false
t.integer :birthdate_year, null: false
t.integer :birthdate_month, null: false
t.integer :birthdate_day, null: false
t.integer :phone_number, null: false, unique: true
t.string :address_last_name, null: false
t.string :address_first_name, null: false
t.string :address_last_name_kana, null: false
t.string :address_first_name_kana, null: false
t.string :address_number, null: false
t.integer :address_prefecture, null: false, default: 0
t.string :address_name, null: false
t.string :address_block, null: false
t.string :address_building
t.integer :address_phone_number
t.text :introduce
t.string :encrypted_password, null: false, default: ""
## Recoverable
t.string :reset_password_token
t.datetime :reset_password_sent_at
## Rememberable
t.datetime :remember_created_at
## Trackable
# t.integer :sign_in_count, default: 0, null: false
# t.datetime :current_sign_in_at
# t.datetime :last_sign_in_at
# t.string :current_sign_in_ip
# t.string :last_sign_in_ip
## Confirmable
# t.string :confirmation_token
# t.datetime :confirmed_at
# t.datetime :confirmation_sent_at
# t.string :unconfirmed_email # Only if using reconfirmable
## Lockable
# t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
# t.string :unlock_token # Only if unlock strategy is :email or :both
# t.datetime :locked_at
t.timestamps null: false
end
add_index :users, :email, unique: true
add_index :users, :reset_password_token, unique: true
# add_index :users, :confirmation_token, unique: true
# add_index :users, :unlock_token, unique: true
end
end
$ bundle exec rake db:migrate
###Deviseとは関係のないコントローラの作成
今回は、signup_controller.rbを作成しました。
$ bundle exec rails g controller signup
###ルーティングの設定
必要なビューの数(遷移させたい数)だけ、ルーティングを設定してください。
(具体例)
resources :signup do
collection do
get 'step1'
get 'step2'
get 'step3'
get 'step4' # ここで、入力の全てが終了する
get 'done' # 登録完了後のページ
end
end
###フォームのマークアップ
遷移ページ分だけビューを用意してください。
e.g.) step1.html.haml, step2.html.haml, step3.html.haml, ...etc
各ページのフォームタグのurlで、上記で設定したルーティング(次のページへのパス)を記載します
(具体例)
= form_for @user, url: step2_signup_path, method: :get, html: {class: 'first-main__box'} do |f|
= f.text_field :nickname, placeholder: '例) メルカリ太郎'
= f.email_field :email, placeholder: 'PC・携帯どちらでも可'
= f.password_field :password, placeholder: '6文字以上'
= f.password_field :password_confirmation, placeholder: '6文字以上'
= form_for @user, url: step3_signup_path, method: :get, html: {class: 'second-wrapper__box'} do |f|
= f.text_field :last_name, placeholder: '例) 山田'
= f.text_field :first_name, placeholder: '例) 彩'
= f.text_field :last_name_kana, placeholder: '例) ヤマダ'
= f.text_field :first_name_kana, placeholder: '例) アヤ'
###sessionを使ったデータの保持
Railsではsessionという情報保管方法があります
Railsドキュメント
http://railsdoc.com/references/session
概略としては、キーバリュー形式で、任意のデータを保管できる仕組みです。
# 保管方法
session[キー] = 値
# 具体例
session[:nickname] = "hoge" # キー":nickname"にバリュー"hoge"を保管
session[:nickname]
-> "hoge"
sessionを利用して、各ページのデータを遷移ごとに保管していきます。
やるべきことは、以下の2つです。
- ストロングパラメータの使用
- 各アクションでsessionにparamsデータを保管
private
# 許可するキーを設定します
def user_params
params.require(:user).permit(
:nickname,
:email,
:password,
:password_confirmation,
:last_name,
:first_name,
:last_name_kana,
:first_name_kana,
~省略~
)
end
class SignupController < ApplicationController
# 各アクションごとに新規インスタンスを作成します
# 各アクションごとに、遷移元のページのデータをsessionに保管していきます
def step1
@user = User.new # 新規インスタンス作成
end
def step2
# step1で入力された値をsessionに保存
session[:nickname] = user_params[:nickname]
session[:email] = user_params[:email]
session[:password] = user_params[:password]
session[:password_confirmation] = user_params[:password_confirmation]
@user = User.new # 新規インスタンス作成
end
def step3
# step2で入力された値をsessionに保存
session[:last_name] = user_params[:last_name]
session[:first_name] = user_params[:first_name]
session[:last_name_kana] = user_params[:last_name_kana]
session[:first_name_kana] = user_params[:first_name_kana]
@user = User.new # 新規インスタンス作成
end
~遷移ページ分繰り返す~
###最後にsessionのデータを使って、一括登録
データの入力が全て終了するページで、フォームのurlをcreateアクションが実行されるように記載します。
(具体例)
= form_for @user, url: signup_index_path, method: :post, html: {class: 'forth-main-wrapper__box'} do |f|
createアクション内で、今まで保管したsessionのデータを渡し、DBに保存します。
def create
@user = User.new(
nickname: session[:nickname], # sessionに保存された値をインスタンスに渡す
email: session[:email],
password: session[:password],
password_confirmation: session[:password_confirmation],
last_name: session[:last_name],
first_name: session[:first_name],
last_name_kana: session[:last_name_kana],
first_name_kana: session[:first_name_kana],
~省略~
)
if @user.save
# ログインするための情報を保管
session[:id] = @user.id
redirect_to done_signup_index_path
else
render '/signup/registration'
end
end
###一括登録後、自動的にサインインさせる
deviseのメソッドsign_inを活用し、createアクションで作成・保存したデータのidを用いてサインインさせます。
def done
sign_in User.find(session[:id]) unless user_signed_in?
end
#まとめ
以上の方法を使えば、問題なく登録できるかと思います。
deviseを導入しているので、サインインやログアウト、ログインユーザーのデータ取得、サインイン中かどうかの判断など、全てdeviseのメソッドを活用できる点がいいところです。
他テーブルも含めた一括登録もできます。以下の記事を参考にしてみてください。
https://qiita.com/NT90957869/items/614842934feff9812203
#注意点
上記の実装のままでは、ページ遷移ごとにバリデーションをチェックできません。
ページ遷移前のパリデーションのチェック方法はまた別記事にまとめたいと思います。