Rails 管理者画面作成 AdminLTE 解説

#AdminLTE 3 のインストール

```:ターミナル
yarn add admin-lte@^3.0
```
・node_modulesというディレクトリが作成される。

#管理者画面用のマニフェストファイルの設定

・管理者画面ではユーザー画面とレイアウトが全く別物の為、管理者画面とユーザー画面は分ける。
・「app/assets/javascripts/admin.js」「app/assets/stylesheets/admin.scss」ファイルを作成。

```:app/assets/javascripts/application.js
//= require jquery3
//= require activestorage
//= require rails-ujs
//= require bootstrap-sprockets
//= require cable.js
//= require tree. ←ここを削除
```

・`//= require tree.`はapplication.jsと同じ階層の全ファイルを読み込んでいる。今回作成した、admin.jsも同じ階層にあるので、それぞれのファイルにあるrails-ujsやjquery3が同時に読み込まれると二重になりエラーが起きる。

###別々にファイルを読み込むように設定

・それぞれのファイルに以下のように記述

```:app/assets/javascripts/admin.js
//= require jquery3
//= require rails-ujs
//= require admin-lte/plugins/bootstrap/js/bootstrap.bundle.min
//= require admin-lte/dist/js/adminlte
```

```:app/assets/stylesheets/admin.scss
@import 'admin-lte/plugins/fontawesome-free/css/all.min.css';
@import 'admin-lte/dist/css/adminlte.css';
```

#config/initializer/assets.rbの修正

```:config/initializer/assets.rb
Rails.application.config.assets.precompile += %w[admin.js admin.css] ←この部分をコメントアウト
```
・admin.js　admin.cssをプリコンパイルしている。

#一般ユーザーと管理者ユーザーを分ける

###Adminのコントローラーを作成

```:ターミナル
$ rails g controller Admin::Base
```
・adminディレクトリ階層を作成する理由は、レイアウトファイルなど、通常の一般ユーザー画面と別物にしたいのでこの`admin/base_controller.rb`をベースに作成していく。

```:ターミナル(ダッシュボード用のコントローラー)
$ rails g controller Admin::Dashboards index
```

```:ターミナル（ログイン用のコントローラー）
rails g controller Admin::User_sessions new
```

###UserモデルにAdminを判別するカラムを追加

```:ターミナル
$ rails g migration add_role_to_users
```

```rb:migrationファイル_add_role_to_users.rb
class AddRoleToUsers < ActiveRecord::Migration[5.2]
  def change
    add_column :users, :role, :integer, null: false, default: 0
  end
end
```
・roleというカラムでデータ型はstring(整数)、空は許可しない（null: false）,初期値には0(default:0)を指定。
・一般ユーザーと管理者ユーザーを判断する為にroleカラムを追加。（整数：stringで管理する）
・integer型は整数を保存する型で2個以上の定数と紐づけることができる。

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

###一般ユーザーを「0」、管理者ユーザーを「1」をenumを使って定義する。

```rb:app/models/user.rb
enum role: { general: 0, admin: 1 } #追加
```

・roleカラムに`0`が保存されていれば`general`,`1`が保存されていれば`admin`という定数で扱うということになる。＊後でroleを追加した際に数字を明示していないと、「"general"=>0, "admin"=>1」のように必ず紐ずくという状態にならない。

・enumを使うと、カラム（ここではrole）に指定した定数が入っているレコードを取り出すのが容易になる。

・現ユーザーがadmin（管理者）かどうかを判断したいときに`current_user.admin?`というものが使えるようになる。

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

```rb:config/routes.rb
  namespace :admin do
    root to: 'dashboards#index'
    get 'login', to: 'user_sessions#new'
    post 'login', to: 'user_sessions#create'
    delete 'logout', to: 'user_sessions#destroy'
  end
```

・`namespace`を使うことによってURLとファイル構成を指定のパス（admin）にすることができる。（以下のようになる）

```:ルーティング
admin_root GET    /admin(.:format)                                  admin/dashboards#index
              admin_login GET    /admin/login(.:format)             admin/user_sessions#new
                          POST   /admin/login(.:format)             admin/user_sessions#create
             admin_logout DELETE /admin/logout(.:format)            admin/user_sessions#destroy
```

#管理者画面のレイアウトを設定

```rb:controllers/base_controller.rb
class Admin::BaseController < ApplicationController
  layout 'admin/layouts/application'  #layoutを宣言
end
```
・現状のままでは、`ApplicationController`を継承しているので、レイアウト（共通するファイル）は`views/layouts/application.html.erb`になっている。
・`layout`を宣言する事で`admin/layouts/application`を設定する事ができる。
・先程作成した、ダッシュボード用コントローラ`（admin/dashboards_controller.rb）`とログイン用のコントローラー`(admin/user_sessions_controller.rb)`に`admin/base_controller.rb`を継承させていく。

```rb:app/controllers/admin/dashboards_controller.rb
class Admin::DashboardsController < Admin::BaseController ←#ここを変更
  def index; end
end
```
・`admin/base_controller.rb`を継承しているので、`layout 'admin/layouts/application'`と記述しなくてもよい。

```rb:app/controllers/admin/user_sessions_controller.rb
class Admin::UserSessionsController < Admin::BaseController ←#ここを変更

end
```
###管理者ユーザーレイアウト（テンプレート）

```HTML:app/views/admin/layouts/application.html.erb
<html>
<head>
  <meta charset="UTF-8">
  <meta lang='ja'>
  <meta name="robots" content="noindex, nofollow">
  <title><%= page_title(yield(:title), admin: true) %></title>
  <%= csrf_meta_tags %>
  <%= stylesheet_link_tag 'admin', media: 'all' %>
  <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,400i,700" rel="stylesheet">
</head>

<body class="hold-transition sidebar-mini layout-fixed">
<div class="wrapper">

  <%= render 'admin/shared/header' %>
  <%= render 'admin/shared/sidebar' %>

  <div class="content-wrapper">
    <%= render 'shared/flash_message' %>
    <%= yield %>
  </div>
  
  <%= render 'admin/shared/footer' %>

</div>
<%= javascript_include_tag 'admin' %>
</body>
</html>
```

#ダッシュボード用のview

```rb:app/views/admin/dashboards/index.html.erb
<% content_for(:title, t('.title')) %> 
<div class="content-wrapper">
  <div class="row">
   <p>ダッシュボードです</p>
  </div>
</div>
```
#タイトルを設定

```rb:app/helpers/application_helper.rb
module ApplicationHelper
   def page_title(page_title = '', admin = false)
     base_title = if admin
                    '学習用掲示板(管理画面)'
                  else
                    '学習用掲示板'
                  end

     page_title.empty? ? base_title : page_title + ' | ' + base_title
   end
```
・`「各ビューのタイトル　｜　固定タイトル」`のような形でタイトルを設定していく。
・viewと別の場所（helper）で定義しておくとviewから呼び出すことができる。
・メソッドの引数を柔軟に変えられるデフォルト値付き引数である`(page_title = '')`
を使って、引数あり・なしで表示を変えている。
・`empty?`メソッドは、タイトルが空の場合タイトルに`固定タイトル(base_title)`を表示。タイトルがあれば、`各ビューのタイトル | 固定タイトル(base_title)`を表示。

###管理者用共通レイアウトに読み込む

```rb:app/views/admin/layouts/application.html.erb
<title><%= page_title(yield(:title), admin: true) %></title>　
```
・`yield(:title)`個別レイアウト(各view)のタイトル

###個別レイアウト(各view)に読み込む
```rb:
<% content_for(:title, t('.title')) %>  #titleは翻訳ファイルに記入
```
・`content_for`個別レイアウトから共通レイアウトに表示する時に使用

#コントローラー

・作成した`base_controller`に追記していく

```rb:app/controllers/base_controller.rb
class Admin::BaseController < ApplicationController
  before_action :check_admin
  layout 'admin/layouts/application'

  private

  def not_authenticated
    flash[:warning] = t('defaults.message.require_login')
    redirect_to admin_login_path
  end

  def check_admin
    redirect_to root_path, warning: t('defaults.message.not_authorized') unless current_user.admin?
  end
end
```
・privateに記述した`before_action :check_admin`でログインした`current_user`が`admin`かを判定している。
・enumでroleカラムの整数をadminと定義したので、`admin?`が使用できる。
・`application_controller`で`before_action :require_login`を記述しているので、ログインしていない場合は自動的に`not_authenticaed`というメソッドを実行。（Sorceryのヘルパー）
・ログインしていなければ、管理者用のログインページへリダイレクトさせる。

#管理者用ログイン画面のコントローラーを作成

```:ターミナル
$ rails g controller Admin::User_sessions new
```
```rb:app/controllers/admin/user_sesssion_controller.rb
class Admin::UserSessionsController < Admin::BaseController ←#BaseControllerを継承している
  skip_before_action :require_login, only: %i[new create]
  skip_before_action :check_admin, only: %i[new create]
  layout 'layouts/admin_login'

  def new; end

  def create
    @user = login(params[:email], params[:password])
    if @user
      redirect_to admin_root_path, success: t('.success')
    else
      flash.now[:danger] = t('.fail')
      render :new
    end
  end

  def destroy
    logout
    redirect_to admin_login_path, success: t('.success')
  end
end
```
・`layout 'layouts/admin_login'`ログインページ用のレイアウトを宣言
・`@user = login(params[:email], params[:password])`mailとpasswordが一致していれば@userにデータが代入される(loginメソッド(sorceryメソッド)で検証を行なっている)

###ルーティング

```rb:config/routes.rb
namespace :admin do
    root to: 'dashboards#index'
    get 'login', to: 'user_sessions#new'
    post 'login', to: 'user_sessions#create'
    delete 'logout', to: 'user_sessions#destroy'
  end
```

#ログイン用のレイアウトのテンプレート

```rb:app/views/layouts/admin_login.erb
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="robots" content="noindex, nofollow">
  <title><%= page_title(yield(:title), admin: true) %></title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <%= csrf_meta_tags %>
  <%= stylesheet_link_tag 'admin', media: 'all' %>
</head>
<body class="hold-transition login-page">
  <div>
    <%= render 'shared/flash_message' %>
    <%= yield %>
  </div>
</body>
</html>
```

```rb:app/views/admin/user_sessions/new.html.erb
<% content_for(:title, t('.title')) %>
<div class="login-box">
  <div class="login-logo">
    <h1><%= t('.title') %></h1>
  </div>
  <!-- /.login-logo -->
  <div class="card">
    <div class="card-body login-card-body">

      <%= form_with url: admin_login_path, local: true do |f| %>
        <%= f.label :email, User.human_attribute_name(:email) %>
        <div class="input-group mb-3">
          <%= f.text_field :email, class: 'form-control', placeholder: :email %>
          <div class="input-group-append">
            <div class="input-group-text">
              <span class="fas fa-envelope"></span>
            </div>
          </div>
        </div>

        <%= f.label :password, User.human_attribute_name(:password) %>
        <div class="input-group mb-3">
          <%= f.password_field :password, class: 'form-control', placeholder: :password %>
          <div class="input-group-append">
            <div class="input-group-text">
              <span class="fas fa-lock"></span>
            </div>
          </div>
        </div>

        <div class="row">
          <div class="col-12">
            <%= f.submit (t 'defaults.login'), class: 'btn btn-block btn-primary' %>
          </div>
        </div>
      <% end %>
    </div>
  </div>
</div>
```

#管理者ユーザーを作成

```rb:db/seeds.rb
User.create!(last_name:  "aa",
             first_name: "aa",
             email: "a@a.com",
             password:  "aaa",
             password_confirmation: "aaa",
             role: 1)
```

・`role: 1`roleカラムを1にしてadmin(管理者)に設定

```:ターミナル
$　rails db:seed
```
・`rails db:seed`コマンドでコマンドを実行するとDBに反映される。