【この記事の説明】
現在、私はプログラミングスクールでプログラミング(主にRuby)の勉強をしています。
そして、その一環でチーム開発(メルカリもどきのサイト作成)を行っており、私はその中で記事のタイトルにあるように、ウィザード形式でユーザの登録ができるようにする作業を担当致しました。
又、スクールからの指示で、この登録フォームにはdeviseを使用するようにとのことでした。
この記事は、その担当作業についてまとめたものになります。
【作業】
ここから私が行った作業(そこで困ったこと)を記事として記載していこうと思います
※この記事では、viewとcontrollerについて記載したいと思います
view、controllerの作成
本当はviewはview、controllerはcontrollerで記載したいのですが、この二つは密接に関わり合う為、別々に説明ができません。ですので、一緒に説明していきたいと思います
又、ここでは、view作成の細かいところの記載はしません
あくまでもユーザー登録に必要な部分のみの記載となります
では、以下にcontrollerとviewのコードを記載します
(前の記事で記載しましたが、ウィザード形式で3ページ分の登録ページを作成している為、viewは3つ記載します
詳しくはウィザード形式でユーザ登録のフォームを作成してみたをご覧ください)
controller
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ページ目:ユーザ情報)
.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: "7文字以上",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 '会員登録情報その2', new2_users_path
.login-footer
= render partial: '/template/input-bottom'
view(2ページ目:ユーザー本人の住所)
.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ページ目:お届け先住所)
.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'
⦅苦難したところ⦆
1ページ目は主にユーザ情報の入力部分となります
入力フォームには現在推奨されているform_withの方を使用しています
ここで、まず私が苦難したところは以下の部分です
= form_with(model: @user, url: profileaddresses_path,local: true,method: :post) do |f|
url: profileaddresses_pathの部分に注目してください。
ちなみに、普通にdeviseで作成するとここは
= 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で送られたデータはどこに送られるのでしょうか?
ここでルーティングを確認したいと思います
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を確認してみます
= form_with(model: @profileaddress,url: deliveryaddresses_path,local: true,method: :post) do |f
Prefix Verb URI Pattern Controller#Action
deliveryaddresses POST /deliveryaddresses(.:format) users/registrations#new_deliveryaddresses
上記を見てもらえれば、1ページ目と同じ動きをしていることがわかると思います
では、3ページ目はどうでしょうか?
= form_with(model: @deliveryaddress,url: user_registration_path,local: true) do |f|
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内について詳しく記載していきます