1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Rails】ウィザード形式でのユーザー新規登録機能実装

Last updated at Posted at 2020-05-22
1 / 2

最初に

ユーザー新規登録の際にdevise認証機能を使用して、ユーザーの情報(ユーザーモデル)と、ユーザーの連絡先の情報(addressモデル)を登録できるよにすることが目的であり、1ページでまとめて(fileds_forを使用しない)ではなくウィザード形式を用いて2ページに分割しての実装が難しかったため、備忘録として残します。

ウィザード形式とは

画面が切り替わりながら操作が進んでいくことで、具体的にはこんな感じ。
https://gyazo.com/c0b7e9d98fb66ad7da724a95a77e4438
ユーザー情報(メールアドレスなど)の登録が完了した際に次のページでユーザー情報に付随する住所などの情報をページを切り替えて表示して登録させている。これをウィザード形式と呼ぶ。らしい。

実装の流れ

ユーザー情報を入力させ、その情報を一旦sessionに保持させる。
次に住所の情報を入力させ、入力が完了したらsessionに保持したユーザー情報と住所の情報を各テーブルに保存させる。
ページが切り替わる際にバリデーションをかけられるため、各ページごとにテーブルを分ける必要があります。
例えばユーザー情報登録画面にaddressモデルの住所を登録する項目(カラム)があった場合、userモデルではpermitされていないため、登録が進まないことになってしまう。1ページに1モデル対応させるようにする。

手順

1.deviseのコントローラーをカスタムできるようにする。
deviseのデフォルトの状態だとコントローラを作成していなくてもdevise/registrationsコントローラーが起動して、view/regstrations/new.html.hamlにルーティングが敷かれている。(ルーティングはdevise_for: users)
これだとカスタムができないため、対応するコントローラーを作成する。

$  rails g devise:controllers users

これでusers/registrations_contoroller.rbが作成される。
https://gyazo.com/a8052ca6b6cc4bd1e19ff4e2465cff1b

2.deviseのルーティングを変更
deviseのルーティングを指定する。
スクリーンショット 2020-05-22 1.00.10.png

この記述でdeviseのルーティングの指定が完了。
devise/registrationsコントローラー → users/registrationsコントローラー
にルーティングを変更することができた。
スクリーンショット 2020-05-22 1.13.26.png

3.app/controllers/application_controller.rbの修正
deviseのpermitはapplication_controller.rbで行う。

application_controller.rb
before_action :configure_permitted_parameters, if: :devise_controller?

  protected

  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: [:nickname, :family_name, :first_name, :family_name_kana, :first_name_kana, :birthday]) 
  end
 before_action :configure_permitted_parameters, if: :devise_controller?

この記述でdevise_controllerが動いた際に、後に記述したconfigure_permitted_parametersメソッドが始動する様にしている。

def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: [:nickname, :family_name, :first_name, :family_name_kana, :first_name_kana, :birthday]) 
  end

この記述により、Strong Parametersで追加したいカラムを追加。
デフォルトで:email、:password は許可されているので記載しなくてOK。

4.コントローラーの記述の変更
スクリーンショット 2020-05-22 1.35.15.png

先ほど作ったコントローラー。
クラス名意外消してOK。
あとはいつも通り、コントローラーに記載をしていく。

users/registrations_controller.rb
def new
    @user = User.new
  end

空のハッシュを生成して、

users/registrations_controller.rb
def new
    @user = User.new
  end

 def create
    @user = User.new(sign_up_params)
    unless @user.valid?
      flash.now[:alert] = @user.errors.full_messages
      render :new and return
    end
    session["devise.regist_data"] = {user: @user.attributes}
    session["devise.regist_data"][:user]["password"] = params[:user][:password]
    @address = @user.addresses.build
    render :new_address
  end

createアクションで実際にsessionsに一度保持して、address情報を登録するページに飛ぶように記載している。
最初これ何やってるか意味がわからなかったので3つに分けます。

・1ページ目(ユーザー情報)で入力した情報のバリデーションのチェック
・1ページ目で入力した情報をsessionに保持させること
・次ページの住所情報登録で使用するインスタンスを生成、次ページへ遷移すること

users/registrations_controller.rb
 @user = User.new(sign_up_params)
    unless @user.valid?
      flash.now[:alert] = @user.errors.full_messages
      render :new and return
    end

Userモデルのインスタンスを生成し、ユーザー情報登録画面から送られてきたパラメータをインスタンス変数@userに代入し、引数としてapplication_controller.rbで修正したsign_up_paramsを引数とする。
configure_permitted_parametersメソッドなのにsign_up_paramsで引っ張ってこれている。これは謎です。
もしかしたらdeviseが勝手に動いてくれているのかも。
インスタンス変数@userに対してvalid?メソッドを適用することで送られてきたパラメータが指定されたバリデーションに引っかかっているかどうかをチェック。falseになった場合は、エラーメッセージとともにnewアクションへrenderさせるような記述となっています。

users/registrations_controller.rb
 @user = User.new(sign_up_params)
    unless @user.valid?
      flash.now[:alert] = @user.errors.full_messages
      render :new and return
    end
   session["devise.regist_data"] = {user: @user.attributes}
    session["devise.regist_data"][:user]["password"] = params[:user][:password]

end以下の2文でユーザー情報登録画面での情報をsessionに保持させています。
ウィザード形式上、次ページの入力が完了した段階で各モデルのデータをDBに反映させたいです。なのでここでsessionに保持させる必要があります。この2行ずば抜けて意味不明だったので細かく書きます。。(笑)
session["devise.regist_data"]に値を代入します。この時、sessionにハッシュの形で情報を保持させるために、attributesメソッドを使用して、@userの属性を全てデータとして整形しています。
ここで注意したいのがパスワードはattributesメソッドでは追加できないということ。
paramsから取得して別で追加してあげます。パスワードを再度sessionに代入する必要があり、session["devise.regist_data"][:user]["password"]に再代入している。らしい。全然わからん。(泣)

users/registrations_controller.rb
def new
    @user = User.new
  end

 def create
    @user = User.new(sign_up_params)
    unless @user.valid?
      flash.now[:alert] = @user.errors.full_messages
      render :new and return
    end
    session["devise.regist_data"] = {user: @user.attributes}
    session["devise.regist_data"][:user]["password"] = params[:user][:password]
    @address = @user.addresses.build
    render :new_address
  end

最後の2行で、次ページでユーザー情報に紐ずく住所情報を入力させるため、addresses.buildで生成したインスタンス@userに紐づくAddressモデルのインスタンスを生成して@addressに代入。
住所情報を登録させるページを表示するためnew_addressアクションのビューへrenderしています。

5.new_addressへのルーティング指定と対応するビューを移動。

config/routes.rb
Rails.application.routes.draw do
  devise_for :users, controllers: {
    registrations: 'users/registrations'
  }
  devise_scope :user do
    get 'addresses', to: 'users/registrations#new_address'
    post 'addresses', to: 'users/registrations#create_address'
  end
end

devise_scope :user do~endでルーティングを指定。これで#new_addressと#create_addressが使用できるようになる。
対応するnew_address.html.hamlファイルは、
app/views/devise/registrations/下に移動。これでusers/registrationsのアクションでビューが表示される。

6.create_addressアクションでユーザー情報とプロフィール情報をテーブルに保存。

registrations_controller.rb
def create_address
    @user = User.new(session["devise.regist_data"]["user"])
    @address = Address.new(address_params)
    unless @address.valid?
      flash.now[:alert] = @address.errors.full_messages
      render :new_address and return
    end
    @user.addresses.build(@address.attributes)
    @user.save
    session["devise.regist_data"]["user"].clear
    sign_in(:user, @user)
    redirect_to root_path
end

private
  def address_params
    params.require(:address).permit(:post_code, :prefectures, :city, :block, :building, :phone_number)  
  end 

さきほどsessionにuserオブジェクトのデータを保存したので、create_addressアクションでもuserオブジェクトを生成して@userに代入。2行目でprivateメソッド下の引数で address_paramsメソッドを引数としてAddressモデルを生成、@addressに代入。
あとは先ほどと同じで、addressオブジェクトのバリデーションを確認して、通ったら保存。
addressesテーブルに外部キーを設定していると、バリデーションに引っかかるので、モデルでoptional: trueを追加。

address.rb
class Address < ApplicationRecord
  belongs_to :user, optional: true
end

optional: trueは外部キーにnullが入ることを許可してくれるオプション。これでバリデーションに引っ掛からなくなる。
その後はuserモデルに紐付いたaddressモデルという記述を追加。
@userをsaveして、sessionをclearメソッドを使用して削除。
まだ登録できただけなので、sign_inメソッドでサインイン。

あとはcreate_address.html.hamlが動くかを確認して、正常に動けばウィザード形式の実装完了です!

##参考文献

https://satoryu.hatenablog.com/entry/antipattern_in_designing_models_for_wizard
https://note.com/vixer93/n/nac92cc4c0983

1
2
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
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?