1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Rails】ウィザード形式のデータ登録について

Posted at

記事概要

Ruby on Railsにウィザード形式のユーザー管理機能を実装する方法について、まとめる

前提

  • Ruby on Railsでアプリケーションを作成している
  • Gemのdeviseを導入している

サンプルアプリ(GitHub)

ウィザード形式

対話するように順番に操作が進んでいく方式のこと
1ページで全ての情報を登録するのではなく、いくつかに分類した情報ごとにページを切り替えて登録を行う

特徴:どの情報を、どのページで登録しているのか分かるので、ユーザーが使いやすい

DB構造

Image from Gyazo

画面遷移図

Image from Gyazo
※ページが切り替わるときに都度バリデーションのチェックを行うため、ウィザードの各ページごとにテーブルを分ける必要がある

手順1(ログイン機能の実装)

Userモデルを作成

  1. Userモデルを作成する
    % rails g devise user
    
  2. nameカラムとageカラムを追加するため、マイグレーションファイルを編集する
    db/migrate/20************_devise_create_users.rb
    # frozen_string_literal: true
    
    class DeviseCreateUsers < ActiveRecord::Migration[7.1]
      def change
        create_table :users do |t|
          t.string :name, null: false # nameカラム
          t.integer :age, null: false # ageカラム
          ## Database authenticatable
          t.string :email,              null: false, default: ""
          t.string :encrypted_password, null: false, default: ""
          
          # 省略
    
  3. マイグレートを実行する
    % rails db:migrate
    
  4. Sequel Proを開いてテーブルが作成されているか確認し、nameカラムとageカラムが追加されていることを確認する
  5. nameカラムとageカラムを追加したので、Application Controllerを編集する
    app/controllers/application_controller.rb
    class ApplicationController < ActionController::Base
      before_action :configure_permitted_parameters, if: :devise_controller?
    
      private
      def configure_permitted_parameters
        devise_parameter_sanitizer.permit(:sign_up, keys: [:name, :age])
      end
    end
    
  6. nameカラムとageカラムにバリデーションを設定するため、Userモデルを編集する
    app/models/user.rb
    class User < ApplicationRecord
      # Include default devise modules. Others available are:
      # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
      devise :database_authenticatable, :registerable,
             :recoverable, :rememberable, :validatable
      validates :name, :age ,presence: true # nameカラムとageカラムにバリデーションを設定
    end
    

ビューを編集

  1. deviseのビューファイルを表示する
    % rails g devise:views
    
  2. app/views/devise/registrations/new.html.erbを編集し、新規登録画面にname項目とage項目を追加する
    <h2>Sign up</h2>
    
    <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
      <%= render "devise/shared/error_messages", resource: resource %>
    
      <%#= name項目 %>
      <div class="field">
        <%= f.label :name %><br />
        <%= f.text_field :name %>
      </div>
    
      <%#= age項目 %>
      <div class="field">
        <%= f.label :age %><br />
        <%= f.text_field :age %>
      </div>
    
      <div class="field">
        <%= f.label :email %><br />
        <%= f.email_field :email, autofocus: true, autocomplete: "email" %>
      </div>
      
      <%#= 省略 %>
    
  3. app/views/home/index.html.erbを編集し、ログイン画面と新規登録画面へのリンクを設定する
    <h1>トップページ</h1>
    
    <%#= ログイン画面と新規登録画面へのリンクを設定 %>
    <% if user_signed_in?%>
      <h2>ログインしています</h2>
      <%= link_to "ログアウト", destroy_user_session_path, data: { turbo_method: :delete } %>
    <% else %>
      <h2>ログインしていません</h2>
      <%= link_to "新規登録", new_user_registration_path %>
      <%= link_to "ログイン", new_user_session_path %>
    <% end %>
    

挙動を確認

  1. サーバーを起動する
  2. 新規登録し、ログイン状態に遷移できるかをブラウザで確認する

手順2(ウィザード形式の新規登録)

Addressモデルを作成

  1. Addressモデルを作成する
    % rails g model address
    
  2. postal_codeカラムとaddressカラムを追加するため、マイグレーションファイルを編集する
    db/migrate/20************_devise_create_addresses.rb
    class CreateAddresses < ActiveRecord::Migration[7.1]
      def change
        create_table :addresses do |t|
          t.integer :postal_code, null: false # postal_codeカラム
          t.text :address, null: false # addressカラム
          t.references :user, foreign_key: true, null: false # Userモデルと紐付け
          t.timestamps
        end
      end
    end
    
  3. マイグレートを実行する
    % rails db:migrate
    
  4. Sequel Proを開いてテーブルが作成されているか確認し、postal_codeカラムとaddressカラム、user_idカラムが追加されていることを確認する
  5. バリデーションなどを設定するため、Addressモデルを編集する
    app/models/address.rb
    class Address < ApplicationRecord
      belongs_to :user, optional: true # belongs_toの外部キーがnilであることを許可するオプション
      validates :postal_code, :address ,presence: true # postal_codeカラムとaddressカラムにバリデーションを設定
    end
    
    • Userモデルに対して、optional: trueを設定する理由
      addressesテーブルのuser_idが外部キーになる
      住所情報を入力した時点で、user_idには値が入っていない(sessionに保持しているが、DB未登録)ので、通常ではバリデーションに引っかかってしまう

      optional: trueを設定することにより、外部キーがnilであってもバリデーションを通過することができる

      DBに保存する段階ではuser_idが必ず存在してほしいので、マイグレーションファイルでは外部キーにnull: falseを設定する

Userモデルにアソシエーションを設定

  1. Userモデルに、アソシエーションを設定する
    app/models/user.rb
      # 省略
      
      validates :name, :age ,presence: true # nameカラムとageカラムにバリデーションを設定
      has_one :address # アソシエーション(一人のユーザー情報の中に住所の情報が紐づく)
    end
    

ルーティングを設定

  1. deviseのコントローラーを作成する

    % rails g devise:controllers users
    
  2. rails routesでルーティングを確認すると、devise管理下のコントローラーが紐付き先として表示される

                       Prefix Verb   URI Pattern              Controller#Action
     cancel_user_registration GET    /users/cancel(.:format)  devise/registrations#cancel
        new_user_registration GET    /users/sign_up(.:format) devise/registrations#new
       edit_user_registration GET    /users/edit(.:format)    devise/registrations#edit
            user_registration PATCH  /users(.:format)         devise/registrations#update
                              PUT    /users(.:format)         devise/registrations#update
                              DELETE /users(.:format)         devise/registrations#destroy
                              POST   /users(.:format)         devise/registrations#create
    
  3. どのコントローラーと紐づけるのかを明示するため、routes.rbを編集する

    config/routes.rb
    Rails.application.routes.draw do
      #devise_for :users
      #get 'home/index'
    
      # ユーザー新規登録に必要なregistrationsコントローラーのみに適用
      devise_for :users, controllers: {
        registrations: 'users/registrations'
      }
      root to: "home#index"
    end
    
  4. rails routesでルーティングを確認すると、devise管理下のコントローラーの紐付きが変更されている

                       Prefix Verb   URI Pattern              Controller#Action
     cancel_user_registration GET    /users/cancel(.:format)  users/registrations#cancel
        new_user_registration GET    /users/sign_up(.:format) users/registrations#new
       edit_user_registration GET    /users/edit(.:format)    users/registrations#edit
            user_registration PATCH  /users(.:format)         users/registrations#update
                              PUT    /users(.:format)         users/registrations#update
                              DELETE /users(.:format)         users/registrations#destroy
                              POST   /users(.:format)         users/registrations#create
    
  5. registrations_controller.rbを確認する
    クラス名をみると、Devise::RegistrationsControllerを継承していることがわかる。コメントアウトしている箇所はすでにDevise::RegistrationsControllerで定義されているもの

    users/registrations_controller.rbに同名のメソッドを定義することによって、コメントアウト部分を上書きすることができる。deviseの機能を継承しているので、定義されているメソッドの中のsuperは、deviseで使用できるメソッドをそのまま実行することができる

ユーザー情報を登録するためのフォームを作成

  1. ユーザー情報登録画面へ遷移するnewアクションを設定するため、registrations_controller.rbを編集する
    app/controllers/users/registrations_controller.rb
    # frozen_string_literal: true
    
    class Users::RegistrationsController < Devise::RegistrationsController
      # before_action :configure_sign_up_params, only: [:create]
      # before_action :configure_account_update_params, only: [:update]
    
      def new
        @user = User.new
      end
      
      # 省略
    
  2. newアクションに対応するビューを整えるため、app/views/devise/registrations/new.html.erbを編集する
    <!--<h2>Sign up</h2>-->
    <h2>ユーザー情報登録</h2>
    
    <%= form_for(@user, url: user_registration_path) do |f| %>
      <%= render "devise/shared/error_messages", resource: @user %>
    
      <%#= name項目 %>
      
      <%#= 中略 %>
    
      <%#= Nextボタン %>
      <div class="actions">
        <%= f.submit "Next" %>
      </div>
    <% end %>
    
    <%= render "devise/shared/links" %>
    

挙動を確認

  1. サーバーを起動する
  2. 新規登録ページにフォームが表示されることを確認する
    Image from Gyazo

ユーザー情報をsessionに保持

  1. registrations_controller.rbのcreateアクションを実装する
    app/controllers/users/registrations_controller.rb
    # frozen_string_literal: true
    
    class Users::RegistrationsController < Devise::RegistrationsController
      # before_action :configure_sign_up_params, only: [:create]
      # before_action :configure_account_update_params, only: [:update]
    
      def new
        @user = User.new
      end
    
      def create
        # ユーザー情報のバリデーションチェックをする
        @user = User.new(sign_up_params)
        unless @user.valid?
          render :new, status: :unprocessable_entity and return
        end
        # ユーザー情報をsessionに保持させる
        session["devise.regist_data"] = {user: @user.attributes}
        session["devise.regist_data"][:user]["password"] = params[:user][:password] # attributesメソッドでデータ整形をした際にパスワードの情報は含まれないため、パスワードを再度sessionに代入する必要がある
        # 次の住所情報登録で使用するインスタンスを生成する
        @address = @user.build_address
        # 住所情報登録ページへ遷移する
        render :new_address, status: :accepted
      end
      
      # 省略
    

住所情報を登録

  1. ルーティングを編集する
    config/routes.rb
      # 省略
      
      # 住所情報を登録させるページを表示するnew_addressアクションと住所情報を登録するcreate_addressアクションのルーティングを設定
      devise_scope :user do
        get 'addresses', to: 'users/registrations#new_address'
        post 'addresses', to: 'users/registrations#create_address'
      end
      root to: "home#index"
    end
    
  2. ビューapp/views/devise/registrations/new_address.html.erbを手動作成する
  3. app/views/devise/registrations/new_address.html.erbを編集する
    <h2>住所情報登録</h2>
    
    <%= form_for @address, data: { turbo: false } do |f| %>
      <%= render "devise/shared/error_messages", resource: @address %>
    
      <div class="field">
        <%= f.label :postal_code %><br />
        <%= f.text_field :postal_code %>
      </div>
    
      <div class="field">
        <%= f.label :address %><br />
        <%= f.text_field :address %>
      </div>
    
      <div class="actions">
        <%= f.submit "Sign up" %>
      </div>
    <% end %>
    
    <%= render "devise/shared/links" %>
    

挙動を確認

  1. サーバーを起動する
  2. 新規登録→ユーザー情報登録→住所登録と進み、上記で設定したフォームを閲覧することができるか確認する
    Image from Gyazo

住所情報をDB保存

  1. create_addressアクションで、ユーザー情報と住所情報全てをテーブルに保存するため、registrations_controller.rbを編集する
    app/controllers/users/registrations_controller.rb
    # 省略
    def create
      # 省略
    end
    
    def create_address
      # 住所情報のバリデーションチェック
      @user = User.new(session["devise.regist_data"]["user"])
      @address = Address.new(address_params)
      unless @address.valid?
        render :new_address, status: :unprocessable_entity and return
      end
      # バリデーションチェックが完了した情報とsessionで保持していた情報を合わせ、ユーザー情報として保存
      @user.build_address(@address.attributes)
      @user.save
      # sessionを削除
      session["devise.regist_data"]["user"].clear
      # ログインをする
      sign_in(:user, @user)
    end
    
    private
    def address_params
      params.require(:address).permit(:postal_code, :address)
    end
    
    # 省略
    
  2. app/views/devise/registrations/create_address.html.erbを手動作成する
  3. app/views/devise/registrations/create_address.html.erbを編集する
    <h2>登録が完了しました</h2>
    <%= link_to "トップへ戻る", root_path%>
    

挙動を確認

  1. サーバーを起動する
  2. 新規登録が完了できるか確認する
    Image from Gyazo
1
0
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
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?