『2ページ遷移して会員登録』できる最低限のRailsアプリを丁寧に作る（deviseをウィザード形式に拡張）

# この記事の基本的な方針
deviseで実装したログイン機能は特にカスタマイズしなければ、1ページでEmailとパスワードの2項目を入力して登録完了です。
これを2ページに拡張し、1ページ目にEmailとパスワードに加えニックネームを、2ページ目でケータイ番号の入力を求めます。

この記事は、以下の「登録画面」「ログイン画面」「TOP画面」の3画面の簡単なアプリを元に拡張していきます。

【TOP画面(ログイン前)】             　　　 【TOP画面(ログイン後)】
<img width="230" display="inline" alt="a0.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559746/066baa43-6945-9174-0040-403550137cbe.png">    <img width="230" alt="a9.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559746/23eb0bd8-130a-e4f5-2c6e-2e5071388877.png">
【登録画面】
<img width="230" alt="a1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559746/a0c57e94-7fd4-45ee-34c7-bf08dcb9a4a5.png">
【ログイン画面】
<img width="230" alt="a2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559746/8c1ea7d6-3f1b-5624-71b8-50a5f16770b3.png">

手を動かしながら読みたいようでしたら、以下でこの3画面アプリを手に入れてください。

```bash:Terminal
$ git clone -b 超最低限のRailsアプリ（messageコントローラVer）  https://github.com/annaPanda8170/minimum_rails_application.git
$ bundle install
$ bundle exec rake db:create
$ bundle exec rake db:migrate
```
これ自体の作り方は[こちら](https://qiita.com/annaPanda8170/items/8856425be9d563fbffa7)。

最終的に以下のように1ページ目にニックネームの項目が加わり、ケータイ番号の登録をする2ページ目が新たに作られます。

<img width="230" alt="222a.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559746/cb087c97-b089-36e7-cb04-18e41d4afa5f.png">    <img width="230" alt="333a.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559746/ae43d160-fc4d-fd99-ba37-6f802df9428f.png">

# 想定する読み手
既に一度Railsアプリをチュートリアルやスクール等で作ったことがある方を想定しております。
Mac使用で、パソコンの環境構築は完了していることが前提です。
# 具体的なコーディング手順
[完成品GitHub](https://github.com/annaPanda8170/minimum_rails_application/tree/%E3%80%8E2%E3%83%9A%E3%83%BC%E3%82%B8%E9%81%B7%E7%A7%BB%E3%81%97%E3%81%A6%E4%BC%9A%E5%93%A1%E7%99%BB%E9%8C%B2%E3%80%8F%E3%81%A7%E3%81%8D%E3%82%8B%E6%9C%80%E4%BD%8E%E9%99%90%E3%81%AERails%E3%82%A2%E3%83%97%E3%83%AA%E3%82%92%E4%B8%81%E5%AF%A7%E3%81%AB%E4%BD%9C%E3%82%8B%EF%BC%88devise%E3%82%92%E3%82%A6%E3%82%A3%E3%82%B6%E3%83%BC%E3%83%89%E5%BD%A2%E5%BC%8F%E3%81%AB%E6%8B%A1%E5%BC%B5%EF%BC%89)（masterではなく一つのブランチなので注意して下さい）
## ①取り急ぎ1ページ目のみでニックネームカラムを登録項目として増やす
まず1ページのままでニックネーム入力項目を追加してみましょう。
通常、最初にテーブルを作成するときは`rails g model <モデル名>`としたときに一緒にマイグレーションファイルも作られますが、今回はすでにあるテーブルにカラムを増やすだけなのでマイグレーションファイルのみを作ります。

```bash:Terminal
$ rails g migration <クラス名>
```


を打ちます。
クラス名はなんでもいいです。`rails g migration Aaa`でも問題ないですが、`rails g migration AddColumn<テーブル名>`とするのが一般的です。今回は`rails g migration AddColumnUsers`としましょう。
そして、作られたマイグレーションファイルを編集します。

```ruby:db/migrate/20200xxxxxxxxxxx_add_column_users.rb
class AddColumnUsers < ActiveRecord::Migration[5.2]
  def change
    add_column :users, :nickname, :string 
  end
end
```

カラムを加える`add_column`メソッドを使います。第一引数がテーブル名、第二引数がカラム名、第三引数がデータ型です。「`ユーザテーブル`に`ニックネームカラム`を`文字列型`で加えます」ってことですね。極めて直感的です。余談ですが、コードを読むときは悶々とコードのまま黙読で理解しようとせずに、きっちりと日本語(自然言語)に直して読んでみることをオススメいたします。

そして

```bash:Terminal
$ rails db:migrate
```

して、テーブルは完成です。一応データベースを見に行ってカラムが増えているかを確認しましょう。

続いてコントローラで、追加したカラムを受け入れられるようにします。ここは`app/controllers/messages_controller.rb`ではなく、`app/controllers/application_controller.rb`を編集します。

```ruby:app/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: [:<追加したカラム名>])
  end
end
```

難しいですが、それぞれのメソッドを完全に理解できなくでも大丈夫です。[deviseのGitHub](https://github.com/heartcombo/devise)にそうしろと書いてあるのでそれに従います。掃除機を正しく使うのに掃除機を作れるようになる必要はありません。説明書をきちんと読みましょう。
今回は`devise_parameter_sanitizer.permit(:sign_up, keys: [:nickname])`ですね。

今回はnicknameなしでは登録できないようにしようと思うので、モデルを編集します。以下を追記します。

```ruby:app/models/user.rb
〜省略〜
  validates :nickname ,presence: true
〜省略〜
```

あとはビューにニックネームを入力する欄を加えれば完了ですね。
あれ？ビューファイルはどこにありますか？
ありません。deviseをデフォルトのまま使うにはビューファイルとコントローラファイルの編集が必要ないので見えないところに隠されています。これを編集するには表に出させる必要があります。

```bash:Terminal
$ rails g devise:views
```

すると、隠れていたビューファイル達が表に出てきます。当たり前ですが、ただ表に出てきただけなので編集しなければ何も変わりません。
以下を追記します。

```:app/views/devise/registrations/new.html.erb
<div class="field">
  <%= f.label :nickname %><br />
  <%= f.text_field :nickname %>
</div>
```

`autofocus: true`に関しては適切な場所に一箇所置きます。詳細は省きます。

これで登録してみます。
大丈夫そうですね。
## ②2ページ目用モデル
2ページ目で入力されるケータイ番号をデータベースに保存するには、usersテーブルにカラムを追加するのではなく新たにテーブルを作ります。新たにテーブルを作るには、

```bash:Terminal
$ rails g model cellphone
```
して、userテーブルに従属するので、

```ruby:db/migrate/2020xxxxxxxxxx_create_cellphones.rb
class CreateCellphones < ActiveRecord::Migration[5.2]
  def change
    create_table :cellphones do |t|
      t.integer :cellphone
      t.references :user
      t.timestamps
    end
  end
end
```
と編集して、

```bash:Terminal
$ rails db:migrate
```

です。慣れたもんです。

ここからモデルにバリデーションとアソシエーションを記述します。
まず`app/models/cellphone.rb`に`validates :cellphone ,presence: true`を加えます。バリデーションです。これは簡単ですね。
続いて、アソシエーションです。cellphaneモデルとuserモデルを同時に見てください。

```ruby:app/models/cellphone.rb
class Cellphone < ApplicationRecord
  validates :cellphone ,presence: true
  belongs_to :user, optional: true
end
```

```ruby:app/models/user.rb
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable
  validates :name ,presence: true
  has_one :cellphone
end
```

`belongs_to`メソッドと`has_one`メソッドが使われています。テーブル同士の関係が1対1の場合に使われます。1対1でどちらにどちらのメソッドを使うかというと、外部キーを置いていない側にbelongs_toを置きます。今回は後から作ったcellphoneの側のテーブルにuser_idカラムがあります。だから、cellphoneテーブルがuserテーブルに従属する(belongs)というイメージですね。そして逆側ににhas_oneを置くことになります。
`optional: true`が付いていますが、これはウィザード形式を実装するには必須です。(コントローラでCellphoneモデルのみでインスタンスを生成する場面があるからのようです)
## ③2ページ目用コントローラ
隠れていたビューファイルとコントローラファイルのうち前者は①で表に出していますが、ここでは後者のコントローラファイルを表に出して編集していく必要があります。　表に出してみましょう。

```bash:Terminal
$ rails g devise:controllers <コントローラのディレクトリ名>
```

コントローラのディレクトリ名はなんでも大丈夫ですが、通常モデル名の複数形なので`rails g devise:controllers users`にします。

さてここで話を整理しましょう。普通、登録画面に関するバックグラウンドの流れは、コントローラのnewアクションを通り、newのビューが表示され、そこのformにクライアントが情報が入力してsubmitが押されると、createアクションに来て、バリデーションを通過すればデータベースに登録するというものであります。図示するとこんな感じです。

```
newアクション　→　登録画面　→　createアクション　→　データベース
```

ここで2ページ目の登録画面を挟まなくてはならないので、

```
①newアクション　→　①登録画面　→　①createと②newのアクション　→　②登録画面　→　②createアクション　→　データベース
```
となります。
この`①createと②newのアクション`と`②createアクション`が難しいです。丁寧に行きましょう。

`①createと②newのアクション`は、`registrations_controller.rb`にもとからあるcreateアクション（コメントアウトされています）を使います。`②createアクション`は新たに適当にアクションを作ります。

まず`①createと②newのアクション`についてです。ここは①登録画面と②登録画面の橋渡しです。①登録画面の情報を受け取って変数(session変数という特別な変数)に格納して②登録画面に遷移します。

まず、最終的なcreateアクションの全体を掲載します。

```ruby:app/controllers/users/registrations_controller.rb
# frozen_string_literal: true

class Users::RegistrationsController < Devise::RegistrationsController
〜省略〜
  # POST /resource
  def create
    user = User.new(sign_up_params)
    unless user.valid?
      render :new and return
    end
    session[:registration] = {user: user.attributes}
    session[:registration][:user][:password] = params[:user][:password]
    @cellphone = user.build_cellphone
    render :new_cellphone
  end
〜省略〜
end

```

これを構築してゆきます。
まず、表に出した`app/controllers/users/registrations_controller.rb`のコメントアウトしているcreateアクションを復活させ、superを消して`binding.pry`をかませてみましょう。

```ruby:app/controllers/users/registrations_controller.rb
# frozen_string_literal: true

class Users::RegistrationsController < Devise::RegistrationsController
〜省略〜
  # POST /resource
  def create
    binding.pry
  end
〜省略〜
end
```

こんな感じです。`http://localhost:3000/users/sign_up`にアクセスして適当に値を埋めて`Sign up`ボタンを押すと停止するので、ターミナルを見ます。

まず①登録画面で入力された情報は

```bash:Terminal
> sign_up_params
```
で取れます。私の場合、

```bash:Output
=> {"email"=>"aaa@aaa", "password"=>"123123123", "password_confirmation"=>"123123123", "name"=>"annaPanda"}
```

こんな感じです。`sign_up_params`とはdeviseが準備しているメソッドでしょう。
さてこの`sign_up_params`を代入してUserモデルのインスタンスを生成してみましょう。

```bash:Terminal
> aaa = User.new(sign_up_params)
```


```bash:Output
=> #<User id: nil, email: "aaa@aaa", created_at: nil, updated_at: nil, nickname: "annaPanda">
```

これでバリデーションを通過できるかどうかは`valid?`メソッドを使います。

```bash:Terminal
> aaa.valid?
```


```bash:Output
  User Exists (0.8ms)  SELECT  1 AS one FROM `users` WHERE `users`.`email` = BINARY 'aaa@aaa' LIMIT 1
  ↳ (pry):3
=> true
```

trueって出ました。OKなようです。
続いて、②登録画面に引き継ぐべき値は`attributes`メソッドで取れます。

```bash:Terminal
> aaa.attributes
```

```:Output
=> {"id"=>nil,
 "email"=>"aaa@aaa",
 "encrypted_password"=>"$2a$11$W/lFietoiCkM6Lj6bsuY2eTsvGSg7nakpLCisng73tpv5bp.8OGFu",
 "reset_password_token"=>nil,
 "reset_password_sent_at"=>nil,
 "remember_created_at"=>nil,
 "created_at"=>nil,
 "updated_at"=>nil,
 "nickname"=>"annaPanda"}

```

パスワードが`encrypted_password`で暗号化されていますね。ですが引き継ぐ段階では元のパスワードが必要なのでそれを引き出します。params変数から、

```bash:Terminal
> params[:user][:password]
```

```bash:Output
=> "123123123"
```

このように取れるのはわかりますね。
さてここでsession変数を使います。普通変数などの値は、ブラウザが読み込まれるたびに一度クリアになります。これは"ステートレス"というHTTP通信の性質です。これに抗って値を保持できるのがsession変数です。
格納するsessionに付随する[:registration]や[:user]や[:password]の名前はなんでもOKですが今回はこうします。
あとはcellphone用のインスタンスを生成できればOKです。これにはnewメソッド出なく特別な`build_<テーブル名>`メソッドを使います。

```bash:Terminal
> aaa.build_cellphone
```

```bash:Output
=> #<Cellphone:0x00007fc4c5d1db20 id: nil, cellphone: nil, user_id: nil, created_at: nil, updated_at: nil>
```

これらの情報を元にcreateアクションを構築すると上のようになるわけです。
再掲します。

```ruby:app/controllers/users/registrations_controller.rb
# frozen_string_literal: true

class Users::RegistrationsController < Devise::RegistrationsController
〜省略〜
  # POST /resource
  def create
    user = User.new(sign_up_params)
    unless user.valid?
      render :new and return
    end
    session[:registration] = {user: user.attributes}
    session[:registration][:user][:password] = params[:user][:password]
    @cellphone = user.build_cellphone
    render :new_cellphone
  end
〜省略〜
end

```

そしてこのコントローラが使用されるにはルーティングを変更する必要があります。

```config/routes.rb
Rails.application.routes.draw do
  get 'messages/index'
  devise_for :users, controllers: {
    registrations: 'users/registrations',
  }
  devise_scope :user do
    get 'cellphones', to: 'users/registrations#new_cellphone'
    post 'cellphones', to: 'users/registrations#create_cellphone'
  end
  root "messages#index"
end
```
`devise_for :users`の先に加えられているのがcreateアクションへのルーティングです。これがないと上で書いたcreateアクションが使われません。
2ページ目用は`devise_scope`メソッドを使います。その先は普通にURIを設定しているだけですね。

`②登録画面`のビューは以下です。ファイル自体もないので作ります。

```app/views/devise/registrations/new_cellphone.html.erb
<%= form_for @cellphone do |f| %>
  <%= render "devise/shared/error_messages", resource: @cellphone %>

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

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

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

続いて、`②createアクション`です。名前はcreate_cellphoneです。

```ruby:app/controllers/users/registrations_controller.rb
# frozen_string_literal: true

class Users::RegistrationsController < Devise::RegistrationsController
〜省略〜
  def create_cellphone
    @user = User.new(session[:registration]["user"])
    @cellphone = Cellphone.new(cellphone_params)
    unless @cellphone.valid?
      flash.now[:alert] = @cellphone.errors.full_messages
      render :new_cellphone and return
    end
    @user.build_cellphone(@cellphone.attributes)
    @user.save
    sign_in(:user, @user)
    redirect_to root_path
  end

  protected

  def cellphone_params
    params.require(:cellphone).permit(:cellphone)
  end
〜省略〜
end
```
最終的にこうなります。これを構築してゆきます。

createアクションと同じように`binding.pry`をかませて見てみましょう。

```ruby:app/controllers/users/registrations_controller.rb
# frozen_string_literal: true

class Users::RegistrationsController < Devise::RegistrationsController
〜省略〜
  def create_cellphone
    binding.pry
  end
〜省略〜
end
```

`②createアクション`のアクション名は`create_<テーブル名>`でないとならないみたいですね。

まず、session変数に格納されている値を引き出してみましょう。

```bash:Terminal
> session[:registration][:user]
```

```bash:Output
=> nil
```
あれれ？なんででしょう。仕方ないので[:user]を抜いて見てみましょう。

```bash:Terminal
> session[:registration]
```

```bash:Output
=> {"user"=>
  {"id"=>nil,
   "email"=>"bbb@bbb",
   "encrypted_password"=>"$2a$11$Gp6J1spbcfu4EiS6EfqJsuGjZV1GB9LZXpWTzTBNDTY0GBmsIDDgm",
   "reset_password_token"=>nil,
   "reset_password_sent_at"=>nil,
   "remember_created_at"=>nil,
   "created_at"=>nil,
   "updated_at"=>nil,
   "nickname"=>"annaPanda",
   "password"=>"321321321"}}
```

取れました。`"user"`になっているみたいなんでそれに変えてみましょう。

```:Terminal
> session[:registration]["user"]
```

```bash:Output
=> {"id"=>nil,
 "email"=>"bbb@bbb",
 "encrypted_password"=>"$2a$11$Gp6J1spbcfu4EiS6EfqJsuGjZV1GB9LZXpWTzTBNDTY0GBmsIDDgm",
 "reset_password_token"=>nil,
 "reset_password_sent_at"=>nil,
 "remember_created_at"=>nil,
 "created_at"=>nil,
 "updated_at"=>nil,
 "nickname"=>"annaPanda",
 "password"=>"321321321"}
```

OKです。
次に②登録画面で入力されたケータイ番号を引き出してみましょう。
通常のストロングパラメータと同じですね。

```:Terminal
> params.require(:cellphone).permit(:cellphone)
```

```bash:Output
=> <ActionController::Parameters {"cellphone"=>"09011111111"} permitted: true>
```

さてコントローラ全体はこんな感じになります。

```ruby:app/controllers/users/registrations_controller.rb
# frozen_string_literal: true

class Users::RegistrationsController < Devise::RegistrationsController
〜省略〜
  def create
    user = User.new(sign_up_params)
    unless user.valid?
      render :new and return
    end
    session[:registration] = {user: user.attributes}
    session[:registration][:user][:password] = params[:user][:password]
    @cellphone = user.build_cellphone
    render :new_cellphone
  end

  def create_cellphone
    @user = User.new(session[:registration]["user"])
    @cellphone = Cellphone.new(cellphone_params)
    unless @cellphone.valid?
      flash.now[:alert] = @cellphone.errors.full_messages
      render :new_cellphone and return
    end
    @user.build_cellphone(@cellphone.attributes)
    @user.save
    sign_in(:user, @user)
    redirect_to root_path
  end

  protected

  def cellphone_params
    params.require(:cellphone).permit(:cellphone)
  end
〜省略〜
end
```
これで完成です。
まだケータイ番号用に桁数を制限などはしていません。余裕があったら挑戦してみてください。