ウィザード形式でユーザ登録のフォームを作成してみた②-1　〜viewとcontroller〜

## 【この記事の説明】
現在、私はプログラミングスクールでプログラミング（主にRuby）の勉強をしています。
そして、その一環でチーム開発（メルカリもどきのサイト作成）を行っており、私はその中で記事のタイトルにあるように、ウィザード形式でユーザの登録ができるようにする作業を担当致しました。
又、スクールからの指示で、この登録フォームにはdeviseを使用するようにとのことでした。

この記事は、その担当作業についてまとめたものになります。
## 【作業】
ここから私が行った作業（そこで困ったこと）を記事として記載していこうと思います
※この記事では、viewとcontrollerについて記載したいと思います


### view、controllerの作成
本当はviewはview、controllerはcontrollerで記載したいのですが、この二つは密接に関わり合う為、別々に説明ができません。ですので、一緒に説明していきたいと思います
又、ここでは、view作成の細かいところの記載はしません
あくまでもユーザー登録に必要な部分のみの記載となります

では、以下にcontrollerとviewのコードを記載します
（前の記事で記載しましたが、ウィザード形式で3ページ分の登録ページを作成している為、viewは３つ記載します
詳しくは[ウィザード形式でユーザ登録のフォームを作成してみた](https://qiita.com/drafts/ed9b0e9b446866ee1d48/edit?resume=true)をご覧ください）

#### controller
```ruby:/app/controllers/users/registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController

  # GET /resource/sign_up
  def new
    super
  end
  
  def create
    @user = User.new(session["devise.regist_data"]["user"])
    @profileaddress = Profileaddress.new(session["profileaddress"])
    @deliveryaddress = Deliveryaddress.new(deliveryaddresses_params)
    unless @deliveryaddress.valid?
      flash.now[:alert] = @deliveryaddress.errors.full_messages
      render :new_deliveryaddresses and return
    end
    @user.build_profileaddress(@profileaddress.attributes)
    @user.build_deliveryaddress(@deliveryaddress.attributes)
    if @user.save
      session["devise.regist_data"]["user"].clear
      session["profileaddress"].clear
      sign_in(:user,@user)
      redirect_to root_path
    else
      render :new
    end
  end

  def new_profileaddresses
    @user = User.new(birthday_save)
    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]
    @profileaddress = @user.build_profileaddress
  end

  def new_deliveryaddresses
    user = User.new(session["devise.regist_data"]["user"])
    @profileaddress = Profileaddress.new(profileaddress_params)
    unless @profileaddress.valid?
      flash.now[:alert] = @profileaddress.errors.full_messages
      render :new_profileaddresses and return
    end
    user.build_profileaddress(@profileaddress.attributes)
    session["profileaddress"] = @profileaddress.attributes
    @deliveryaddress = user.build_deliveryaddress
  end

  private
    def birthday_save
      date = user_params
      year = date[:"birthday(1i)"]
      month = date[:"birthday(2i)"]
      day = date[:"birthday(3i)"]
      if year.empty? || month.empty? || day.empty?
        return
      end
      # 年月日別々できたものを結合して新しいDate型変数を作り,paramsに数字化したDataをくっつけました
      birthday = Date.new(year.to_i,month.to_i,day.to_i)
      params.require(:user).permit(:email,:password,:password_confirmation,:nickname,:family_name,:given_name,:family_name_kana,:given_name_kana).merge(birthday: birthday)
    end
    def user_params
      params.require(:user).permit(:email,:password,:password_confirmation,:nickname,:family_name,:given_name,:family_name_kana,:given_name_kana).merge("birthday(1i)": params[:birthday][:"birthday(1i)"]).merge("birthday(2i)": params[:birthday][:"birthday(2i)"]).merge("birthday(3i)": params[:birthday][:"birthday(3i)"])
      # paramsの中のカラム→:email,:password,:password_confirmation,:nickname,:family_name,:given_name,:family_name_kana,:given_name_kana,:birthday(1i),:birthday(2i),:birthday(3i)
    end
    def profileaddress_params
      params.require(:profileaddress).permit(:postal_code, :prefectures,:city,:address,:building)
    end
    def deliveryaddresses_params
      params.require(:deliveryaddress).permit(:family_name,:given_name,:family_name_kana,:given_name_kana,:postal_code,:prefectures,:city,:address,:building,:phone_number)
    end
end

```
#### view(1ページ目：ユーザ情報)
```ruby:/app/views/users/registrations/new.html.haml
.input-top
  = render partial: '/template/input-top'
  
  .login-main
    .login-main__title
      会員情報入力
    .login-main__content
      .login-main__content__form
        = form_with(model: @user, url: profileaddresses_path,local: true,method: :post) do |f|  

          .form-group
            ニックネーム
            %span.required
              必須
            %br
            .field
              = f.text_field :nickname, autofocus: true, autocomplete: "nickname",placeholder: "例）フリマ太郎",class: "default"            
              - if @user.errors.any?
                .error_explanation
                  %h2
                    - I18n.t("errors.messages.not_saved",                 |
                      count: resource.errors.count,                       |
                      resource: resource.class.model_name.human.downcase) |
                  %ul
                    - @user.errors.full_messages_for(:nickname).each do |message|
                      %li{style: "color:red"}
                        = message

          .form-group
            メールアドレス
            %span.required
              必須
            %br
            .field
              = f.text_field :email, autofocus: true, autocomplete: "email", placeholder: "PC・携帯どちらでも可", class:"default"         
              - if @user.errors.any?
                .error_explanation
                  %h2
                    - I18n.t("errors.messages.not_saved",                 |
                      count: resource.errors.count,                       |
                      resource: resource.class.model_name.human.downcase) |
                  %ul
                    - @user.errors.full_messages_for(:email).each do |message|
                      %li{style: "color:red"}
                        = message

          .form-group
            パスワード
            %span.required
              必須
            %br
            .field
              = f.password_field :password, autocomplete: "new-password", placeholder: "７文字以上",class: "default"
            %p.info ※英字と数字の両方を含めて設定してください
            - if @user.errors.any?
              .error_explanation
                %h2
                  - I18n.t("errors.messages.not_saved",                 |
                    count: resource.errors.count,                       |
                    resource: resource.class.model_name.human.downcase) |
                %ul
                  - @user.errors.full_messages_for(:password).each do |message|
                    %li{style: "color:red"}
                      = message

          .form-group 
            パスワード（確認用）
            %span.required
              必須
            %br
            .field
              = f.password_field :password_confirmation, autocomplete: "new-password",placeholder: "もう一度パスワードを入力してください",class:"default"
              - if @user.errors.any?
                .error_explanation
                  %h2
                    - I18n.t("errors.messages.not_saved",                 |
                      count: resource.errors.count,                       |
                      resource: resource.class.model_name.human.downcase) |
                  %ul
                    - @user.errors.full_messages_for(:password_confirmation).each do |message|
                      %li{style: "color:red"}
                        = message

          .form-group
            本人確認
            %p.info-text 安心・安全にご利用いただく為に、お客様の本人情報の登録にご協力ください。他のお客様に公開されることはありません。

          .form-group
            お名前(全角)
            %span.required
              必須
            %br
            .field
              = f.text_field :family_name, autofocus: true, autocomplete: "family_name",placeholder: "例）山田",class: "default half"           
              = f.text_field :given_name, autofocus: true, autocomplete: "given_name",placeholder: "例）太郎",class: "default half__right"
              - if @user.errors.any?
                .error_explanation
                  %h2
                    - I18n.t("errors.messages.not_saved",                 |
                      count: resource.errors.count,                       |
                      resource: resource.class.model_name.human.downcase) |
                  %ul
                    - @user.errors.full_messages_for(:family_name).each do |message|
                      %li{style: "color:red"}
                        = message
              - if @user.errors.any?
                .error_explanation
                  %h2
                    - I18n.t("errors.messages.not_saved",                 |
                      count: resource.errors.count,                       |
                      resource: resource.class.model_name.human.downcase) |
                  %ul
                    - @user.errors.full_messages_for(:given_name).each do |message|
                      %li{style: "color:red"}
                        = message

          .form-group
            お名前カナ(全角)
            %span.required
              必須
            %br
            .field
              = f.text_field :family_name_kana, autofocus: true, autocomplete: "family_name_kana",placeholder: "例）ヤマダ",class: "default half"           
              = f.text_field :given_name_kana, autofocus: true, autocomplete: "given_name_kana",placeholder: "例）タロウ",class: "default half__right"
              - if @user.errors.any?
                .error_explanation
                  %h2
                    - I18n.t("errors.messages.not_saved",                 |
                      count: resource.errors.count,                       |
                      resource: resource.class.model_name.human.downcase) |
                  %ul
                    - @user.errors.full_messages_for(:family_name_kana).each do |message|
                      %li{style: "color:red"}
                        = message
              - if @user.errors.any?
                .error_explanation
                  %h2
                    - I18n.t("errors.messages.not_saved",                 |
                      count: resource.errors.count,                       |
                      resource: resource.class.model_name.human.downcase) |
                  %ul
                    - @user.errors.full_messages_for(:given_name_kana).each do |message|
                      %li{style: "color:red"}
                        = message

          .form-group
            生年月日
            %span.required
              必須
            %br
            .field
              != sprintf(f.date_select(:birthday, prefix:'birthday',with_css_classes:'XXXXX', prompt:"--",use_month_numbers:true, start_year:Time.now.year, end_year:1900, date_separator:'%s'),'年','月')+'日'
                  
              - if @user.errors.any?
                .error_explanation
                  %h2
                    - I18n.t("errors.messages.not_saved",                 |
                      count: resource.errors.count,                       |
                      resource: resource.class.model_name.human.downcase) |
                  %ul
                    - @user.errors.full_messages_for(:birthday).each do |message|
                      %li{style: "color:red"}
                        = message
        

          .form-group
            .actions
              = f.submit "次へすすむ",class: "submit"
            -# %input.submit{type: "submit", value: "次へすすむ"}

            -# 実験的にページ移動の為のリンクです。ウィーザード形式に後になります
            -# =link_to '会員登録情報その２', new2_users_path
            
  .login-footer
    = render partial: '/template/input-bottom'
```
#### view(2ページ目：ユーザー本人の住所)
```ruby:/app/views/users/registrations/new_profileaddresses.html.haml
.input-top
  = render partial: '/template/input-top'
  
  .login-main
    .login-main__title
      ご本人様情報入力（任意）

    .login-main__content
      .login-main__content__form
        = form_with(model: @profileaddress,url: deliveryaddresses_path,local: true,method: :post) do |f|

          .form-group
            郵便番号  
            %span.any
              任意
            %br 
            .field
              = f.text_field :postal_code, autofocus: true, autocomplete: "postal_code",placeholder: "ーーー",class: "default"
              -if @profileaddress.errors.any?
                .alert.alert-warning
                  %ul
                    - @profileaddress.errors.full_messages.each do |message|
                      %li{style: "color:red"} 
                        - if message.include?("郵便番号")
                          = message.delete!("Translation missing: ja")

                    
          .form-group
            都道府県
            %span.any
              任意
            %br
            .field 
              = f.select :prefectures, Profileaddress.prefectures.keys, {},placeholder: "選択してください",class: "default"
          .form-group
            市区町村
            %span.any
              任意
            %br 
            .field
              = f.text_field :city, autofocus: true, autocomplete: "city",placeholder: "ーーー",class: "default"
              -if @profileaddress.errors.any?
                .alert.alert-warning
                  %ul
                    - @profileaddress.errors.full_messages.each do |message|
                      %li{style: "color:red"} 
                        - if message.include?("市区町村")
                          = message.delete!("Translation missing: ja")

          .form-group
            番地
            %span.any
              任意
            %br 
            .field
              = f.text_field :address, autofocus: true, autocomplete: "address",placeholder: "ーーー",class: "default" 
              -if @profileaddress.errors.any?
                .alert.alert-warning
                  %ul
                    - @profileaddress.errors.full_messages.each do |message|
                      %li{style: "color:red"} 
                        - if message.include?("番地")
                          = message.delete!("Translation missing: ja")
          .form-group
            マンション名など部屋号室
            %span.any
              任意
            .field 
              = f.text_field :building, autofocus: true, autocomplete: "building",placeholder: "ーーー",class: "default" 
              -if @profileaddress.errors.any?
                .alert.alert-warning
                  %ul
                    - @profileaddress.errors.full_messages.each do |message|
                      %li{style: "color:red"} 
                        - if message.include?("マンション")
                          = message.delete!("Translation missing: ja")
            %p.info ※本人情報は正しく入力してください。会員登録後、修正するにはお時間を頂く場合がございます。
          
          .form-group
            .action
              = f.submit "次へすすむ",class: "submit"
            
  .login-footer
    = render partial: '/template/input-bottom'
```
#### view(3ページ目：お届け先住所)
```ruby:/app/views/users/registrations/new_deliveryaddresses.html.haml
.input-top
  = render partial: '/template/input-top'
  
  .login-main
    .login-main__title
      お届け先住所入力
    .login-main__content
      .login-main__content__form
        = form_with(model: @deliveryaddress,url: user_registration_path,local: true) do |f|

          .form-group
            お名前(全角)
            %span.required
              必須
            %br
            .field
              = f.text_field :family_name, autofocus: true, autocomplete: "family_name",placeholder: "例）山田",class: "default half"           
              = f.text_field :given_name, autofocus: true, autocomplete: "given_name",placeholder: "例）太郎",class: "default half__right"       
              -if @deliveryaddress.errors.any?
                .alert.alert-warning
                  %ul
                    - @deliveryaddress.errors.full_messages.each do |message|
                      %li{style: "color:red"} 
                        - if message.include?("名字は")
                          = message.delete!("Translation missing: ja")
              -if @deliveryaddress.errors.any?
                .alert.alert-warning
                  %ul
                    - @deliveryaddress.errors.full_messages.each do |message|
                      %li{style: "color:red"} 
                        - if message.include?("名前は")
                          = message.delete!("Translation missing: ja")
          .form-group
            お名前カナ(全角)
            %span.required
              必須
            %br
            .field
              = f.text_field :family_name_kana, autofocus: true, autocomplete: "family_name_kana",placeholder: "例）ヤマダ",class: "default half"           
              = f.text_field :given_name_kana, autofocus: true, autocomplete: "given_name_kana",placeholder: "例）タロウ",class: "default half__right"
              -if @deliveryaddress.errors.any?
                .alert.alert-warning
                  %ul
                    - @deliveryaddress.errors.full_messages.each do |message|
                      %li{style: "color:red"} 
                        - if message.include?("名字（カナ）は")
                          = message.delete!("Translation missing: ja")
              -if @deliveryaddress.errors.any?
                .alert.alert-warning
                  %ul
                    - @deliveryaddress.errors.full_messages.each do |message|
                      %li{style: "color:red"} 
                        - if message.include?("名前（カナ）は")
                          = message.delete!("Translation missing: ja")
          .form-group
            郵便番号 
            %span.required
              必須
            %br 
            .field
              = f.text_field :postal_code, autofocus: true, autocomplete: "postal_code",placeholder: "ーーー",class: "default"
              -if @deliveryaddress.errors.any?
                .alert.alert-warning
                  %ul
                    - @deliveryaddress.errors.full_messages.each do |message|
                      %li{style: "color:red"} 
                        - if message.include?("郵便番号")
                          = message.delete!("Translation missing: ja")
            .form-group
              都道府県 
              %span.required
                必須
              %br         
              .field 
                = f.select :prefectures, Deliveryaddress.prefectures.keys, {},placeholder: "選択してください",class: "default"
            .form-group
              市区町村 
              %span.required
                必須
              %br            
              .field
                = f.text_field :city, autofocus: true, autocomplete: "city",placeholder: "ーーー",class: "default" 
                -if @deliveryaddress.errors.any?
                  .alert.alert-warning
                    %ul
                      - @deliveryaddress.errors.full_messages.each do |message|
                        %li{style: "color:red"} 
                          - if message.include?("市区町村")
                            = message.delete!("Translation missing: ja")
            .form-group
              番地
              %span.required
                必須
              %br             
              .field
                = f.text_field :address, autofocus: true, autocomplete: "address",placeholder: "ーーー",class: "default" 
                -if @deliveryaddress.errors.any?
                  .alert.alert-warning
                    %ul
                      - @deliveryaddress.errors.full_messages.each do |message|
                        %li{style: "color:red"} 
                          - if message.include?("番地")
                            = message.delete!("Translation missing: ja")
            .form-group
              マンション名など部屋号室
              %span.any
                任意
              %br
              .field 
                = f.text_field :building, autofocus: true, autocomplete: "building",placeholder: "ーーー",class: "default"  
                -if @deliveryaddress.errors.any?
                  .alert.alert-warning
                    %ul
                      - @deliveryaddress.errors.full_messages.each do |message|
                        %li{style: "color:red"} 
                          - if message.include?("マンション")
                            = message.delete!("Translation missing: ja")
            .form-group
              電話番号
              %span.any
                任意
              %br
              .field 
                = f.text_field :phone_number, autofocus: true, autocomplete: "phone_number",placeholder: "ーーー",class: "default" 
                -if @deliveryaddress.errors.any?
                  .alert.alert-warning
                    %ul
                      - @deliveryaddress.errors.full_messages.each do |message|
                        %li{style: "color:red"} 
                          - if message.include?("電話番号")
                            = message.delete!("Translation missing: ja")
          .form-group
            %p.info-text__end 「登録する」のボタンを押すことにより、利用規約に同意したものとみなします
            .action
              = f.submit "登録する",class: "submit" 

  .login-footer
    = render partial: '/template/input-bottom'
```

#### ｟苦難したところ｠
１ページ目は主に**ユーザ情報**の入力部分となります
入力フォームには現在推奨されているform_withの方を使用しています

ここで、まず私が苦難したところは以下の部分です

```ruby
 = form_with(model: @user, url: profileaddresses_path,local: true,method: :post) do |f|  
```
```url: profileaddresses_path```の部分に注目してください。

ちなみに、普通にdeviseで作成するとここは

```ruby
= form_with(model: @user, url: user_registration_path) do |f|
```
となっていると思います
そう```url: user_registration_path```となっているのです
私もこれを直すと思っていませんでした

なぜなら、**newアクションを通したviewの次はcreateという固定概念があったからです**
しかし、**createはデータを保存するアクション**です
そして、今回のウィザード形式では、データを保存するのは3ページ目なのでcreateアクションは3ページ目のviewの後にくるアクションだったのです

では、ここで```url: profileaddresses_path```で送られたデータはどこに送られるのでしょうか？
ここでルーティングを確認したいと思います

```console:ターミナル（必要部分のみ記載）
 Prefix Verb   URI Pattern                                                                              Controller#Action
         profileaddresses POST   /profileaddresses(.:format)                                                              users/registrations#new_profileaddresses 
```
ここでわかるように、newのview（ウィザード形式1ページ目）で入力されたデータはnew_profileaddressesアクションに送信されました
そして、new_profileaddressesアクションでデータは一時保存され（詳しいことは別記事に記載します）、そのままnew_profileaddressesのviewへと移動します

では、次に2ページ目のviewを確認してみます

```ruby
= form_with(model: @profileaddress,url: deliveryaddresses_path,local: true,method: :post) do |f
```
```console:ターミナル（必要部分のみ記載）
 Prefix Verb   URI Pattern                                                                              Controller#Action
        deliveryaddresses POST   /deliveryaddresses(.:format)                                                             users/registrations#new_deliveryaddresses 
```
上記を見てもらえれば、1ページ目と同じ動きをしていることがわかると思います

では、3ページ目はどうでしょうか？



```ruby
 = form_with(model: @deliveryaddress,url: user_registration_path,local: true) do |f|
```
```console:ターミナル（必要部分のみ記載）
 Prefix Verb   URI Pattern                                                                              Controller#Action
        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
 
```
3ページ目では、```url: user_registration_path```にデータを送信しています
ターミナルを確認すれば、これは```users/registrations#create```となります
つまり、createアクションにデータを送信し、1ページ目、2ページ目で一時保存したデータと3ページ目で入力されたデータを一緒に保存してユーザ登録を完了します

#### ｟結論｠
view1ページ目(new.html.haml)
　=>入力
　　=>入力データ送信（データを一時保存するアクションへと送信）
　　　=>アクションで一時保存
　　　　=>view2ページ目（一つ前のアクションと同じ名前のview）
　　　　　=>入力
　　　　　　=>入力データ送信（データを一時保存するアクションへと送信）
　　　　　　　=>アクションで一時保存
　　　　　　　　=>view3ページ目（一つ前のアクションと同じ名前のview）
　　　　　　　　　=>入力
　　　　　　　　　　=>入力データ送信
　　　　　　　　　　　=>createアクションにて全ての入力データを保存（ユーザ登録する）
この形がウィザード形式であり、データを送信する時は**データを一時保存するアクションへと送信**することを忘れないようにします

###【次の記事】
この記事では、ウィザード形式のデータの大まかな動きを記載しました
次の記事ではcontroller内について詳しく記載していきます

**次の記事** → [ウィザード形式でユーザ登録のフォームを作成してみた②-2　〜viewとcontroller〜](https://qiita.com/niisan3211/items/1c3b86db778552bb9df6)