【Rails】deviseで二つのモデルを管理する【Ruby】

現在作成中のアプリでは、UserとShop、二つのモデルが必要になってきます。
deviseを用いて二つのモデルのユーザー管理機能を実装しました。

# 環境
```
Ruby 2.6.5
rails 6.0.3.4
devise  4.7.3
```

#目次
[1.deviseのインストール](#1-deviseのインストール)
[2.configの変更](#2-configの変更)
[3.モデルの作成](#3-モデルの作成)
[4.コントローラーの作成](#4-コントローラーの作成)
[5.ビューの作成](#5-ビューの作成)
[6.ルーティングの設定](#6-ルーティングの設定)
[7.ストロングパラメーターの設定](#7-ストロングパラメーターの設定)



#1. deviseのインストール

``` ruby:Gemfile
#[--- 中略 ---]# 
gem 'devise'
```
```ruby:ターミナル
bundle install
rails g devise:install 
```

ここまでは一つのモデルを管理する時と同じです。

#2. configの変更

```ruby:/config/initializers/devise.rb
#[--- 中略 ---]# 

# ==> Scopes configuration
  # Turn scoped views on. Before rendering "sessions/new", it will first check for
  # "users/sessions/new". It's turned off by default because it's slower if you
  # are using only default views.
  # config.scoped_views = false

#[--- 中略 ---]# 
```

の

```
# config.scoped_views = false
```

を

```
config.scoped_views = true   # コメントアウトは外します
```

に変更。

deviseをインストールすると、config/initializer/devise.rbファイルが作成されます。
デフォルトの設定ではdeviseによって作成されたviewファイルは変更できません。
そのため、config.scoped_views = falseをtrueに変えることでビューファイルの変更を可能にします。


#3. モデルの作成

```ruby:ターミナル
rails g model user
rails g model shop
```

それぞれのモデルを作成します。

#4. コントローラーの作成

```ruby:ターミナル
rails g controllers users
rails g controllers shops
```

それぞれのコントローラーを作成します。

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

上記のように各機能ごとにコントローラーが作成されればOKです。


#5. ビューの作成

```ruby:ターミナル
rails g devise:views users
rails g devise:views shops
```

それぞれのビューを作成します。

#6. ルーティングの設定

```ruby:config/route.rb
devise_for :users
devise_for :shops
#[--- 中略 ---]# 
```

を

```ruby:config/routes.rb
devise_for :users, controllers: {
  sessions:      'users/sessions',
  passwords:     'users/passwords',
  registrations: 'users/registrations'
}
devise_for :shops, controllers: {
  sessions:      ‘shops/sessions',
  passwords:     ‘shops/passwords',
  registrations: ‘shops/registrations'
}

#[--- 中略 ---]# 

```

に変更。

元々の設定だと、users,shops共にdeviseコントローラーを通ってしまいます。先ほど作成したそれぞれのコントローラーを通るために、上記のように書き換えましょう。



#7. ストロングパラメーターの設定

`controllers/users/parameter_sanitizer.rb`
`controllers/shops/parameter_sanitizer.rb`
の二つのファイルを新たに作成します。
(`lib`配下に置いたほうがいいかとは思ったのですが、上手くいかなかったので今回はそれぞれのコントローラーの配下に置きました。よりいい書き方があればご指摘いただきたいです。)

```ruby:controllers/users/parameter_sanitizer.rb
class Users::ParameterSanitizer < Devise::ParameterSanitizer
  def initialize(*)
    super
    permit(:sign_up, keys: [:nickname,:first_name, :last_name, :first_kana, :last_kana, :birthday, :gender_id] )
  end
end
```

```ruby:controllers/shops/parametersanitizer.rb
class Shops::ParameterSanitizer < Devise::ParameterSanitizer
  def initialize (*)
    super
    permit(:sign_up, keys: [:shop_name,:administrator, :postal, :prefecture_id, :address, :building, :nearest_station, :genre_id])
  end
end
```

```ruby:applicatioin_controller.rb
class ApplicationController < ActionController::Base
  protected

  def devise_parameter_sanitizer
    if resource_class == User
      Users::ParameterSanitizer.new(User, :user, params)
    elsif resource_class == Shop
      Shops::ParameterSanitizer.new(Shop, :shop, params)
    else
      super
    end
  end
end
```



デフォルトの設定では、パラメーターとしてemailとパスワードしか受け取ることができません。
今回は他の項目も追加しているため、ストロングパラメーターの設定をする必要があります。
一つのモデルだけ管理する場合は、devise_parameter_sanitizerメソッドをapplication.controller内に記述することで設定できましたが、今回は二つのモデルを使用しています。
そのため、Devise::ParameterSanitizerクラスを継承したUsers::ParameterSanitizerクラスと、Shops::ParameterSanitizerクラスを作成します。
（ちなみに私は「s」を抜かしてUser::ParameterSanitizerと記述してしまっていたために、しばらくハマってしましました。公式のリポジトリに書いてあるんだもん。。。コントローラーの名称に対応するみたいですね。）

<br>

間違っている点などあればご指摘お願いいたします。

# 参考URL
https://qiita.com/Yama-to/items/54ab4ce08e126ef7dade
https://github.com/heartcombo/devise#strong-parameters