追記
※ウィザードフォームの実装について、下記記事の実装のほうが簡単でわかりやすいです。
[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でデータの中身見ていくと何やってるか少し理解できた。
全部の手順とファイルの記述を書いていくのはちょっと大変すぎるので、
ポイントだと思ったとこだけ羅列。
手順ではないので注意。
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にいるのか、コントローラーで引っ張れるようにしている。
STEPS = %w(step1 step2 step3 step4).freeze
appの直下に「form_models」フォルダを新規作成する。
上記のコード部分でstepがどう進んでいくか記述している。
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が空でバリデーションに引っかかる。
その理由は下のコードを見ると少しわかる。
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の項目が隠されてしまう。
# 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