deviseを用いたユーザー登録をウィザード形式にする

備忘録として書き込みます

## 環境
- ruby 2.5.1
- Rails 5.2.4.1
- devise (4.7.1)




一つのアカウントで複数人の栄養管理をするという趣旨アプリを作っています。
また、アカウント登録と同時に管理ユーザーも一人登録するという想定でアプリを作っています。
そのためにウィザード形式での登録を使ったらカッコ良いんじゃないかなあと思い使ってみました。

まず、deviseを使ってUserモデルを登録（アカウントに当たります）

``` rails:db/migrate

class DeviseCreateUsers < ActiveRecord::Migration[5.2]
  def change
    create_table :users do |t|
      ## Database authenticatable
      t.string :email,              null: false, default: ""
      t.string :encrypted_password, null: false, default: ""
      t.string :name, null: false

　　　#略

      ## Recoverable
      t.string   :reset_password_token
      t.datetime :reset_password_sent_at

      ## Rememberable
      t.datetime :remember_created_at

     #略

      t.timestamps null: false
    end

    add_index :users, :email,                unique: true
    add_index :users, :reset_password_token, unique: true
    
end
```

そして、Userに紐づいたPersonです（管理ユーザーに当たります）

```rails:db/migrate

class CreatePeople < ActiveRecord::Migration[5.2]
  def change
    create_table :people do |t|
      t.string :name, null: false
      t.float :height, null: false
      t.float :weight, null:false
      t.string :gender, null: false

      t.timestamps
    end
  end
end

```

ここには書きませんがUserとPersonはUser１の一対多のアソシエーションを組んでいます。

## 作業

ここからが本題です。
まず、見えていなかったdeviseのコントローラーを呼び出します

```terminal:ターミナル
rails g devise:controllers users
```

次にルーティングでコントローラーをいじれるようにして、
deviseに新しいアクションを設定します

```rails:config/route.rb
#usersで遷移するようにする
devise_for :users, controllers: {
    registrations: 'users/registrations',
  }
#アクション２つ作成
devise_scope :user do
    get 'new_first_person', to: 'users/registrations#new_first_person'
    post 'create_first_person', to: 'users/registrations#create_first_person'
end
```

こうすることでルーティングが、
devise/registrations#○○○
↓
users/registrations#○○○
とカスタマイズすることができます。
こうしないとUser.newをモデルに引き込んでいるformがregistrationコントローラーのアクションに飛んでくれません

ビューのコードです。
僕はこんな感じです。
やったこととしてはform_forのresourceの部分を@user(User.new)にしたりurlを変えたぐらいです

```rails:views/users/registrations/new.html.haml

.form
  %h1.form__title 新しいアカウントを作成
  = form_for(@user, url: user_registration_path) do |f|
    = render "devise/shared/error_messages", resource: resource
    .field
      = f.text_field :name,placeholder: "アカウント名", autofocus: true, autocomplete: "email",class:"field--form"
    .field
      = f.email_field :email,placeholder: "メールアドレス", autofocus: true, autocomplete: "email",class:"field--form"
    .field
      = f.password_field :password,placeholder: "パスワード（半角英数字６文字以上）", autocomplete: "new-password",class:"field--form"
    .field
      = f.password_field :password_confirmation,placeholder: "パスワード（確認）", autocomplete: "new-password",class:"field--form"
    .actions
      = f.submit "新しいアカウントを作成",class: "form--submit"

```

もう一つ、renderで出す方のビューです

```rails:views/users/resistrations/new_first_person.html.haml

.form_header
  .form_header--title
    =link_to "CARE KITCHEN","#"
  .form_header--icon
    =link_to new_user_session_path do
      %i.fas.fa-door-open
      %p ログイン


.form
  %h1.form__title 一人目のユーザー登録
  = form_for(@person, url: create_first_person_path) do |f|
    -# = render "devise/shared/error_messages", resource: resource
    .field
      = f.text_field :name,placeholder: "ユーザー名", autofocus: true,class:"field--form"

    .field.birthday
      = f.date_field :birthday,placeholder: "生年月日", autofocus: true,class:"birthday__form"
      %p.birthday__title 生年月日

    .field
      = f.select :gender,[["性別を選択してください",""],["男性","0"],["女性","1"]],class: "select_gender"
    .field
      = f.number_field :height,placeholder: "身長(m)　例:165.5kg",class:"field--form",step: "0.1"
    .field
      = f.number_field :weight,placeholder: "体重(kg)　例:55.5kg",class:"field--form",step: "0.1"
    .actions
      = f.submit "入力完了",class: "form--submit"
  -# = render "users/shared/links"

```


ここでコントローラーを弄ります


```rails:app/controllers/users/registrations_controller.rb

class Users::RegistrationsController < Devise::RegistrationsController

  #略

  def new
    @user = User.new
  end


  def create
    @user = User.new(sign_up_params)
    unless @user.valid?
      flash.now[:alert] = @user.errors.full_messages
      render :new
    end
    session["create_acount"] = { user: @user.attributes}
    session["create_acount"][:user]["password"] = params[:user][:password] 
    @person = @user.people.build
    render :new_first_person
  end

  def create_first_person
    @user = User.new(session["create_acount"]["user"])
    @person = Person.new(person_params)
    unless @person.valid?
      flash.now[:alert] = @address.errors.full_messages
      render :new_first_person and return
    end
    @user.people.build(@person.attributes)
    @user.save
    @person[:user_id] = @user.id
    sign_in(:user, @user)
    redirect_to root_path
  end

  #略

  
  protected

  
  def configure_sign_up_params
    devise_parameter_sanitizer.permit(:sign_up, keys: [:attribute])
  end

  def person_params
    params.require(:person).permit(:name,:birthday,:gender,:height,:weight)
  end

　#略
  
end


```

上から順に話します

①newアクション
ここでUserのインスタンスを作成

②createアクション
ストロングパラメータで値を受け取り@userに代入します
@userの検証を行い、問題がなかったら"create_acount"と名付けたsessionという箱に"user"のデータを入れます
ここで気をつけたいのがpasswordはこの時sessionに代入されない点です
改めてsessionに入れてあげます
で、@userに紐づいたpersonのインスタンスを生成し、new_first_personのビューをrenderします

③create_first_person
ひとまずsessionに入れておいたuserの情報からUserインスタンスを作ります
personはフォームから送られた情報です
personの検証を行い保存です


## まとめ

sessionに情報を入れてアクションをまたいで保存、っていうイメージなんですかね？
勉強になりました。
間違っていることがあれば指摘していただけると助かります