LoginSignup
88
113

More than 3 years have passed since last update.

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

Last updated at Posted at 2019-07-18

そもそもウィザード形式フォームとは

❌縦に長いフォーム画面
⭕️複数画面に順に遷移していくフォーム画面

(具体例)
Image from Gyazo
Image from Gyazo

実装方法

開発環境

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. ルーティングの設定
  4. フォームのマークアップ
  5. sessionを使ったデータの保持
  6. 最後にsessionのデータを使って、一括登録
  7. 一括登録後、自動的にサインインさせる

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

ルーティングの設定

必要なビューの数(遷移させたい数)だけ、ルーティングを設定してください。
(具体例)

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

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

# 保管方法
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

注意点

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

88
113
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
88
113