前書き
※この記事は、以前はパラメータを渡す方式(GET)で実装しましたが、POSTに変更しました。
環境
Ruby 2.5.7
Rails 5.2.4
gem
gem 'devise'
前提
deviseの新規登録フォームがカスタムできる状態(デフォルトではない状態)
※事前に、新規登録フォームが自由にカスタムできることをご確認ください。
やりたいこと
deviseのデフォルト設定ではsign_upページで情報が入力され送信されると、そのまま即登録され、あらかじめ指定した(もしくはデフォルトの)ページに遷移します。
登録の前に入力された情報が確認できるページを挟んだり、登録が完了したことをお知らせするページがあると、より親切かと思いますので、今回は「入力フォーム」に加えて、「入力された情報の確認画面」、「登録完了をお知らせする画面」を作成していきます。
今回の作成で完成するページの流れとしては、
新規会員情報の入力フォームregistrations/new.html.erb(既存)
→確認画面registrations/confirm.html.erb(新規作成)
→完了画面registrations/complete.html.erb(新規作成)
という順番で画面が遷移していきます。
手順
1.views(views/users/registrations
)に入力画面(new.html.erb
)、確認画面(confirm.html.erb
)、完了画面(complete.html.erb
)を用意
2.routes.rb
に、1.で追加したviewファイルのルーティング、registrations_controller.rb
の使用を追記
3.controllers(controllers/users/registrations_controller.rb
)でconfirmアクション、completeアクションを追記、createアクションの再定義、登録後のリダイレクト先の変更
1.入力画面、確認画面、完了画面の作成
冒頭で記した通り、新規登録時の画面の流れとしては、
新規登録入力フォームnew.html.erb
→確認画面confirm.html.erb
→完了画面complete.html.erb
となります。
確認画面で"登録する"ボタンを押すとdeviseの標準機能でユーザー情報が登録されます。
入力画面
<div>
<h2>新規会員登録</h2>
<%= form_with model: @user, url: users_sign_up_confirm_path(@user), local: true do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :phone_number %>
<%= f.text_field :phone_number %>
<%= f.label :email %>
<%= f.email_field :email %>
<%= f.label :password %>
<% if @minimum_password_length %>
(<%= @minimum_password_length %>文字以上)
<% end %>
<%= f.password_field :password %>
<%= f.password_field :password_confirmation %>
<%= render "users/shared/links" %>
<%= f.submit "確認" %>
<% end %>
</div>
元々用意されているregistrations/new.html.erb
のフォームを書き換えていきます。
登録情報に名前や電話番号情報を追加する方法は割愛します。
追加される場合は先にそちらの設定をして動作が確認できた上で、今回の方法を試していただくか、カラムは増やさずにデフォルトの設定(メールアドレス、パスワードのみ)でお試しください。
ポイントとしてはフォームタグです。
...
<%= form_with model: @user, url: users_sign_up_confirm_path(@user), local: true do |f| %>
...
<% end %>
ここで入力された値が@user
に保存され、確認画面に渡されます。
後述しますが、確認画面はPOSTメソッドですので、送信ボタンが押されると後述するコントローラー(registrations_controller.rb
)で指定したURL(アクション)が実行され、確認画面に遷移します。(※URLやパスはご自身の環境によって異なります。)
今回使うフォームタグはform_with
です。
form_with
はデフォルトの送信方法がAjax(remote: true
)で、画面遷移が行われない仕様なので、local: true
も明示する必要があります。
form_for
の場合はデフォルトがlocal: true
なので、普段は意識する必要はありません。
確認画面
<div>
<h2>入力情報の確認</h2>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<p><%= f.label :name %><%= f.text_field :name, value: @user.name, readonly: true %></p>
<p><%= f.label :phone_number %><%= f.text_field :phone_number, value: @user.phone_number, readonly: true %></p>
<p><%= f.label :email %><%= f.text_field :email, value: @user.email, readonly: true %></p>
<p><%= f.label :password %><%= @password %></p>
<%= f.submit "修正する", name: :back %>
<%= f.submit "登録する" %>
<% end %>
</div>
確認画面では[登録するボタン]が押された時にdeviseの機能を使って会員登録を行いたいので、フォームタグはdeviseで元から用意されているform_for
とresource
など、そのまま利用します。
...
<p><%= f.label :name %><%= f.text_field :name, value: @user.name, readonly: true %></p>
...
f.text_field
を利用していますが、オプションとしてreadonly: true
を付けているため、フォーム内の編集はできず表示だけとなります。
また、valueオプションで前のページから受け取った@user
をそのまま値にしていますので、[登録するボタン]が押されると、中の値がそのまま送信されます。
<p><%= @password %></p>
はパスワードにブラインドをかけてパスワードの文字数だけが確認できるようにしています。
ブラインドのやり方についてはコントローラーのところで解説します。
<%= f.submit "修正する", name: :back %>
submitが押されると、指定したパスのアクション(今回の場合はregistrations_controller.rbのcreateアクション)が実行されますが、:back
キーが付与された状態でcreateが行われた場合は、保存されずにrender :new
が行われるようにコントローラーで記述しています。(後述します。)
完了画面
<div>
<p>登録が完了しました!</p>
</div>
前のページで登録する
が押されると、確認した会員情報が保存されると同時に、この画面に遷移します。
2.新たに作成したviewsとusers/registrations_controller.rbをroutes.rbに記述する
Rails.application.routes.draw do
...
# registrations_controller.rbを有効にします。
devise_for :users, controllers: {
...
registrations: 'users/registrations'
}
# views/users/registrations内に作成したconfirm.html.erbとcomplete.html.erbもルーティングに追加します。
devise_scope :user do
post 'users/sign_up/confirm', to: 'users/registrations#confirm'
get 'users/sign_up/complete', to: 'users/registrations#complete'
end
...
end
3.registrations_controller.rbにconfirmとcompleteを追加
確認画面・完了画面のレンダリングのために両アクションの追加、createアクションの再定義、登録完了後のリダイレクト先の変更を行います。
class Users::RegistrationsController < Devise::RegistrationsController
...
# newアクションは変更しません。
# GET /resource/sign_up
# def new
# super
# end
# createアクションはコメントアウトを外し、superの上に下記を追加
# POST /resource
def create
@user = User.new(sign_up_params)
render :new and return if params[:back]
super
end
...
# 新規追加
def confirm
i = 0
@password = ""
while i < @user.password.length
@password += "*"
i += 1
end
end
# 新規追加
def complete
end
# アカウント登録後
def after_sign_up_path_for(resource)
users_sign_up_complete_path(resource)
end
...
end
createアクション
@user
は入力画面で入力された値がそのまま引き継がれています。(確認画面では編集されないため。)
render :new and return if params[:back]
この一文は以下のように書き換えることもできます。
if params[:back]
render :new
return
end
super
submit
が押された時に:back
([修正するボタン])キーが含まれていたらrender :new
を実行し、@user
を保持したまま入力画面に戻ります。
return
を記述することで、レンダリングがされた後にcreateアクションが終了します。return
を書き忘れるとそのままsuperが実行されてしまうため、修正するが押されたにもかかわらず保存されてしまい、コンプリート画面に遷移します。
また、super
はDevise::RegistrationsController
での設定をそのまま継承するという意味で、この記述がなければform_forの中にあるresource
やresource_name
も使えなくなるので、createアクションのコメントアウトを外すときは消さないように注意しましょう。
confirmアクション
@user.password.length
でパスワードの文字数を取得しています。
while i < @user.password.length ... end
で処理の中でi
が1ずつカウントアップしていきます。
同時に、直前で定義した変数@password = ""
にも"*"
が足されていき、文字数に達したら処理が終了します。
そしてviewに<%= @password %>
変数の中身を表示します。
completeアクション
レンダリングのためだけのアクションなので、中身は空白のままで大丈夫です。(暗示的にrender :complete
が実行されます。)
登録後のリダイレクト先
確認画面で登録する
が押されたときに、完了画面registrations/complete.html.erb
に遷移させる必要があります。
デフォルトか、もしくは任意で別のページがリダイレクト先になっているかと思いますが、users_sign_up_complete_path(resource)
(完了画面のpath)に設定します。
まとめ
質問や解釈の違い、記述方法に違和感ありましたら、コメント等でご指摘いただけると幸いです。
最後まで読んでいただきありがとうございました。
参考サイト
より実用的な使い方については、私のGitHubに実際に使っているファイルを公開しているのでそちらも参考にしていただければと思います!
GitHub - MasaoSasaki/matchi