ユーザー管理機能をウィザード形式で実装する

#概要
今回は、ユーザー管理機能をウィザード形式で実装することに関してです。
ウィザード形式とは、画面遷移しながらユーザー登録を行っていく形式です。

今回、userモデルの登録要件は
nickname, grade, email, password

続いて画面遷移してuser_infoモデルに
subject, school, profile

を登録します。

ちなみに、user_infoは得意教科、在籍校、プロフィールとなっています。

#実装

まずはdeviseをインストールして、userモデルを作成します。

emailとpasswordはデフォルトで入っているので、nicknameとgradeを追加します。

カラムを追加したので、次のようにコントローラーを編集します。

```controllers.application_controller.rb
class ApplicationController < ActionController::Base
  before_action :configure_permitted_parameters, if: :devise_controller?

  protected

  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: [:nickname, :grade])
  end
end
```

次に、userモデルにバリデーションをかけます(コードは省略)

そして、ビューファイルを作ります。

```views.devise.registrations.new.html.erb
<h2>ユーザー情報登録</h2>

<%= form_for(@user, url: user_registration_path) do |f| %>
  <%= render "devise/shared/error_messages", resource: @user %>

  <div class="field">
    <%= f.label :nickname %><br />
    <%= f.text_field :nickname %>
  </div>

  <div class="field">
    <%= f.label :grade %><br />
    <%= f.text_field :grade %>
  </div>

  <div class="field">
    <%= f.label :email %><br />
    <%= f.email_field :email, autofocus: true, autocomplete: "email" %>
  </div>

  <div class="field">
    <%= f.label :password %>
    <% if @minimum_password_length %>
    <em>(<%= @minimum_password_length %> characters minimum)</em>
    <% end %><br />
    <%= f.password_field :password, autocomplete: "new-password" %>
  </div>

  <div class="field">
    <%= f.label :password_confirmation %><br />
    <%= f.password_field :password_confirmation, autocomplete: "new-password" %>
  </div>

  <div class="actions">
    <%= f.submit "Sign up" %>
  </div>
<% end %>

<%= render "devise/shared/links" %>
```

ログイン画面を作ります。

```views.home.index.html.erb
<h1>トップページ</h1>

<% if user_signed_in?%>
  <h2>ログインしています</h2>
  <%= link_to "ログアウト", destroy_user_session_path, method: :delete %>
<% else %>
  <h2>ログインしていません</h2>
  <%= link_to "新規登録", new_user_registration_path %>
  <%= link_to "ログイン", new_user_session_path %>
<% end %>

```

画面遷移後のuser_infoモデルを作ります。

この時、外部キーとして、user_idを紐づけるのを忘れないようにします。

その後、userモデルとuser_infoモデルのアソシエーションを組みます。

次はdeviseのコントローラーを作成。

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

次にルーティング。

```routes.rb
Rails.application.routes.draw do
  devise_for :users, controllers: {
    registrations: 'users/registrations'
  }
  root to: "home#index"
end
```

ここまできたらuser_infoを登録する実装をしていきます。

registrationsコントローラーにnewメソッドを記述し、ビューファイルにformを入れます。

```views/devise/registrations/new.html.erb
<%= form_for(@user, url: user_registration_path) do |f| %>
  <%= render "devise/shared/error_messages", resource: @user %>

---省略---

 <div class="actions">
    <%= f.submit "Next" %>
  </div>
<% end %>
```

ここからが少し記述が増えてきます。
createアクションを記述していきましょう。

```controllers/users/registrations_controller.rb

def create
   @user = User.new(sign_up_params)
    unless @user.valid?
      render :new and return
    end
   session["devise.regist_data"] = {user: @user.attributes}
   session["devise.regist_data"][:user]["password"] = params[:user][:password]
   @user_info = @user.build_user_info
   render :new_user_info
 end

```

sessionを用いて1ページ目に記述したデータを持ってきます。
そしてattributesメソッドでデータを整形しています。

build_user_infoで今回生成したインスタンス@userに紐づくUserInfoモデルのインスタンスを生成します。ここで生成したUserInfoモデルのインスタンスは、@user_infoというインスタンス変数に代入します。そして、住所情報を登録させるページを表示するnew_user_infoアクションのビューへrenderします。

user_infoを登録させるページを表示するnew_user_infoアクションと住所情報を登録するcreate_user_infoアクションのルーティングを設定しましょう。

```routes.rb
Rails.application.routes.draw do

  devise_for :users, controllers: {
    registrations: 'users/registrations'
  }
  devise_scope :user do
    get 'user_infos', to: 'users/registrations#new_user_info'
    post 'user_infos', to: 'users/registrations#create_user_info'
  end

  resources :posts

  root to: "home#index"
end

```

このルーティングに書いた通り、user_infoを登録するビューファイルを作成していきます。

```views/devise/registrations/new_user_info.html.erb
<h2>ユーザー情報登録</h2>

<%= form_for @user_info do |f| %>
  <%= render "devise/shared/error_messages", resource: @user_info %>

  <div class="field">
    <%= f.label :subject, "頑張りたい教科" %><br />
    <%= f.text_field :subject %>
  </div>

  <div class="field">
    <%= f.label :school, "学校名" %><br />
    <%= f.text_field :school %>
  </div>

  <div class="field">
    <%= f.label :profile, "自己紹介を記入しましょう" %><br />
    <%= f.text_area :profile %>
  </div>

  <div class="actions">
    <%= f.submit "Sign up" %>
  </div>
<% end %>

<%= render "devise/shared/links" %>
```

このuser_infoを保存する記述をコントローラーにしましょう。

```controllers/users/registrations_controller.rb
def create_user_info
    @user = User.new(session["devise.regist_data"]["user"])
    @user_info = UserInfo.new(user_info_params)
      unless @user_info.valid?
        render :new_user_info
      end
    @user.build_user_info(@user_info.attributes)
    @user.save
    session["devise.regist_data"]["user"].clear
    sign_in(:user, @user)
  end

  protected

  def user_info_params
    params.require(:user_info).permit(:subject, :school, :profile)
  end
```

userをsaveした後に、.clearでsessionを削除しています。
そして、保存された後にログインされるように記述されています。

最後に、create_user_infoアクションに対応するビューを作成します。

```views/devise/registrations/create_user_info.html.erb
<h2>登録が完了しました</h2>
<%= link_to "トップへ戻る", root_path%>
```

以上です。

#感想
初めてウィザード形式の登録機能を実装しました。

本当はgradeの学年のところをactive_hashで実装したかったのですが、二時間エラーに悩まされた末に断念しました、、、

一通り実装してみて余裕があったらまたチャレンジしてみます。

日々頑張ってはいるのですが、あまり成長を感じられないです。
めちゃくちゃ記事みないと何もできません、、、

頑張ります。