1 Railsでエンドユーザーのサインアップとログイン機能、管理者のログイン機能を実装

## 1 Deviseの導入
#### (1)Gemfileに以下を追記する

```
gem 'devise'
```
#### (2) bundle intallを実行する

## 2 顧客 , 管理者用のモデルを作成する

顧客のモデルを作成する
```
rails g model devise Enduser
```

管理者用のモデルを作成する
```
rails g model Admin
```

## 3 顧客 , 管理者用のテーブルを作成する
それぞれのマイグレーションファイルに以下を追記する

devise_create_end_users.rb
```Ruby
      t.string :first_name  名前　姓
      t.string :katakana_first_name 名前  カナ　姓
      t.string :last_name  名前 名
      t.string :katakana_last_name  名前 カナ 名
      t.string :postal_code  郵便番号
      t.string :address  住所
      t.string :telephone_number  電話番号
```

###### Adminは特に、追記することなし

devise_create_admins
```Ruby
class DeviseCreateAdmins < ActiveRecord::Migration[5.2]
  def change
    create_table :admins do |t|
      ## Database authenticatable
      t.string :email,              null: false, default: ""
      t.string :encrypted_password, null: false, default: ""

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

      ## Rememberable
      t.datetime :remember_created_at

      ## Trackable
      # t.integer  :sign_in_count, default: 0, null: false
      # t.datetime :current_sign_in_at
      # t.datetime :last_sign_in_at
      # t.string   :current_sign_in_ip
      # t.string   :last_sign_in_ip

      ## Confirmable
      # t.string   :confirmation_token
      # t.datetime :confirmed_at
      # t.datetime :confirmation_sent_at
      # t.string   :unconfirmed_email # Only if using reconfirmable

      ## Lockable
      # t.integer  :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
      # t.string   :unlock_token # Only if unlock strategy is :email or :both
      # t.datetime :locked_at


      t.timestamps null: false
    end

    add_index :admins, :email,                unique: true
    add_index :admins, :reset_password_token, unique: true
    # add_index :admins, :confirmation_token,   unique: true
    # add_index :admins, :unlock_token,         unique: true
  end
end

```

migrationファイルをデータベースに反映させる

```
rails db:migrate
```

## 4 コントローラを作成する

###### devise用のコントローラを作成する
1 使用する機能のみをルーティングを行える
2 ログイン前に特定のメソッドを実行することができるbefore_actionなどを使用できる
3 デフォルトのDeviseの設定のみでは実現することができない機能を追加できるようになる

顧客用のdeviseのコントローラを作成する
```
 rails g devise:controllers customer
```

管理者用のdeviseのコントローラを作成する
```
rails g devise:controllers admin
```

## 5 viewを作成する
```
rails g devise:views publics
```

```
rails g devise:views admins
```

この時viewフォルダが作成されるが名前を下記の通りに変更する

publics -> public

admins -> admin

#### <font color="Red">フォルダの名前を変更したので </font>
```
<%= render ~
```
#### <font color="Red">の記述を変更する必要がある!!</font>

###### 管理者側のViewの編集

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

###### 顧客側のview編集
```
<%= render "customer/shared/links" %>
```

###### customer/registrations/new.html.erb
```
<%= render "customer/shared/error_messages", resource: resource %>
```

## 6 ルーティングの編集
devise_for :customers
devise_for :admins
これらで作られたルーティングでは、、、

###### 1 先程作成したコントローラーの記述を変更してもその処理を行うことができない。
###### 2 URLに関してもアプリケーション詳細設計の通りに実装を行うことができない

=> このルーティングを消して新たなルーティングを↓のように作り直す。


#### (1)ルーティングを作り直す

###### 顧客のコントローラに関するルーティング
```
devise_for :end_users, controllers: {
  registrations: "public/registrations",
  sessions: 'public/sessions'
}
```

###### 管理者のコントローラに関するルーティング

```
devise_for :admin, controllers:{
    sessions: "admin/sessions"
  }
```

##### <font color="Red">ポイント</font>
###### このように記述することで
###### publicフォルダとadminフォルダでsessions_controller.rbが2つあるので
###### どちらのsessions_controller.rbを参照するのか指定出来る。

#### (2)不要なルーティングを削除する
skipオプションを使ってルーティングを削除する

顧客側のルーティング
```Ruby
 devise_for :end_users , path: 'public',  skip: [:passwords,],controllers: {
    registrations: "public/registrations",
    sessions: 'public/sessions'
  }

```

管理者側のルーティング
```Ruby
devise_for :admin, skip: [:registrations, :passwords] ,controllers: {
  sessions: "admin/sessions"
}
```

##### <font color="Red">ポイント</font>

1 skipというオプションを使うことで指定したコントローラのルーティングを削除する

2 デフォルトのURLの先頭を変える方法
 [RailsのdeviseのController,View,URLのカスタマイズ（初心者向け）](https://qiita.com/70yama/items/dbf428a51830e70aa9bd)より、
end_users/~をpublic/~にする方法
```
path: 'public'
```
を追記する

#### (3) 削除したルーティングへのリンクを削除する
app/views/public/shared/_links.html.erb

```Ruby
<%- if controller_name != 'sessions' %>
  <%= link_to "Log in", new_session_path(resource_name) %><br />
<% end %>

<%- if devise_mapping.registerable? && controller_name != 'registrations' %>
  <%= link_to "Sign up", new_registration_path(resource_name) %><br />
<% end %>

--------------------------------ここから削除--------------------------------
<%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %>
  <%= link_to "Forgot your password?", new_password_path(resource_name) %><br />
<% end %>

<%- if devise_mapping.confirmable? && controller_name != 'confirmations' %>
  <%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %><br />
<% end %>

<%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %>
  <%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %><br />
<% end %>

<%- if devise_mapping.omniauthable? %>
  <%- resource_class.omniauth_providers.each do |provider| %>
    <%= link_to "Sign in with #{OmniAuth::Utils.camelize(provider)}", omniauth_authorize_path(resource_name, provider), method: :post %><br />
  <% end %>
<% end %>
--------------------------------ここまで削除--------------------------------
```

app/views/admin/shared/_links.html.erb

```Ruby
<%- if controller_name != 'sessions' %>
  <%= link_to "Log in", new_session_path(resource_name) %><br />
<% end %>

--------------------------------ここから削除--------------------------------
<%- if devise_mapping.registerable? && controller_name != 'registrations' %>
  <%= link_to "Sign up", new_registration_path(resource_name) %><br />
<% end %>

<%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %>
  <%= link_to "Forgot your password?", new_password_path(resource_name) %><br />
<% end %>

<%- if devise_mapping.confirmable? && controller_name != 'confirmations' %>
  <%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %><br />
<% end %>

<%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %>
  <%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %><br />
<% end %>

<%- if devise_mapping.omniauthable? %>
  <%- resource_class.omniauth_providers.each do |provider| %>
    <%= link_to "Sign in with #{OmniAuth::Utils.camelize(provider)}", omniauth_authorize_path(resource_name, provider), method: :post %><br />
  <% end %>
<% end %>
--------------------------------ここまで削除--------------------------------
```

## 7 エンドユーザーのサインアップにフォームを作成する

```
<h2>Sign up</h2>

<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
  <%= render "public/shared/error_messages", resource: resource %>

  <div class="field">
    <%= f.label :first_name %><br />
    <%= f.text_field :first_name ,autofocus: true%>
  </div>

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

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

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



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

  <div class="field">
    <%= f.label :address %><br />
    <%= f.text_area :address %>
  </div>

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

  <div class="field">
    <%= f.label :telephone_number %><br />
    <%= f.text_field :telephone_number %>
  </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 "public/shared/links" %>
```

## 8 エンドユーザー , 管理者のトップページを作成する

###### (1)コントローラーを作成する

管理者のtopページのコントローラを作成する
```
rails g controller admin/homes
```

エンドユーザーのtopページのコントローラを作成する
```
rails g controller public/homes
```

###### (2)アクションを追加する

管理者
```Ruby
class Admin::HomesController < ApplicationController
  def top
  end
end
```

エンドユーザー
```Ruby
class Public::HomesController < ApplicationController
  def top
  end
end
```

###### (3) viewを作成する
管理者
app/views/admin/homes/top.html.erb
```
<h1>管理者トップページ</h1>
```
app/views/public/homes/top.html.erb
```
<h1>エンドユーザ#トップページ</h1>
```

###### (4) ルーティングを作成する

 管理者用
```Ruby
    namespace :admin do
        get '/' => 'homes#top'
    end
```

エンドユーザ用
```Ruby
root to: 'public/homes#top'
```

## 9 エンドユーザーのマイページを作成する
###### (1) コントローラーを作成する
```
rails g controller public/customers
```

###### (2) アクションを定義する

```Ruby
def show
    @customer = current_end_user
end
```

###### (3) ビューを作成する
public/customers/show.html.erb
```
<h1>エンドユーザ#マイページ</h1>
```

###### (4) ルーティングを作成する
```Ruby
get 'customers/mypage' => 'public/customers#show'
```

## 10 ログアウトetc...の遷移先を設定する

エンドユーザーの新規登録時の遷移を設定する
app/controllers/public/registrations_controller.rb

```Ruby
サインアップした遷移先
def after_sign_up_path_for(resource)
    customers_mypage_path
end
```
app/controllers/public/sessions_controller.rb
```Ruby
ログインした遷移先
def after_sign_in_path_for(resource)
   root_path
end

ログアウトした遷移先
def after_sign_out_path_for(resource)
   root_path
end
```

## 11 seedでadminにログインできるようにする

seedとは、、、
seedファイルにデータベースへのデータ投入の処理を追記していき、
railsコマンドを実行することでデータベースに初期データとしてデータが投入される

###### (1) seedファイルに初期データを記述する

```
Admin.create!(email: '123@456', password: '123456')
```
モデル名.create(カラム名: 入れるデータ)で作成する

###### (2)コマンドを実行する
```
$rails db:seed
```

### <font color="Red">エラー発生: seedできてなかった!?</font>

seeds.rb
```
Admin.create(email: '123@456',encrypted_password: '123456')
```
開発段階では、passwordのカラムはpassword
保存される時にencrypted_passwordというカラムになる

###### 解決法
seeds.rbの中身をこちらに変更し、コマンドを実行する
```
Admin.create!(email: '123@456', password: '123456')
```

!をつけるとcreate,update出来ない時、エラーを出力する 
(railsのエラーが原因で失敗した際、本来はrollbackと表示されるだけで原因がわからない。
!をつけることで分かるようになる)

### <font color="Red">エラー発生: エンドユーザーの新規登録ができない!?</font>

```Ruby
class Public::RegistrationsController < Devise::RegistrationsController

  def after_sign_up_path_for(resource)
    customers_mypage_path
  end

  protected

  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: [:first_name,:katakana_first_name,:last_name,:katakana_last_name,:postal_code,:address,:telephone_number,:telephone_number])
  end

end
```


新規登録時のターミナル
```SQL
Unpermitted parameters: :first_name, :katakana_first_name, :last_name, :katakana_last_name, :address, :postal_code, :telephone_number
   (0.1ms)  begin transaction
  ↳ /home/ec2-user/.rvm/gems/ruby-2.6.3/gems/activerecord-5.2.6/lib/active_record/log_subscriber.rb:98
  EndUser Exists (0.2ms)  SELECT  1 AS one FROM "end_users" WHERE "end_users"."email" = ? LIMIT ?  [["email", "2@2"], ["LIMIT", 1]]
  ↳ /home/ec2-user/.rvm/gems/ruby-2.6.3/gems/activerecord-5.2.6/lib/active_record/log_subscriber.rb:98
   (0.0ms)  rollback transaction
```

原因:
```Ruby
def configure_permitted_parameters
   devise_parameter_sanitizer.permit(:sign_up, keys: [:first_name,:katakana_first_name,:last_name,:katakana_last_name,:postal_code,:address,:telephone_number,:telephone_number])
end
```
この記述は、記述しただけでは使ってない。
before_action :configure_permitted_parameters, if: :devise_controller?と記述することで初めて使う

解決策: 
```Ruby
before_action :configure_permitted_parameters, if: :devise_controller?
```
を記述する