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


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

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

環境
Ruby:   2.5.6
Rails:  5.2.4
devise: 4.7.3

docker内で作業しますので、bundle execがついていますが、
docker外の方は外して入力してみてください。

devise導入

1.Gemのインストール

今回はFacebook認証も行いたかったので、deviseomniauth-twitterをGemfileへ追加

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を記載します。

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

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

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

 config/routes.rbにてURLを設定

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

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

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

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

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

devise設定

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

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

deviseモデル作成

今回は一般の方と企業の2パターンを作ろうと思います!

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

マイグレーションファイルを確認

(モデル名以外同様のファイルが生成されているので、companyモデルのファイルは省略します)

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認証を取り入れるため、:omniauthableomniauth_providers: [:facebook]を追記しました。

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を確認

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だけ離れてしまいました...
  めんどくさくて直せていないので、右にスライドしてください・・・!)

ターミナル
          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

ルーディング修正

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を実行した際に対応しなかった部分です。

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

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

コントローラーの作成

ターミナル
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_inhttp://localhost:3000/companies/sign_in
どちらでアクセスしてもログイン画面が出ると思います!

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

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