基礎編22

2021.4.6

[管理画面] 管理画面へのログイン機能、管理画面トップページの作成

<今回出たエラー>

❶ BootstrapよりjQueryを先に記述してくださいという意味

Uncaught TypeError: Bootstrap's JavaScript requires jQuery. jQuery must be included before Bootstrap's JavaScript.
と
Uncaught TypeError: Cannot read property 'fn' of undefined

原因 

・application.scssで@import 'adminを記述していたので削除

adimは管理者用なので。

application.jsに//= require jquery3がなかった。

 

❷コンフリクトが起きた

*masterブランチで解消せずに作業ブランチで解消commit/pushを行う

参照ページ

gitでリモートのブランチにローカルを強制一致させたい時 - Qiita

Git コンフリクト解消手順 - Qiita

git checkout master #masterブランチに切り替え
git pull origin master  #リモートの状態を反映させ、最新にする。
git checkout 〇〇 #作業ブランチに移動する。
git merge master #masterブランチをマージする

原因

リモートのmasterブランチとローカルのmasterブランチの状態に差があった。

git co master
git fetch origin
git reset --hard origin/master

これで差をなくし、

git co 22_admin_login
git merge master

で移動し、マージしここで起きたコンフリクトを解消する。

 

git reset --hard HEAD^: 前回のcommitの直後まで戻す。addとかも全て戻ってる。

 

<今回調べたこと>

パッケージマネージャー

大量のプログラムをひとまとめにしたものをパッケージと呼び、そのパッケージを管理するツールのこと

これを使用することで、インストールなどが高速に正確に行うことができる。

 ex)rubyのライブラリ管理を行うGemMacOSに導入するライブラリ管理ツールであるHomebrewなどなど

フロントエンド関連の現在主流のパッケージマネージャー

Bower→npm→yarn という感じの流れできている。

今回はyarnを使用する。

 

enum(イーナム)

列挙型とも呼ばれる。一連の整数値に対して複数の変数名をつけることができる仕組み。

この3つを読めばなんとなくわかる。

【Ruby on Rails】enumとは?enumを使って可読性をあげてみよう - SakuraWi - BLog

【Rails】Enumってどんな子?使えるの? - Qiita

Active Record クエリインターフェイス - Railsガイド

 

ルーティングのnamespace

コントローラを名前空間によってグループ分けをすることができる。

Rails のルーティング - Railsガイド

 

継承

他のクラスをベースとして新しいクラスを作成することをクラスの継承と呼ぶ。継承先のクラスを子クラスサブクラスと呼び、継承元のクラスを親クラスと呼ぶ。

複数のクラスに共通する項目を別クラスに切り出し、そのクラスを継承することによって再利用が簡単にできたり、記述箇所を一箇所にまとめることが可能。

親クラスの記述を変更したいときは、こクラスで同じメソッド名を使用してあげることで、親クラスの内容をオーバーライドすることができる。

 

コールバック

特定のタイミングで実行されるアクションのこと。

Active Record コールバック - Railsガイド

 

AdminLTE

AdminLTEはBootstrapベースで作られた管理画面作成などに特化したCSSフレームワーク(枠組み、土台)のこと。

骨組みはBootstrapで、それにスタイリングを加え、サンプルも提供してくれる。

俺たちは雰囲気でAdminLTEを使っている - Qiita

管理画面を作る:AdminLTE 基本編 - Qiita

 

マニフェストファイル

RailsでcssファイルとJavascriptファイルをマニフェストファイルから読み込む | Boys Be Engineer 非エンジニアよ、エンジニアになれ

 

アセットパイプライン

一言で言うと...

複数のディレクトリやファイルに分かれたassetsディレクトリ内のファイルをひとつに連結・圧縮する機能

参照

こっちで仕組みを理解して、

DIVE INTO CODE | 「アセットパイプライン」を学ぼう

細かい補足はこっち

Rails初学者がつまずきやすい「アセットパイプライン」

Webブラウザ上に画面が表示されるのは、”WebブラウザがHTMLとCSSJavaScriptを認識する” 必要がある。

Ruby on Rails では、この仕組みがアセットパイプラインによって実現されるようになっている

Webブラウザには、ひとつのWebページを表示するために複数のファイルを結合する機能は備わっていない。そのため、開発者が作業をしやすいようにファイルをたくさんのディレクトリに分けてしまうと、それらのディレクトリをそのままひとつのWebページとして扱うことができなくなってしまいまう為アセットパイプラインがある。

 

キャッシュとは

Railsアプリケーションを高速化するもの

リクエスト・レスポンスなどのサイクルの中で作成されたコンテンツを保存しておき、次回同じようなリクエストが発生したときのレスポンスでそのコンテンツを再利用すること。

 

参照

CDNってそもそも何?なんかサーバの負荷が下がるって聞いたんだけど!〜Web制作/運営の幅が広がるCDNを知ろう第1回〜 | さくらのナレッジ

すごくわかりやすいのがこの部分

キャッシュとは複雑なデータベースリクエストなどで作成された完成品を一時的に置いておくもの。→カレー屋さんでカツカレーのカツをあらかじめあげておくようなもの。とんかつを揚げるのには7分程度かかるが作り置きしておけばカレーをかけるだけで出来上がるので高速でカツカレーを提供できる。

 

 

<流れ>

参照

AdminLTE 3を使って管理者ページを実装しよう - Ruby on Railsの備忘録

・AdminLTE3をインストールする

・管理者ページようにマニフェストファイルを作成、記述し読み込みの設定を行う

・Userモデルに管理者かどうかを判別させるroleカラムを追加する

・↑enumを追加する

・管理者用のコントローラーの作成

・管理者用のルーティングの設定

・管理者用のページレイアウトファイルの作成

・管理者用のビューの作成

 

admin

--adminをインストールする

yarn add admin-lte@^3.0

node_modulespackage.jsonyarn.lockというファイルがインストールされる。

node_modules/admin-lteディレクトリにデフォルトテンプレートが記載されているその中から

今回は、starter.html を使用していく。

 

--検証を使用して、取り込むファイル確認する

一般のページと管理者用のページの見た目が大きく変わるため、別々で管理する。

f:id:mmm_st:20210411151828p:plain

一般→(app/assets/javascripts/application.js)(app/assert/stylesheets/application.scss)

管理者→(app/assets/javascripts/admin.js)

(app/assert/stylesheets/admin.scss)

 

このリンクから管理画面のサイトイメージが見れる

https://adminlte.io/themes/v3/starter.html

検証を開いて使用されているファイルを自分のほうでも読み込む。

JavaScriptについては

JavaScriptタグ内の--REQUIRED SCRIPTS--を確認。

(app/assets/javascripts/admin.js)

//= require jquery3 #jQueryを導入
//= require rails-ujs #rails-ujs導入
//= require admin-lte/plugins/bootstrap/js/bootstrap.bundle.min
//= require admin-lte/dist/js/adminlte

 

CSSについては

headタグ内をみる。GoogleAPIFontはyarnで取得したパッケージではないので無視。

(app/assert/stylesheets/admin.scss)

 
 
@import 'admin-lte/plugins/fontawesome-free/css/all.min.css';
@import 'admin-lte/dist/css/adminlte.css';

 

--application.jsの設定を変更

以前はappliction.jsファイル内で//= require tree .javascriptディレクトリ階層全ファイルを読み込んでいた。

だが、せっかく管理者用のファイルを別に作成したのにadmin.jsとaplication.jsが同じ階層にある為、このままでは全てのファイルを読み込んでしまう新しく作成した管理者用ファイル以外のファイルを個別で読み込む。

(app/assert/javascripts/application.js)

//= require jquery3
//= require popper #bootstrapのため
//= require bootstrap-sprockets#bootstrapのため
//= require rails-ujs
//= require activestorage #ファイルアップロードの昨日のため
 

 

マニファストファイルの読み込み設定

プリコンパイルの設定をする。

application.jsまたは.css 以外のファイルを個別に呼び出したい(<%= stylesheet_link_tag 'application'... %>のような感じで<head>タグで呼び出す。)場合はプリコンパイルの設定を行わないと対象外とされ、エラーが起こることがある。

(config/initializars/assets.rb)

# 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 << Ra
ils.root.join('node_modules')
#ここでアセットパイプラインのpathの設定が行われている
# 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 ad
min.css]
コンパイルの設定。コメントアウトを外した。
 

 

Userモデルに管理者かどうかを判別させるroleカラムを追加する enumを使用

一般権限はdefaultは0 管理者権限は1とする。

$rails g migration add_role_to_users

 
class AddRoleToUsers < ActiveRecord::Migration[5.2]
def change
add_column :users, :role, :integer, null: false, default: 0
end
end

$ rails db:migrate

enumの追加

モデルの数値カラムに対して文字列の名前を定義することができる。

一般をgenetal

管理者をadminとして定義する

(user.rb)

enum role:
{
general: 0,
admin: 1
}
 

 

管理者用Controllerを作成する

アプリケーション全体:  ApplicationController

一般ユーザー関係:  ApplicationControllerを継承したController

管理ユーザー関係に共通する基盤:  Admin::BaseController

他の管理ユーザー関係: Admin::BaseControllerを継承したController

 

--管理者用のコントローラーの基礎となるadmin/base_controller.rbを作成

$ rails g controller admin::base

 

--admin/base_controllerにAdmin::BaseControllerを継承したControllerの基盤になる処理を追加する

(admin/base_controller)

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.not_authorized') unless
current_user.admin? #user.rbで記述したものを使用
admin権限でない場合はトップページへ遷移
end
end

 

--管理画面のトップページへ遷移するコントローラー

$ rails g controller admin::dashboards

(admin/base_controller)を継承しているので、レイアウトの宣言は不要

(admin/dashboards_controller)

class Admin::DashboardsController < ApplicationController
def index; end
end

 

--管理画面へのログインの為のコントローラー

$ rails g controller admin::user_sessions

一般ユーザーと同様に管理画面へのログインフォームを設定する。app/controllers/user_sessions_controllerと同じような形で記述。

(admin/user_sessions_controller)

class Admin::UserSessionController < ApplicationController
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_back_or_to boards_path, success: t('.success')
else
flash.now[:danger] = t('.fail')
render :new
end
end

def destroy
logout
redirect_to root_path, success: t('.success')
end
end

 

ルーティングの設定

参照

Rails のルーティング - Railsガイド

--namespaceを使用して、/adminから始まるURLにする。

namespace :admin do
root to: 'dashboards#index'
get 'login', to: 'user_sessions#new'
get 'login', to: 'user_sessions#create'
delete 'logout', to: 'user_sessions#destroy'
end

 

ビューの設定

 

スタイルシートを指定する

stylesheet_link_tag 'スタイルシートへのパス' ,HTML属性 or イベント属性

media: 関連ファイルの出力メディアのリンクタイプ

application.html.erbと、adminディレクトリ以下にない(view/layouts/admin_login.html.erb)で呼び出し。

参照

stylesheet_link_tag | Railsドキュメント

 

javaScriptをインクルードする

javascript_include_tag 'javaScriptファイルへのパス' (,オプション)

application.html.erbのみで一度だけ呼び出し

参照

javascript_include_tag | Railsドキュメント

 

--レイアウトファイルの作成

controllerのlayout: で宣言したファイルをviews/admin/layoutsディレクトリとviews/layoutsディレクトリの作成、配置する。

 

パーシャルも使用する

(view/admin/layouts/application.html.erb )

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta lang='ja'>
<meta name="robots" content="noindex, nofollow">
<title><%= page_title(yield(:title), admin: true) %></title>
#helperメソッドで定義しているので、(管理画面)がついた表示がされる。
<%= csrf_meta_tags %>
<%= stylesheet_link_tag 'admin', media: 'all' %>
#admin.scssを読み込む
</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' %>
#admin.jsを読み込む
</body>
</html>
 

(view/admin/layout/shared/_header.html.erb )

<!-- 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>

(view/admin/shared/_sidebar.html.erb )

<!-- 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>
<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>

(view/admin/shared/_footer.html.erb )

 
<footer class="main-footer">
<strong>Copyright &copy; 2019 RUNTEQ.</strong>
All rights reserved.
</footer>

(view/layouts/admin_login.html.erb)*admin/下にないこと注意

<!DOCTYPE html>
<html lang="en">
<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' %>
#admin.scss読み込み
</head>
<body class="hold-transition login-page">
<div>
<%= render 'shared/flash_message' %>
<%= yield %>
</div>
</body>
</html>

(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>

(admin/dashboards/index.html.erb)

<% content_for(:title, t('.title')) %>
<div class="container">
<div class="row">
ダッシュボードです
</div>
</div>

 

タイトルの設定

--ヘルパーメソッドを作成

module ApplicationHelper
def page_title(page_title = '', admin = false)
#defaultをadmin = falseにしておく
#user.rbに定義したadminをここでも使用
base_title = if admin
'RUNTEQ BOARD APP(管理画面)'
else
'RUNTEQ BOARD APP'
end

page_title.empty? ? base_title : page_title + ' | ' + base_title
end
end