deviseを使ってウィザード形式のユーザー登録

普通にインストールして

rails g devise:intall でdeviseのヘルパーメソッド使えるようにして

(config以下に設定ファイルが生成される)

 

rails g devise user でモデルを作成してmigrate

 

rails g devise:views でviewファイルを作成

confirmations, mailer, passwords, registrationsフォルダが作成される。大体いじるのはregistrations内のviewファイル。

 

rails g devise:controllers users でdevise管理下のコントローラーを作成。この段階でcontrollersの下にusersディレクトリとhogehoge_controller.rbがいくつか作成されるが、今回いじるのは大体registrations_controller.rbだろう。

ちなみにカラムを追加してるなら、application_controllerの方でif:  :devise_controller?だったらbefore_actionでprivateエリアに定義するストロングパラメタ的なメソッド使うようにする。ストロングパラメタとちょっと違ってdevise_parameter_sanitizer.permit()で許可する

っていうのはいつも通り。

 

ウィザードに関係があるのはここから。

まず、別テーブルをmigrateしておく。ウィザードでページが切り替わる時にvalidationが都度かかるので、モデルごとに切り分けておかないと色々不都合が起きる。今回は仮にユーザーの住所でaddressesテーブルみたいな感じ。リレーションは例としてはuser側はhas_one :addressでaddress側はbelongs_to :userみたいな感じ。belongs_toしてるaddress側はuser_idを持ってるだろうけどこれはoptional: trueで一旦外部キー入ってなくてもフォームに進めるようにしとく。

 

ここからルーティングの話。deviseのデフォルのルーティングをここで全部書くのはめんどいので、見たけりゃrails routesするんだけど、基本的にコントローラーがdevise/sessions, devise/passwords, devise/registrationsにわかれててアクションはよくある命名って感じ(cancelとかもあったが)。やっぱり自分で触るとしたら大抵はregistrations関係になるんかな?

 

config/routes.rbで

devise_for :users, controllers: {
    registrations: 'users/registrations',
  }

 

と記述することで今まで参照してたdevise/registrationsコントローラーがusers/registrationsに変わる。ざっくり全体像を掴むための理解の補足として、sessionsとかpasswordに関してはdevise/sessions#アクション名のままだけどregistrationsコントローラーのルーティングだけusers/registrations#アクション名に変わったって感じ。

 

ファイルの場所はapp/controllers/users/registrations_controllers.rb

これがDevise::RegistrationsControllerを継承した

Users::RegistrationsControllerに生まれ変わった感じ、なので中身を見ると

色々コメントアウトされてるが、Devise::RegistrationsControllerに書いてあった物がすでに書いてある箇所が多いのでコメントアウト外せばそのまま使える。ちなみにsuperは継承元を見に行ってる感じ。

def new

super

end

こんな感じになってるから今回はこのあたりを上書きして、ウィザード形式になるようにカスタマイズしていく。

例えばこんな感じ

def new

 @user = user.new

end

ただこれはsuperのままにしといても同じことしてくれるからあんまり意味なくて、今回いじるのはcreatアクションと新しく作るcreat_addressアクションって感じ。他のは特に使わない。

 

 

 とりあえずviewの方でform_forとかの中身とかresources: resourecesを@userとかresources: @userに変えとく。(分かり易いように)。元々のresourcesは仮引数みたいな物なので。resources_nameとかresources_pathとかより普通のform_forの書き方にした方が分かり易いはず。

 

そしてcreateアクションをいじる。普通のcreateアクションなら保存して登録完了ページって感じだろうけどそれじゃ困るのでとりあえず

 

1最初のページで入力した情報のバリデーションをチェック(ストロングパラメタ部分は説明しなくていいよね)

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

 

 2最初のページで入力した情報をセッションに保持

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

 なんで二行目があるかって話なんだけど

sessionにハッシュオブジェクトの形で情報を保持させるために、attributesメソッドを用いてデータを整形しています。また、paramsの中にはパスワードの情報は含まれていますが、attributesメソッドでデータ整形をした際にパスワードの情報は含まれていません。そこで、パスワードを再度sessionに代入する必要があります。

という感じでpasswordのだけは一行目で入らなかったので追加してる。

あとsessionの使い方の参考記事載せとく

https://qiita.com/ozackiee/items/4ee774c81b2a0c571c05

若干正確なのか自信が持てないのでrailsチュートリアルも。ちゃんと理解するならこっちがいいかな

https://railstutorial.jp/chapters/log_in_log_out?version=4.2

 

3このコントローラーの処理が終わったら次の登録ページ(言うたらnewページ)に遷移にするワケだから、遷移のための記述、その前に次のページで使用するインスタンスを渡しておく必要がある

@address = @user.build_address
render :new_address

 

当然そのページにgetする際や、そのページで入力した情報をpostする際にはそのためのルーティングが必要であるので

devise_scope :user do
get 'addresses', to: 'users/registrations#new_address'
post 'addresses', to: 'users/registrations#create_address'

get '/users/sign_out', to: 'devise/sessions#destroy'
end

とルーティングを設定してあげる。(ログアウトのパスは今関係ないけどついでに載せといた)

 

住所登録ページ自体はform_forとかresource:に渡す物が@userではなくて、@addressに変わってる位で、普通にフォームを作成すれば良い。

 

ページを作成したら、送信ボタンの飛び先であるcreate_addressアクションを作る。

def create_address
@user = User.new(session["devise.regist_data"]["user"])
@address = Address.new(address_params)
@address.user_id = @user.id
unless @address.valid?
flash.now[:alert] = @address.errors.full_messages
render :new_address and return
end
@user.build_address(@address.attributes)
@user.save
 
# session["devise.regist_data"]["user"].clear
sign_in(:user, @user)
end

こんな感じ。

説明加えると、

1まず今までに入力した情報をインスタンスに入れて、外部キーになるuser_idもいれる

@user = User.new(session["devise.regist_data"]["user"])
@address = Address.new(address_params)
@address.user_id = @user.id

 

2今までと同じようにvalid?でバリデーションをチェックする。(もちろん引っ掛かった場合の処理も書いとく)

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

3build_addressを用いて送られてきたparamsを、保持していたsessionが含まれる@userに代入してsaveする。

@user.build_address(@address.attributes)
@user.save

 4clearメソッドで明示的にsessionを削除

session["devise.regist_data"]["user"].clear

これはずっと保持して置きたい場合はいらないかもしれない、開発の時はコメントアウトしとこうかな。

5登録の処理しか書いてないので、登録後自動でログインする。

sign_in(:user, @user)

 

って感じ。

 

最後に、必要であればaddress_create.html.hamlを作成する。