見出し画像

deviseのコントローラをカスタマイズしてウィザード形式の新規登録画面を作成する

deviseをカスタマイズしてウィザード形式のユーザー新規登録画面を実装しました。

ユーザーの情報と、住所の情報を2ページに分けて記入させるようにしました。

ウィザード形式?

アプリのユーザー登録の際に、最初はアドレスやパスワード入力して、次のページで住所やら過去の経歴やら入力させられたことはありませんか?

あんな感じでユーザーに対話形式で操作を誘導していく機能です。

今回はユーザー情報、住所情報の必要なデータがすべてバリデーションクリアして保存できる状態になって一気に保存する仕組みで作っています。

deviseのインストール〜モデル生成

これは参考サイトでいっぱい紹介されているので割愛します。

モデルはUserモデルとAddressモデルを用意しておきます。

usersテーブルとaddressesテーブルにはhas_oneの関係があります。(usersが親)

コントローラ生成

以下コマンドを実行する。

$ rails g devise:controllers users

するとapp/controllers/usersにコントローラがいくつか生成される。

今回は新規登録なので、registrations_controller.rbをいじっていきます。

ルーティングの設定

続いてregistrations_controller.rbを利用するためのルーティングを設定します。

Rails.application.routes.draw do
 devise_for :users, controllers: {
   registrations: 'users/registrations'
 }
end

createアクションをカスタム

まずはユーザー情報をカスタムするためにcreateアクションをカスタマイズしていきます。

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.build_address
 render :new_address
end

protected

def configure_sign_up_params
  devise_parameter_sanitizer.permit(:sign_up, keys:
                        [:nickname,  :birthday, :first_name,:last_name])
end

上から。

@user = User.new(sign_up_params)

ストロングパラメータで取得したデータのuserオブジェクトを作成してます。

ここで疑問なのがストロングパラメータのメソッド名がconfigure_sign_up_paramsなのにsign_up_paramsで呼び出せている点。

きっとdeviseの仕様だと思いますが。。。

その後、userオブジェクトのバリデーションを確認します。

unless @user.valid?
  flash.now[:alert] = @user.errors.full_messages
  render :new and return
end

無事にバリデーション通ったらsessionにuserのデータをattributesメソッドを使用して保存します。

session["devise.regist_data"] = {user: @user.attributes}
session["devise.regist_data"][:user]["password"] = params[:user][:password]

ここで注意したいのがパスワードはattributesメソッドでは追加できないということ。

paramsから取得して別で追加してあげます。

その後、build_addressメソッドを使用して関連づけられたaddressオブジェクトを使用します。

@address = @user.build_address

build_addressメソッドはhas_oneのアソシエーションを設定すると使用できるようになります。関連づけのあるnewメソッドのようなものです。

Addressを保存する処理を記述

先ほどのcreateアクションでrender :new_addressと記述したためルーティングとアクション、ビューを用意する。create_addressも用意する。

ルーティング

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

コントローラ

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.build_address
  render :new_address
 end

def new_address
end

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.build_address(@address.attributes)
  @user.save
  sign_in(:user, @user)
  redirect_to root_path
end

protected

def configure_sign_up_params
  devise_parameter_sanitizer.permit(:sign_up, keys:
                       [:nickname,  :birthday, :first_name, :last_name])
end

def address_params
  params.require(:address).permit(:ship_family_name, :ship_first_name, :ship_family_name_kana,
                                 :ship_first_name_kana, :postal_code, :prefecture,
                                 :prefecture_id, :municipality, :block_number, :apartment_name,
                                 :phone_number)
end

さきほどsessionにuserオブジェクトのデータを保存したので、それを用いてcreate_addressアクションでもuserオブジェクトを生成します。

あとは先ほどとほぼほぼ同じで、addressオブジェクトのバリデーションを確認して、通ったら保存します。

ここで注意したいのが、addressesテーブルに外部キーを設定していると、バリデーションに引っかかってしまいます。

そのため、モデルでoptional: trueして外部キーにnullが入ることを許可しています。

class Address < ApplicationRecord
  belongs_to :user, optional: true
end

まとめ

意外と簡単にできました!

この記事が気に入ったらサポートをしてみませんか?