deviseでユーザー機能実装済みのアプリに管理者機能を追加

やまだゆう（[@yamaday0u](https://twitter.com/yamaday0u)）です。

今回は、Railsのdevise gemでログイン認証機能（ユーザー管理機能）を実装済みのアプリに、管理者機能を追加する実装例をご紹介します。

※2022年から技術系の記事は[個人ブログ](https://yamaday0u.net/category/programmer/)に投稿しております。ぜひこちらもご覧ください→[yamaday0u Blog](https://yamaday0u.net/category/programmer/)

## 目標
この記事では、

1. deviseでログイン認証機能を実装済みのアプリに
2. 新たなテーブルを追加せずに
3. 管理者機能を実装すること

を目標にします。

管理者とユーザーのテーブルを別々に作りたい場合は以下のブログ記事を参考にしてください。
[【Rails】deviseを使用して管理者と会員を作成する方法（初心者向け）（Nulfasのブログ）](https://nllllll.com/ruby-on-rails/rails-devise/)

## 前提
devise gemですでにユーザー管理機能を実装済み ＝ ユーザーを管理するusersテーブルが存在していること。
usersテーブルには最低限の以下のカラムが存在しています。

|カラム名|データ型|
|:--|:--|
|name|string|
|email|string|
|encrypted_password|string|

## 実装のポイント
既存のusersテーブルにboolean型のadminカラムを追加して、adminカラムの値がtrueのユーザーを管理者とします。

## 手順
### migrationファイルを作成
既存のusersテーブルにデータ型がboolaen型の`adminカラム`を追加するためにmigrationファイルを作成します。

```zsh:ターミナル
rails g migration add_admin_to_users admin:boolean
```

### migrationファイルの編集＆実行
作成したmigrationファイルに以下の通り記述します。

ここでのポイントは、初期値を`default: false`で指定しているところです。
これによりusersテーブルの既存のレコードにも初期値がfalseの状態でadminカラムが追加されます。

```db/migrate/2021XXXXXXXXXX_add_admin_to_users.rb
class AddAdminToUsers < ActiveRecord::Migration[6.0]
  def change
    add_column :users, :admin, :boolean, default: false
  end
end
```
ターミナルでマイグレートします。

```zsh:ターミナル
rails db:migrate
```

### 管理者ユーザーを作成
管理者ユーザー（のレコード）を`seeds.rb`ファイルを用いて作成します。

ここでのポイントはもちろん、adminカラムの値を`true`にしていることです。
後の工程で、adminカラムの値が`true`であるか否かを利用してログイン後のマイページの表示を切り替えます。

また、ぼくの場合、githubにアプリのソースコードを公開しているので、`.env`というgemを使って、メールアドレスやパスワードは環境変数を定義して指定しました。

```db/seeds.rb
User.create!(
  name: "管理者",
  email: ENV['ADMIN_EMAIL'],
  password: ENV['ADMIN_PASSWORD'],
  password_confirmation: ENV['ADMIN_PASSWORD'],
  admin: true
)
```
seedsファイルに記述した内容を実行します。

```zsh:ターミナル
rails db:seed
```

これで、管理者ユーザーが作成、usersテーブルに登録されました。

### Routeの定義
ぼくのポートフォリオアプリである[Group Calendar](https://group-calendar.net/)を例にrouteの定義を説明します。

通常のユーザーであれば、ログイン後にconfig/routes.rbに定義した`resources :calendars`によりマイページ（indexアクション）にリダイレクトします。

そこで管理者ユーザーの場合は、namespaceを利用して以下のように記述し、`admin/calendars`をマイページのpathにして、管理者ページであることを明示的にしました。

```config/routes.rb
namespace :admin do
  # 以下の記述により、admin/calendarsへのpathが開かれます。
  resources :calendars, only: %i[index]
end
```

### Controllerの作成その1
routeの定義で`admin/calendars`というpathを開いたので、以下のように`Admin::Calendars Controller`を作成します。

```zsh:ターミナル
rails g controller admin/calendars
```
作成したcontrollerに以下の通り記述します。

```app/controllers/admin/calendars_controller.rb
class Admin::CalendarsController < ApplicationController

  def index
    # 省略
  end

end
```

### Controllerの作成その2
管理者ユーザーにはニュースリリース機能を使えるようにしたいので、News Controllerを作成します。

```zsh:ターミナル
rails g controller news
```

作成したNews　Controllerに以下のように記述します。
private actionかつbefore_actionの`check_admin?`により、管理者以外のユーザーが管理者用のページであるニュースリリース関連機能のページを表示できないようにしています。

```app/controllers/news_controller.rb
class NewsController < ApplicationController
  before_action :check_admin?

  def index
    # 省略
  end

  # 中略

  private
  def check_admin?
    unless current_user.admin
      redirect_to root_path
    end
  end
end
```

### View
Viewはこのように書きます。
ログイン中のユーザー（current_user）のadminカラムの値が`true`なら、ニュースリリース機能関連のページへのリンクを表示します。

```app/views/news/_link_to_news_release_page.html.erb
<% if current_user.admin? %>
  <li><%= link_to "News Release", news_index_path %></li>
<% end %>
```

## 完成!!
以上でdeviseでログイン認証機能を実装済みのアプリに新たなテーブルを追加せずに管理者機能を実装する手順が完了しました。

ぼくの[ポートフォリオアプリ](https://group-calendar.net/)では、以下のように管理者ページにのみNews Release用のページリンクが表示されるようにしました。

管理者ユーザーのマイページ
![スクリーンショット 2021-10-11 21.11.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/849306/28645a1a-2038-aba5-2fda-a534871b5e7a.png)

一般ユーザーのマイページ
![スクリーンショット 2021-10-11 21.12.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/849306/37bd0f1c-d6cf-fb3e-c25a-de17b4a035bd.png)

Rails学習中のみなさまの参考になれば幸いです。

## ポートフォリオ
未経験からのエンジニア転職に向けて作ったポートフォリオを公開しています。
よろしければ覗いて見てください。
[Group Calendar](https://group-calendar.net/)