【Rails】devise gemを使用して複数のモデルを管理する

RailsのdeviseというGemを使用してログイン機能等を作る際、
一般の方と企業でログイン画面や管理を分けたいなという時があると思います。

deviseを使用してWebアプリケーションの制作を行ったことはありますが、
今回初めて複数のモデルで実装を行ったため備忘録として残しておきたいと思います。

```ruby:環境
Ruby:   2.5.6
Rails:  5.2.4
devise: 4.7.3
```
docker内で作業しますので、`bundle exec`がついていますが、
docker外の方は外して入力してみてください。

#devise導入

###1.Gemのインストール
今回はFacebook認証も行いたかったので、`devise`と`omniauth-twitter`をGemfileへ追加

```ruby:Gemfile
gem 'devise'
gem 'omniauth-facebook'
```

そして`bundle install`

###2.devise関連のファイル作成
ターミナルで下記コマンドを入力

```
bundle exec rails g devise:install
```

以下のような文言が出てくると思います

```
create  config/initializers/devise.rb
      create  config/locales/devise.en.yml
===============================================================================

Depending on your application's configuration some manual setup may be required:

  1. Ensure you have defined default url options in your environments files. Here
     is an example of default_url_options appropriate for a development environment
     in config/environments/development.rb:

       config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }

     In production, :host should be set to the actual host of your application.

     * Required for all applications. *

  2. Ensure you have defined root_url to *something* in your config/routes.rb.
     For example:

       root to: "home#index"
     
     * Not required for API-only Applications *

  3. Ensure you have flash messages in app/views/layouts/application.html.erb.
     For example:

       <p class="notice"><%= notice %></p>
       <p class="alert"><%= alert %></p>

     * Not required for API-only Applications *

  4. You can copy Devise views (for customization) to your app by running:

       rails g devise:views
       
     * Not required *

===============================================================================
```

それぞれ内容を確認して追加をしていきます。

1.`config/environments/development.rb`に以下の文言を追加してねという内容。
　　言われた通り、デフォルトのURLを記載します。

```ruby:config/environments/development.rb
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
```

2.`config/routes.rb`にrootでアクセスした時（１で設定したURLにアクセスした時)のURLを設定してねという内容
  私はまだ作成していなかったので、ここで作成します。

```ruby:ターミナル
bundle exec rails g controller Posts(コントローラー名) index
```

　`config/routes.rb`にてURLを設定

```ruby:config/routes.rb
Rails.application.routes.draw do
  root 'posts#index'
end
```


3.`app/views/layouts/application.html.erb`にflashメッセージを追加してねという内容
　　とりあえず記載されているものを書きました。

```ruby:app/views/layouts/application.html.erb
.
.
.
  <body>
    <p class="notice"><%= notice %></p>
    <p class="alert"><%= alert %></p>

    <%= yield %>
  </body>
</html>
```

4.viewを生成してねと言われていますが、２パターン作りたいので今は無視します！

#devise設定
ログインユーザーを２パターンにするために`config/initializers/devise.rb`の設定を変更していきます。
元々コメントアウトされているので探してください！

```ruby:config/initializers/devise.rb(変更前)
.
#config.scoped_views = false
.
.
#config.sign_out_all_scopes = true
.
```

```ruby:config/initializers/devise.rb(変更後)
# 複数のmodelでログイン画面を分けるために変更
config.scoped_views = true
# 複数のmodelでをグインしている際、一方をログアウトした時にもう片方もログアウトしてしまうことを防ぐ
config.sign_out_all_scopes = false
```

#deviseモデル作成
今回は一般の方と企業の２パターンを作ろうと思います！

```ruby:ターミナル
bundle exec rails g devise user
bundle exec rails g devise company
```

###マイグレーションファイルを確認
（モデル名以外同様のファイルが生成されているので、companyモデルのファイルは省略します）

```ruby:db/migrate/20201112105533_devise_create_users.rb
# frozen_string_literal: true

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: ""

      ## 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 :users, :email,                unique: true
    add_index :users, :reset_password_token, unique: true
    # add_index :users, :confirmation_token,   unique: true
    # add_index :users, :unlock_token,         unique: true
  end
end
```

###modelファイルを確認
Facebook認証を取り入れるため、`:omniauthable`と`omniauth_providers: [:facebook]`を追記しました。

```ruby: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, :omniauthable, omniauth_providers: [:facebook]
end
```

変更や追加ができたところで`bundle exec rails db:migrate`

#ルーディングの確認

###config/routes.rbを確認

```ruby:config/routes.rb
Rails.application.routes.draw do
  devise_for :companies
  devise_for :users
  root 'postss#index'
end
```

###bundle exec rails routesで確認
Controller#Actionの部分がcompanyとuserで同じになってしまっている。
つまり、controllerが同一になってしまっているので変更する必要がある。
（＊Controller#Actionだけ離れてしまいました...
　　めんどくさくて直せていないので、右にスライドしてください・・・！）

```ruby:ターミナル
　　　　　　　　　　Prefix Verb  　　　　　　　　   URI Pattern                                                                          Controller#Action
             new_company_session GET      /companies/sign_in(.:format)                                                            devise/sessions#new
                 company_session POST     /companies/sign_in(.:format)                                                            devise/sessions#create
         destroy_company_session DELETE   /companies/sign_out(.:format)                                                           devise/sessions#destroy
            new_company_password GET      /companies/password/new(.:format)                                                       devise/passwords#new
           edit_company_password GET      /companies/password/edit(.:format)                                                      devise/passwords#edit
                company_password PATCH    /companies/password(.:format)                                                           devise/passwords#update
                                 PUT      /companies/password(.:format)                                                           devise/passwords#update
                                 POST     /companies/password(.:format)                                                           devise/passwords#create
     cancel_company_registration GET      /companies/cancel(.:format)                                                             devise/registrations#cancel
        new_company_registration GET      /companies/sign_up(.:format)                                                            devise/registrations#new
       edit_company_registration GET      /companies/edit(.:format)                                                               devise/registrations#edit
            company_registration PATCH    /companies(.:format)                                                                    devise/registrations#update
                                 PUT      /companies(.:format)                                                                    devise/registrations#update
                                 DELETE   /companies(.:format)                                                                    devise/registrations#destroy
                                 POST     /companies(.:format)                                                                    devise/registrations#create
                new_user_session GET      /users/sign_in(.:format)                                                                 devise/sessions#new
                    user_session POST     /users/sign_in(.:format)                                                                 devise/sessions#create
            destroy_user_session DELETE   /users/sign_out(.:format)                                                                devise/sessions#destroy
user_facebook_omniauth_authorize GET|POST /users/auth/facebook(.:format)                                                           devise/omniauth_callbacks#passthru
 user_facebook_omniauth_callback GET|POST /users/auth/facebook/callback(.:format)                                                  devise/omniauth_callbacks#facebook
               new_user_password GET      /users/password/new(.:format)                                                            devise/passwords#new
              edit_user_password GET      /users/password/edit(.:format)                                                           devise/passwords#edit
                   user_password PATCH    /users/password(.:format)                                                                devise/passwords#update
                                 PUT      /users/password(.:format)                                                                devise/passwords#update
                                 POST     /users/password(.:format)                                                                devise/passwords#create
        cancel_user_registration GET      /users/cancel(.:format)                                                                  devise/registrations#cancel
           new_user_registration GET      /users/sign_up(.:format)                                                                 devise/registrations#new
          edit_user_registration GET      /users/edit(.:format)                                                                    devise/registrations#edit
               user_registration PATCH    /users(.:format)                                                                         devise/registrations#update
                                 PUT      /users(.:format)                                                                         devise/registrations#update
                                 DELETE   /users(.:format)                                                                         devise/registrations#destroy
                                 POST     /users(.:format)                                                                         devise/registrations#create
```

###ルーディング修正
```ruby:config/routes.rb
devise_for :companies, controllers: {
    sessions:      'companies/sessions',
    passwords:     'companies/passwords',
    registrations: 'companies/registrations'
  }
  devise_for :users, controllers: {
    sessions:           'users/sessions',
    passwords:          'users/passwords',
    registrations:      'users/registrations',
    omniauth_callbacks: 'users/omniauth_callbacks'
  }
```

もう一度`bundle exec rails routes`で確認していただくと無事controllerの部分が変更されていると思います！

#ビューの作成
最初の`rails g devise:install`を実行した際に対応しなかった部分です。

```ruby:ターミナル
bundle exec rails g devise:views users
bundle exec rails g devise:views companies
```

それぞれファイルが生成されたかと思います！

#コントローラーの作成

```ruby:ターミナル
bundle exec rails generate devise:controllers users
bundle exec rails generate devise:controllers companies
```
こんな感じの実行結果になるかと思います。

```
create  app/controllers/users/confirmations_controller.rb
      create  app/controllers/users/passwords_controller.rb
      create  app/controllers/users/registrations_controller.rb
      create  app/controllers/users/sessions_controller.rb
      create  app/controllers/users/unlocks_controller.rb
      create  app/controllers/users/omniauth_callbacks_controller.rb
===============================================================================

Some setup you must do manually if you haven't yet:

  Ensure you have overridden routes for generated controllers in your routes.rb.
  For example:

    Rails.application.routes.draw do
      devise_for :users, controllers: {
        sessions: 'users/sessions'
      }
    end

===============================================================================
```

下の部分、まさかのエラー！？と思いましたが、エラーじゃないです。（ほっ
controllerがこんな感じだから、追加でroutes.rbに以下のような記述が必要だよ〜
と例を出して教えてくれています。

先ほど設定しているので無視で大丈夫です！

#おまけ（omniauth認証を導入する方）
このままだと`http://localhost:3000/users/sign_in`にアクセスしてもエラーになると思います。
``undefined method `omniauth_authorize_path' for　〜``みたいなやつが。。

Facebook認証の設定についてはここに記載しませんが、
app/views/users/shared/_links.html.erbの
omniauth_authorize_path(resource_name, provider)の部分がおかしいです。

ルーディングを確認すると`omniauth_authorize_path`というpathはなく、`user_facebook_omniauth_authorize_path`となっているはずなので修正しましょう。

asを使用して名前を変更したわけでもないのになんで・・・？と思いましたが、
理由はよくわからなかったです。最初からなのかな・・・？

#とりあえず完成
何はともあれ！とりあえず基本的な設定部分は完成です。

`http://localhost:3000/users/sign_in`と`http://localhost:3000/companies/sign_in`
どちらでアクセスしてもログイン画面が出ると思います！

必要であればカラムを増やしたりカスタマイズしてください！

この記事がどなたかのお役に立てたら嬉しいです。
ありがとうございました！