LoginSignup
7
8

More than 3 years have passed since last update.

[Rails]新規登録画面のウィザードフォーム実装(sessionでデータ保持する)

Last updated at Posted at 2019-06-03

追記

※ウィザードフォームの実装について、下記記事の実装のほうが簡単でわかりやすいです。
[Rails]ウィザードフォーム実装でfields_forを使って複数のテーブルに保存する

はじめに

環境:Rails 5.2.3

ユーザー新規登録画面の実装にて、ウィザードフォームで実装を目指す。
入力データをsessionで保持し、最後のページでデータベースに登録する。

事前に調べてわかったこと

・deviseは使えない
 つまりサインアップ、ログイン、ログアウト機能はdeviseを使わずに実装する必要がある

実装方法

Developing a wizard or multi-steps forms in Rails
GitHub - nicolasblanco/wizard_app
上のサイト見て下のgithubのコードまんまコピーして必要なとこだけ書き換えればOK。
いやマジで。

上のgithubをローカルに落として実際に動かして見たり、binding.pryでデータの中身見ていくと何やってるか少し理解できた。
全部の手順とファイルの記述を書いていくのはちょっと大変すぎるので、
ポイントだと思ったとこだけ羅列。
手順ではないので注意。

routes.rb
resource :wizard do
    get :step1
    get :step2
    get :step3
    get :step4

    post :validate_step
  end

stepさせるファイルの分、ルーティングに記述する

ビューファイル
<%= simple_form_for @user_wizard, as: :user_wizard, url: validate_step_wizard_path do |f| %>
  <%= hidden_field_tag :current_step, 'step1' %>

フォームの中に「= hidden_field_tag :current_step, 'step1'」を記述することで、
今どのstepにいるのか、コントローラーで引っ張れるようにしている。

form_models/wizard/user.rb
STEPS = %w(step1 step2 step3 step4).freeze

appの直下に「form_models」フォルダを新規作成する。
上記のコード部分でstepがどう進んでいくか記述している。

wizards_controller.rb
def validate_step
    current_step = params[:current_step] # 現在のステップをビューから取得

    @user_wizard = wizard_user_for_step(current_step)
    @user_wizard.user.attributes = user_wizard_params # formから受け取った値を取得
    session[:user_attributes] = @user_wizard.user.attributes # formから受け取った値をsessionに代入
    if @user_wizard.valid?
      next_step = wizard_user_next_step(current_step) 
      create and return unless next_step # 次のstepが無ければcreateへ

      redirect_to action: next_step
    else
      render current_step
    end
  end

  def create
    if @user_wizard.user.save # formから受け取った値をデータベースに保存
      session[:user_attributes] = nil # sessionの中身を空にリセット
      redirect_to root_path, notice: 'User succesfully created!'
    else
      # 保存に失敗したら最初のstepへ
      redirect_to({ action: Wizard::User::STEPS.first }, alert: 'There were a problem when creating the user.')
    end
  end

少し解説をコメントで書いておいた。
だいたいこんな感じの処理をしていると思う。

補足

最初に説明した「deviseが使えない」というのは、deviseがインストールされていると、
上記のsave直前の@user_wizard.userにpasswordの項目が存在しなくなるということ。
ちなみに@user_wizard.user.attributesは入力値の全ての属性を取得するため、
passwordも入っている。
つまりpasswordの項目自体はformから取得できているが、実際にDBに保存する@user_wizard.userにはpasswordの項目が存在しないためにpasswordが空でバリデーションに引っかかる。
その理由は下のコードを見ると少しわかる。

wizards_controller.rb
def wizard_user_for_step(step)
    raise InvalidStep unless step.in?(Wizard::User::STEPS)

    "Wizard::User::#{step.camelize}".constantize.new(session[:user_attributes])
  end

この記述を簡単に言うと良くある「User.new」で新しいインスタンスを作成しているのだが、
deviseがあるとこのインスタンスからpasswordの項目が隠されてしまう。

例)DBのカラムがnameとpasswordの時
# devise無しの場合
user = User.find(1)
 => name: ○○, password: ○○

# devise有りの場合
user = User.find(1)
 => name: ○○

極端に言うとこんな感じになる。

おそらくはセキュリティのためにdeviseが別で保持しているのだと思われるが、
paramsで取得したパスワードをインスタンスに引数として渡しても、インスタンス内の項目にpasswordが存在しないので、
DBに保存する際に「passwordが空です」というエラーが出る。
binding.pryでデータを追っていくとわかる。

なので、この方法でウィザードフォームを実装する場合にはdeviseは使わずに実装する必要がある。

 ※おそらくは何らかのやり方があるのだと思うが、現時点ではわからなかった。

参考

[Rails]1つのformを複数画面に分割して表示するwizard formを実装する
Developing a wizard or multi-steps forms in Rails
GitHub - nicolasblanco/wizard_app

7
8
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
7
8