はじめに
管理画面へのログイン機能を実装しましょう。管理画面のトップページも作成しましょう。
作業の流れ
- AdminLTEバージョン3をyarnでインストール
- 管理者画面用のマニフェストファイルを作成
- マニフェストファイルの読み込み設定
- usersテーブルに管理者権限を判定する為のroleカラムを追加
- 管理者用のコントローラを作成(
admin/base_controller
) - 管理画面用のトップページに遷移するコントローラーを作成(
admin/dashboards_controller.rb
) - 管理者ログイン機能を担うコントローラを作成(
admin/user_sessions_controller
) - ルーティングの設定
- ビューファイルの作成
- 管理画面用のページタイトルの設定
- 管理者ログイン機能を作成
- ローケルファイル追記
AdminLTEバージョン3をyarnでインストール
今回はでパッケージ管理ツールであるyarn
を使いましょう。
こちらを参考にします。
$ yarn add admin-lte@^3.1
node_modules
、package.json
、yarn.lock
というファイルが生成されます。
node_modules/admin-lte
ディレクトリ内に、多数テンプレートファイルが用意されていますので、その中のstarter.html
を使います。
管理者画面用のマニフェストファイルを作成
今まで一般ユーザのマニフェストファイルはapp/assets/stylesheets/application.scss
とapp/assets/javascripts/application.js
でしたが、管理者画面は一般ユーザの画面と構成や見た目が大きく異なる事から、別々に管理するのが一般的です。
admin.jsの設定
//= require jquery3
//= require jquery_ujs
//= require admin-lte/plugins/bootstrap/js/bootstrap.bundle.min # 拡張子は省略可能
//= require admin-lte/dist/js/adminlte.min
//= require common.js
//= require rails_ujs
だと現在はエラ-が起こるようで、//= require jquery_ujs
を記述する必要があります。
参考:https://qiita.com/Statham/items/372234e23749ff1f6bf8
//= require admin-lte/plugins/jquery/jquery.min
は必要ない
こちらはjQuery本体を読み込む記述なので、Gemfileに記述してインストールしているgemのjquery-rails
と重複しているので、不要です。
//= require jquery3
は、jquery-rails
を利用するための記述です。
- 「.min」はminifyを表す
.min
はパソコンにとって可読性が良いコードの形式に変換したものです。minifyすることで読み込み速度が向上します。
参考:https://webukatu.com/wordpress/blog/37071/
admin.scssの設定
@import 'admin-lte/plugins/fontawesome-free/css/all.min.css';
@import 'admin-lte/dist/css/adminlte.min.css';
はじめ私は @import 'font-awesome-sprockets';
と @import 'font-awesome';
も記入していましたが無くても動作しました。おそらくAdminLTEバージョン3からインポートしている上の記述だけでfontawesome
は使えるようになっているのだと思います。
application.jsの設定を変更
今までは//= require_tree .
としていました。こちらを設定していると、同じ階層以下すべてのJSファイルを読み込む設定になります。また、読み込む順番も指定することができません。
今回、app/assets/javascripts/application.js
と同じ階層に、app/assets/javascripts/admin.js
を作っていますので、app/assets/javascripts/application.js
で//= require_tree .
を記述していますと、app/assets/javascripts/application.js
でapp/assets/javascripts/admin.js
を読み込んでしまいます。
不要なファイルを読み込むのを避けるために、//= require_tree .
は使わず、必要なファイルを適切な順番で個別に記述しましょう。
//= require jquery3
//= require popper
//= require bootstrap-sprockets
//= require rails-ujs
//= require activestorage
//= require common.js
//= require cable.js
マニフェストファイルの読み込み設定
次にプリコンパイルの設定をします。application以外のマニフェストファイルを個別に読み込む場合は、プリコンパイルの設定をしないと、そのファイルは対象外とされてしまうためエラーが起きてしまいます。
# Be sure to restart your server when you modify this file.
# Version of your assets, change this if you want to expire all your assets.
Rails.application.config.assets.version = '1.0'
# Add additional assets to the asset load path.
# Rails.application.config.assets.paths << Emoji.images_path
# Add Yarn node_modules folder to the asset load path.
Rails.application.config.assets.paths << Rails.root.join('node_modules')
# Precompile additional assets.
# application.js, application.css, and all non-JS/CSS in the app/assets
# folder are already added.
Rails.application.config.assets.precompile += %w[admin.js admin.css]
Rails.application.config.assets.precompile += %w( admin.js admin.css )
のコメントアウトを外してプリコンパイルの設定をします。
usersテーブルに管理者権限を判定する為のroleカラムを追加
特定のuserだけが管理者ページにアクセスできるように権限を付与させたいので、role
カラムを追加します。マイグレーションファイルを作成しましょう。
$ rails g migration add_role_to_users
作成されたマイグレーションファイルを見ます。
class AddRoleToUsers < ActiveRecord::Migration[5.2]
def change
add_column :users, :role, :integer, default: 0, null: false
end
end
一般ユーザと管理者を区別し、整数のinteger
型でrole
というカラムを追加しています。
一般ユーザが0
、管理者は1
とします。デフォルトは一般ユーザである0
とします。
一般ユーザ | 0 |
管理者 | 1 |
enumを設定
enumとは、モデルの数値カラムに対して文字列による名前を付けれるようになる仕組みです。
今回は一般ユーザをgeneral
、管理者をadmin
として定義していきます。
一般ユーザ | general |
管理者 | admin |
user.rbにenumの設定を追記します
class User < ApplicationRecord
enum role: { general: 0, admin: 1 }
end
管理者用のコントローラを作成
管理者用のコントローラを作成するにあたって、「管理系」のカテゴリとして区分したいため、Admin::BaseController
という名前を付けることにします。この名前は、Admin
というモジュールの名前空間の中にBaseController
というクラスを定義するという意味になります。
Railsではモジュール階層を、コードを保存するためのディレクトリ階層に対応させているので、admin/base_controller.rb
というファイルが対応する事になります。
のちのち、管理系の機能を増やす時に、Admin::
のついたコントローラーを追加していけば、コードがadmin
ディレクトリ配下にまとまるので視認性が良くなります。
では、コントローラを作成するコマンドを入力しましょう。
$ rails g controller Admin::Base
管理画面用のトップページに遷移するコントローラーを作成
admin/dashboards_controller.rb
となるように、管理者用のトップページ用のコントローラを作成します。
$ rails g controller Admin::Dashboards index
管理者のログイン機能を担うコントローラを作成
管理者ログイン機能に関するコントローラも作成します。
$ rails g controller Admin::User_sessions new
ルーティングの設定
Adminというモジュールの名前空間を使用したコントローラを作成しましたので、それに対応するようにルーティングにも名前空間を設定しましょう。
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
ビューファイルの設定
管理者用画面は一般用の画面とは見た目も異なるので、専用のテンプレートファイルを用意します。
views/admin/layout/
配下に作成しましょう。
管理者用画面のレイアウトファイルの作成
starter.html
から流用してレイアウトファイルを作成していきましょう。
ヘッダー、メニュー、フッター部分は別の部分テンプレートとして切り分けてviews/admin/shared
に配置しましょう。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta lang='ja'>
<meta name="robots" content="noindex, nofollow"> # SEOに関する記述
<title><%= page_title(yield(:title), admin: true) %></title> # 後半で使う設定
<%= csrf_meta_tags %> # クロスサイトフォージェリ対策
<%= stylesheet_link_tag 'admin', media: 'all' %> # ここでadmin.scssを読み込む
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,400i,700&display=fallback">
</head>
<body class="hold-transition sidebar-mini">
<div class="wrapper">
<%= render 'admin/shared/header' %> # headerをレンダーする
<%= render 'admin/shared/sidebar' %> # sidebarをレンダーする
<!-- Content Wrapper. Contains page content -->
<div class="content-wrapper">
<%= render 'shared/flash_message' %> # フラッシュメッセージを呼び込む
<%= yield %> # bodyを読み込む
</div>
<!-- /.content-wrapper -->
<%= render 'admin/shared/footer' %> # footerをレンダーする
</div>
<%= javascript_include_tag 'admin' %> # ここでadmin.jsを読み込む
</body>
</html>
管理者画面用のマニフェストファイル assets/stylesheets/admin.scss
とassets/javascripts/admin.js
を作成したことを思い出してください。
管理者用のレイアウトファイルでそれらを読み込む記述をしていることに注意しましょう。
ヘッダー、メニュー、フッターは部分テンプレートとしてadmin/shared 配下に作成
ヘッダー
<!-- Navbar -->
<nav class="main-header navbar navbar-expand navbar-white navbar-light">
<!-- Left navbar links -->
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" data-widget="pushmenu" href="#"><i class="fas fa-bars"></i></a>
</li>
</ul>
<!-- Right navbar links -->
<ul class="navbar-nav ml-auto">
<!-- Navbar Search -->
<li class="nav-item">
<%= link_to t('defaults.logout'), admin_logout_path, method: :delete, class: 'nav-link' %>
</li>
</ul>
</nav>
<!-- /.navbar -->
サイドバー
<!-- Main Sidebar Container -->
<aside class="main-sidebar sidebar-dark-primary elevation-4">
<!-- Brand Logo -->
<a href="index3.html" class="brand-link">
<%= image_tag 'AdminLTELogo.png', class: 'brand-image img-circle elevation-3' %>
<span class="brand-text font-weight-light">AdminLTE 3</span>
</a>
<!-- Sidebar -->
<div class="sidebar">
<!-- Sidebar user panel (optional) -->
<div class="user-panel mt-3 pb-3 mb-3 d-flex">
<div class="image">
<%= image_tag current_user.avatar_url, class: 'img-circle elevation-2' %>
</div>
<div class="info">
<a href="#" class="d-block"><%= current_user.decorate.full_name %></a>
</div>
</div>
<!-- Sidebar Menu -->
<nav class="mt-2">
<ul class="nav nav-pills nav-sidebar flex-column" data-widget="treeview" role="menu" data-accordion="false">
<li class="nav-item">
<%= link_to '#', class: "nav-link" do %>
<i class="nav-icon far fa-file"></i>
<p>
掲示板一覧
</p>
<% end %>
<li class="nav-item">
<%= link_to '#', class: "nav-link" do %>
<i class="nav-icon far fa-user"></i>
<p>
ユーザ一一覧
</p>
<% end %>
</li>
</ul>
</nav>
<!-- /.sidebar-menu -->
</div>
<!-- /.sidebar -->
</aside>
フッター
<!-- Main Footer -->
<footer class="main-footer">
<strong>Copyright © 2019 RUNTEQ.</strong>
All rights reserved.
</footer>
管理者画面用トップページのビューファイル
<%= content_for(:title, t('.title')) %>
<div class="content-wrapper">
<div class="row">
<p>ダッシュボードです</p>
</div>
</div>
Admin::Baseコントローラ にlayout宣言を追記する
通常、何も指定が無ければコントローラはapp/views/layouts/application.html.erb
をレイアウトとして探索するでしょう。
管理者機能の根幹クラスであるAdmin::BaseController
で個別の管理者用画面のレイアウトファイルを呼び込むようにlayout宣言
をしてあげましょう。
class Admin::BaseController < ApplicationController
layout 'admin/layouts/application' # layout宣言
end
Admin::Dashboardsコントローラ を修正
管理者画面トップページへ遷移させるためのコントローラは、管理者機能の根幹となるAdmin::BaseControlller
を継承する設計へと変更させます。
そのためlayout宣言の記述は不要です。
class Admin::DashboardsController < Admin::BaseControlller
def index; end
end
管理画面用のページタイトルの設定
以前にカスタムヘルパーを使ってタイトルを動的に出力する方法を実装しました。
module ApplicationHelper
def page_title(page_title = '')
base_title = 'SAMPLE BOARD APP'
page_title.empty? ? base_title : page_title + ' | ' + base_title
end
end
今回は管理者用画面のタイトルを、
「ダッシュボード | 固定タイトル(管理画面)」のように、語尾に(管理画面)
の文字が表示されるような機能も追加しましょう。
module ApplicationHelper
def page_title(page_title = '', admin = false)
base_title = if admin
'SAMPLE BOARD APP(管理画面)'
else
'SAMPLE BOARD APP'
end
page_title.empty? ? base_title : page_title + ' | ' + base_title
end
end
管理者か否かの判定ロジックを組み込みましょう。
デフォルトを admin = false
とすることによって、既存の一般ファイルに影響が出ないようにしています。
管理者用レイアウトファイルでタイトル設定
今一度、管理者用のレイアウトファイル(views/admin/layouts/application.html.erb
)を見てみてください。
<title><%= page_title(yield(:title), admin: true) %></title>
管理者用レイアウトファイルでadmin: true
を設定しておくことで、すべての管理者画面はadmin: true
が付与される仕組みとなっています。
各管理者用ビューファイル
それぞれのビューファイルからcontent_for
ヘルパーを使ってタイトルを設定します。
<% content_for(:title, t('.title')) %> # titleはローケルファイルから読み込み
管理者ログイン機能を作成
いよいよメインとなる管理者ログイン機能を実装していきます。
- ログイン画面で読み込むレイアウトファイルは
admin_login.html.erb
とし、views/layouts
配下に作成 - 管理者ログインページから一般ユーザーでログインした場合は
root_path
にリダイレクトされるよう設定 - 管理者がログイン後は管理画面のトップページにリダイレクトされるように設定
- 管理者がログインに失敗したら、管理者ログインページへリダイレクトされる
- ログアウトしたらログインページへ
Admin::Baseコントローラの設定
管理者機能の根幹となるAdmin::BaseController
に全体として使いたい機能を追記していきましょう。
class Admin::BaseController < ApplicationController
bofore_action :check_admin
layout 'admin/layouts/application'
private
def not_authenticated
redirect_to admin_login_path, warning: t('defaults.message.require_login')
end
def check_admin
redirect_to root_path, warning: t('defaults.message.not_authorized') unless current_user.admin?
end
end
コードを紐解く
既にSorceryを導入していますので、 ApplicationController
の before_action :require_login
によってログインしていない場合は自動的にnot_authenticated
というメソッドが実行されます。
def require_login
return if logged_in?
if Config.save_return_to_url && request.get? && !request.xhr? && !request.format.json?
session[:return_to_url] = request.url
end
send(Config.not_authenticated_action)
end
def not_authenticated
redirect_to root_path
end
今回、このコントローラ内の処理ではログインしていないユーザを管理者用のログインページへリダイレクトさせたいので、not_authenticated
メソッドの中身をredirect_to_ admin_login_path
に書き換えています。
check_admin
メソッドについてですが、unless current_user.admin?
の部分で現在ログインしているユーザが管理者かどうかを判定しています。
admin?
メソッドはというと、enumを設定した際にrole
カラムの1
をadmin
としたので使えるようになっているメソッドです。
before_action :check_admin
とすることで、各アクションより先に実行されます。
Admin::UserSessionsコントローラの設定
作成しておいたAdmin::UserSessions
コントローラの中身を記述していきましょう。
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 'admin/layouts/admin_login' # ログインページ用のレイアウトを用意するので宣言
def new; end
def create
@user = login(params[:email], params[:password]) # Sorceryメソッド emailとpasswordでログイン認証する
if @user
redirect_to admin_root_path , success: 'ログインしました'
else
flash.now[:danger] = 'ログインに失敗しました'
render :new
end
end
def destroy
logout # ログアウトするためのSorceryメソッド
redirect_to admin_login_path, success: 'ログアウトしました'
end
end
Admin::Base
コントローラではadmin/layouts/application
をレイアウトとして使用するようlayout宣言していました。
Admin::UserSessions
コントローラもAdmin::Base
コントローラを継承してますので、そのままではadmin/layouts/application
をレイアウトとして使用するでしょう。
しかしログイン画面は、またもや異なる見た目にするので、別のログインページ用のレイアウトファイルを用意してlayout宣言しています。
また、ログインしていないユーザや、管理者権限のないユーザでも、ログイン画面が表示されるように、skip_before_action
でフィルタをスキップさせてあげましょう。
ログインした後は、admin_root_path
へリダイレクトされ、admin/dashboards#index
アクションが呼び出されます。
このとき、管理者ではない一般ユーザでログインした場合は、before_action :check_admin
フィルタによって、一般ユーザ用のトップページへリダイレクトされます。
管理者ログイン画面のビューファイルを作成
adminログインフォームのテンプレートはnode_modules/admin-lte/pages/example/login.html
を参考にします。
views/admin/layouts/
配下にadmin_login.html.erb
という名前で作成しましょう。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title><%= page_title(yield(:title), admin: true) %></title>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag 'admin', media: 'all' %> # admin.scssを読み込む
</head>
<body class="hold-transition login-page">
<div>
<%= render 'shared/flash_message' %>
<%= yield %>
</div>
</body>
</html>
admin.js
は読み込んでいない点に注意しましょう。
<%= 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, locale: true do |f| %>
<%= f.label :email, User.human_attribute_name(:email) %>
<div class="input-group mb-3">
<%= f.email_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>
コードを紐解く
<%= form_with url: admin_login_path, locale: true do %>
ログイン機能のform_with
はモデルと紐付かない為、送信先はadmin_login_path
です。
ロケールファイル追記
admin/dashboards_controller.rb
のような階層のあるコントローラのローケルファイルはこのように記述しましょう。
admin:
user_sessions:
new:
title: 'ログイン'
create:
success: 'ログインしました'
fall: 'ログインに失敗しました'
destroy:
success: 'ログアウトしました'
dashboards:
index:
title: 'ダッシュボード'
終わりに
非常に実装が多く大変でした。お疲れさまです。
参考記事
https://railsguides.jp/layouts_and_rendering.html#コントローラ用のレイアウトを指定する
コメント