AdminLTEで管理画面の実装
AdminLTEを使用し、以下のような管理画面トップページと管理画面へのログイン機能を実装する。
AdminLTEとは
Bootstrapをベースにした、管理画面等のCSSフレームワーク
今回はバージョン3を使用
AdminLTEのインストール
$ yarn add admin-lte@^3.0
node_modules
ディレクトリが作成され、ここにテンプレートが入っている。
マニフェストファイルの設定
マニフェストファイルとは、ロードするcssやjsファイルを記述しておく場所。
マニフェストファイルはapp/assets/javascripts
、app/assets/stylesheets
にある。
adminページ用のマニフェストファイルを作成
//= require jquery3 //= require jquery_ujs //= require admin-lte/plugins/bootstrap/js/bootstrap.bundle.min //= require admin-lte/dist/js/adminlte.min
@import "font-awesome-sprockets"; @import "font-awesome"; @import 'admin-lte/plugins/fontawesome-free/css/all.min.css'; @import 'admin-lte/dist/css/adminlte.min.css';
application.jsの設定
今回はadmin用マニフェストファイルを同じディレクトリに作成するので、任意のファイルだけ読み込む必要がある。
require = tree.
があるとapplication.js
配下のファイルをすべて読み込んでしまうので、削除して個別にファイルを指定する。
//= require jquery3 //= require popper //= require bootstrap-sprockets //= require rails-ujs //= require activestorage //= require cable.js
アセットのプリコンパイル設定
アセットとはスタイルシートやJavaScriptなどのリソース
application.js
以外のファイルを読み込む場合はプリコンパイルの設定をする必要がある。
以下のファイルのコメントアウトを外す。
Rails.application.config.assets.precompile += %w( admin.js admin.css )
これで、admin.jsとadmin.cssが認識されるようになる。(プリコンパイル)
usersテーブルに権限を判定するカラムを追加
ユーザーとadminを判別するためにrole
カラムを追加する。
データ型はinteger(整数)とし、ユーザー権限を0、admin権限を1とする。
$ rails g migration add_role_to_users
class AddRoleToUsers < ActiveRecord::Migration[5.2] def change add_column :users, :role, :integer, default: 0, null: false #デフォルトを0とし一般ユーザとする end end
マイグレートする。
== 20210702210922 AddRoleToUsers: migrating =================================== -- add_column(:users, :role, :integer, {:default=>0, :null=>false}) -> 0.0013s == 20210702210922 AddRoleToUsers: migrated (0.0016s) ==========================
enumの追加
enumは、先程定義したモデルのintegerカラムに対して文字列の名前をつけることができる。
- general・・・一般
- admin・・・管理者 と定義する。
enum role: { general: 0, admin: 1 }
current_user.admin?
などが使えるようになる。
ルーティング
ルーティングの指定にはnamespace
を使う。
/admin
から始まるURLにすることができる。
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
controller
管理画面用のコントローラの基盤を作成。
application_controllerを継承するadmin/base_controllerを作成する。
他の全ての管理画面用コントローラーはこのbase_controllerを継承する。
% rails g controller admin::base
::
でディレクトリの階層を指定できる。
class Admin::BaseController < ApplicationController before_action :check_admin layout 'admin/layouts/application' private def not_authenticated flash[:warning] = 'ログインしてください' redirect_to admin_login_path end def check_admin redirect_to root_path, warning: '権限がありません' unless current_user.admin? end end
check_adminメソッドで管理者以外はトップページへ遷移させる。
さらに、管理画面用のトップページに遷移するコントローラーadmin/dashboards_controller.rb
と管理画面ログイン用のadmin/user_sessions_controller
を作成する。
これらは全てAdmin::BaseController
を継承する。
$ rails g controller admin::user_session $ rails g controller admin::dashboards
class Admin::UserSessionController < Admin::BaseController skip_before_action :check_admin, only: %i[new create] skip_before_action :require_login, only: %i[new create] layout 'admin/layouts/admin_login' def new; end def create @user = login(params[:email], params[:password]) if @user redirect_to admin_root_path, success: 'ログインしました' else flash.now[:danger] = 'ログインに失敗しました' render :new end end def destroy logout redirect_to admin_login_path, success: 'ログアウトしました' end end
ログイン画面はskip_before_action :require_login
でログインしなくてもアクセスできるようにする。
ログイン画面で読み込むレイアウトファイルはadmin_login.html.erb
を指定。
class Admin::DashboardsController < Admin::BaseController def index; end end
Admin::BaseController
を継承しているのでレイアウトは定義しなくていい。
ログイン画面のview
adminログイン画面の作成
/admin/login
へ以下のようなログイン画面を作成。
node_modules/admin-lte/pages/example/login.html
のテンプレートを参考に作成する。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="robots" content="noindex, nofollow"> <title>AdminLTE 3 | Log in</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>
<% content_for(:title, 'ログイン') %> <div class="login-box"> <div class="login-logo"> <h1>ログイン</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, 'メールアドレス' %> <div class="input-group mb-3"> <%= f.text_field :email, class: 'form-control', placeholder: 'メールアドレス' %> <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>
ログインフォームはモデルと紐付かないので、form_with
のオプションはurl:
を使用
フォーム送信先は/admin/login
なのでadmin_login_path
とする。
何もないとajax通信になるのでlocal: true
をつける。
ユーザに管理者権限を付与する。
実際にログインしたりするには管理者権限がついたユーザを作る必要がある。
今回はID=1
のユーザに管理者権限を設定する。
pry(main)> user = User.find(1) User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]] => #<User:0x00007f905aa97c60 id: 1, email: "meo@gmail.com", crypted_password: "$2a$10$sdmyh3nhvkXVVIWTA5IVA.b4Pc6Syqz5/UCuBFCOMUg/fPVSMAxAa", salt: "56AQgaWqsDrNWaDTq7Kw", created_at: Sun, 30 May 2021 16:45:18 JST +09:00, updated_at: Wed, 23 Jun 2021 16:22:28 JST +09:00, last_name: "meoq", first_name: "meoq", avatar: "883957.png", reset_password_token: "Fzo_q2jdTB3qbxQqsxJm", reset_password_token_expires_at: nil, reset_password_email_sent_at: Sun, 27 Jun 2021 19:59:50 JST +09:00, access_count_to_reset_password_page: 0, role: "general"> pry(main)> user.admin! (0.1ms) begin transaction User Exists (0.3ms) SELECT 1 AS one FROM "users" WHERE "users"."email" = ? AND "users"."id" != ? LIMIT ? [["email", "meo@gmail.com"], ["id", 1], ["LIMIT", 1]] User Exists (1.3ms) SELECT 1 AS one FROM "users" WHERE "users"."reset_password_token" = ? AND "users"."id" != ? LIMIT ? [["reset_password_token", "Fzo_q2jdTB3qbxQqsxJm"], ["id", 1], ["LIMIT", 1]] User Update (0.5ms) UPDATE "users" SET "updated_at" = ?, "role" = ? WHERE "users"."id" = ? [["updated_at", "2021-07-03 17:48:59.494861"], ["role", 1], ["id", 1]] (1.3ms) commit transaction => true pry(main)> user.save (1.7ms) begin transaction User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE "users"."email" = ? AND "users"."id" != ? LIMIT ? [["email", "meo@gmail.com"], ["id", 1], ["LIMIT", 1]] User Exists (0.1ms) SELECT 1 AS one FROM "users" WHERE "users"."reset_password_token" = ? AND "users"."id" != ? LIMIT ? [["reset_password_token", "Fzo_q2jdTB3qbxQqsxJm"], ["id", 1], ["LIMIT", 1]] (0.1ms) commit transaction => true
管理者画面のview
以下のような管理者画面共通のviewの作成。
管理者用テンプレートの作成
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta lang='ja'> <meta name="robots" content="noindex, nofollow"> <title>ダッシュボード | (管理画面)</title> <%= csrf_meta_tags %> <%= csp_meta_tag %> <%= 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' %> <!-- Content Wrapper. Contains page content --> <div class="content-wrapper"> <%= render 'shared/flash_message' %> <%= yield %> </div> <!-- /.content-wrapper --> <%= render 'admin/shared/footer' %> </div> <%= javascript_include_tag 'admin' %> </body> </html>
views/admin/shared
のheader/sidebar/footerのパーシャルを読み込む。
<%= stylesheet_link_tag 'admin', media: 'all' %>
<%= javascript_include_tag 'admin' %>
この記述でブラウザにアセットを読み込んでいる。
ダッシュボードのview
<div class="content-wrapper"> <div class="row"> <p>ダッシュボードです</p> </div> </div>