Railsウィザード形式フォームで新規登録〜Devise+session〜

#そもそもウィザード形式フォームとは
❌縦に長いフォーム画面
⭕️複数画面に順に遷移していくフォーム画面

（具体例）
[![Image from Gyazo](https://i.gyazo.com/c4549ace589459e7e2f996f470ad4f3f.png)](https://gyazo.com/c4549ace589459e7e2f996f470ad4f3f)
[![Image from Gyazo](https://i.gyazo.com/70cd995b1b7331fc2335f927996da05c.png)](https://gyazo.com/70cd995b1b7331fc2335f927996da05c)

#実装方法
###開発環境
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

今回は、全体の流れと、上記の記事と違う部分をピックアップして説明していきます。

1. Deviseの導入
2. Deviseとは関係のないコントローラの作成
3. ルーティングの設定
2. フォームのマークアップ
3. sessionを使ったデータの保持
4. 最後にsessionのデータを使って、一括登録
5. 一括登録後、自動的にサインインさせる

###Deviseの導入
Gemfileに下記を記載

```.rb
gem 'devise'
```
以下をコンソールで実行

```.console
$ bundle install #gemのインストール
$ rails g devise:install #deviseを使うのに必要なファイルの生成
$ rails g devise user #deviseでUserモデルの生成
```
マイグレーションファイルが生成されるので、Usersテーブルに必要なカラムを指定しマイグレートを実行。
※もともと記載されているdevise関連の記述は変更しなくて大丈夫です。
※<font color="Red">passwordカラム、password_confirmationカラムは作成しないでください。</font>左記の記述はビューのフォームで使用しますが、実際のパスワードはdeviseで暗号化され、encrypted_passwordカラムに自動的に保存されます。
（具体例）

```.rb
# 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
```
```.console
$ bundle exec rake db:migrate
```
###Deviseとは関係のないコントローラの作成
今回は、*signup_controller.rb*を作成しました。

```.console
$ bundle exec rails g controller signup
```
###ルーティングの設定
必要なビューの数（遷移させたい数）だけ、ルーティングを設定してください。
（具体例）

```.routes.rb
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で、上記で設定したルーティング（次のページへのパス）を記載します
（具体例）

```.step1.html.haml
= 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文字以上'
```
```.step2.html.haml
= 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

概略としては、キーバリュー形式で、任意のデータを保管できる仕組みです。

```.rb
# 保管方法
session[キー] = 値
# 具体例
session[:nickname] = "hoge" # キー":nickname"にバリュー"hoge"を保管
session[:nickname]
-> "hoge"
```
sessionを利用して、各ページのデータを遷移ごとに保管していきます。
やるべきことは、以下の2つです。

1. ストロングパラメータの使用
2. 各アクションでsessionにparamsデータを保管

```signup_controller.rb
private
　# 許可するキーを設定します
  def user_params
    params.require(:user).permit(
      :nickname, 
      :email, 
      :password, 
      :password_confirmation, 
      :last_name, 
      :first_name, 
      :last_name_kana, 
      :first_name_kana, 
      ~省略~
  )
  end
```
```signup_controller.rb
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アクションが実行されるように記載します。
（具体例）

```step4.html.haml
= form_for @user, url: signup_index_path, method: :post, html: {class: 'forth-main-wrapper__box'} do |f|
```
createアクション内で、今まで保管したsessionのデータを渡し、DBに保存します。

```.signup_controller.rb
  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を用いてサインインさせます。

```.signup_controller.rb
    def done
      sign_in User.find(session[:id]) unless user_signed_in?
    end
```

#まとめ
以上の方法を使えば、問題なく登録できるかと思います。
deviseを導入しているので、サインインやログアウト、ログインユーザーのデータ取得、サインイン中かどうかの判断など、全てdeviseのメソッドを活用できる点がいいところです。

他テーブルも含めた一括登録もできます。以下の記事を参考にしてみてください。
https://qiita.com/NT90957869/items/614842934feff9812203

#注意点
上記の実装のままでは、ページ遷移ごとにバリデーションをチェックできません。
ページ遷移前のパリデーションのチェック方法はまた別記事にまとめたいと思います。

https://qiita.com/ATORA1992/items/c073620be3469bfa1f54