管理画面機能の作成 #Laravel頻出パターン #Laravelの教科書
# はじめに
Webアプリケーションのよくあるパターンとして、一般ユーザーのログイン機能と、運営側の管理画面があるものがあります。
別アプリケーションとして作ることもよくありますが、今回は以前の記事で作成した標準のログイン機能と自作のログイン機能を組み合わせて管理側の機能を作ってみます。
Laravelの教科書では、他のサンプルアプリケーションでも本稿で解説する管理側機能を利用します。基礎的な内容で理解出来るように作っているので是非挑戦してみてください。
# 設計
今回作成するサンプルアプリケーション(admin_sample)の大まかな設計です。
■ユーザー側機能
標準のログイン機能を使います。
■管理側機能
/adminから始まるURLはログイン画面を覗いて、管理側ログインが必要とします。また、管理側はログイン可能なユーザー一覧と、ユーザー詳細のページの機能を作ります。
/admin/login ログイン
/admin/logout ログアウト
/admin/home 管理側TOP
/admin/user_list
/admin/user/xxx 管理側、ユーザー情報確認ページ
# 準備
それではサンプルプロジェクトの準備を行います。
サンプルプロジェクトの準備は次の流れで行います。他の記事で解説している内容については詳細な内容を省略します。
・プロジェクトの作成
・データベースのセットアップ
・laravel/uiのセットアップ
プロジェクトの作成
「composer create-project」を実行し、プロジェクトの作成を行います。
composer create-project --prefer-dist laravel/laravel admin_sample
cd admin_sample
データベースのセットアップ
.envを修正し、データーベースのセットアップを行います。データーベースの情報を追加したら、マイグレーションを実行します。
php artisan migrate
laravel/uiのセットアップ
次に以下のコマンドを実行し、laravel/uiのインストールとセットアップを行います。
composer require laravel/ui
php artisan ui vue --auth
ユーザー側のログイン機能などはここで作成が終わりです。コマンドを実行するだけでセットアップ出来るので非常に便利ですね。
一度サーバーを動かして動作確認してユーザー登録、ログインを試してみてください。
php artisan serve
以前の記事で実行した、npm関連ですが、app.cssとapp.jsを使わない形で組み込むため省略します。
# 管理側機能の作成
次に管理側のコントローラーの作成を行います。
管理側の機能はわかりやすいようにadminディレクトリ以下に作成します。
make:controllerの引数で「ディレクトリ名/クラス名」と指定することで作成するディレクトリを指定することが出来ます。
■ログイン
php artisan make:controller admin/AdminLoginController
app/Http/Controllers/admin/AdminLoginController.php
<?php
namespace App\Http\Controllers\admin;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class AdminLoginController extends Controller
{
function showLoginForm(){
return view("admin.admin_login");
}
function login(Request $request){
//入力内容をチェックする
$user_id = $request->input("user_id");
$password = $request->input("password");
//ログイン成功
if($user_id == "hogehoge" && $password == "fugafuga"){
$request->session()->put("admin_auth", true);
return redirect("admin");
}
//ログイン失敗
return redirect("admin/login")->withErrors([
"login" => "ユーザーIDまたはパスワードが違います"
]);
}
}
AdminLoginControllerはユーザーログイン機能の作成で説明したSimpleLoginControllerをベースに改造しています。
今回はセッションに書き込むキーを"admin_auth"としています。
「session->put("admin_auth", true)」
ではなく
「$request->session()->put("admin_auth", true)」
を利用しています。
これは標準の認証を使った時にはsession()関数が上手く動作しないことが理由です。
AdminLoginControllerにはログインフォームを表示するshowLoginForm()関数を用意しています。
■ログアウト
php artisan make:controller admin/AdminLogoutController
app/Http/Controllers/admin/AdminLogoutController.php
<?php
namespace App\Http\Controllers\admin;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class AdminLogoutController extends Controller
{
function logout(Request $request){
$request->session()->forget("admin_auth");
return redirect("admin");
}
}
AdminLogoutControllerはログイン同様、ユーザーログイン機能の作成で説明したSimpleLogoutControllerをベースに改造しています。セッションに書き込む値が"admin_auth"に変更されているので、forgetするキーをadmin_authに変更しています。
こちらも、ログイン機能と同様に$request->session()経由にしてください。
■管理側TOP
php artisan make:controller admin/AdminTopController
app/Http/Controllers/admin/AdminTopController.php
<?php
namespace App\Http\Controllers\admin;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class AdminTopController extends Controller
{
function show(){
return view("admin.admin_top");
}
}
管理画面TOPはadmin.admin_topで指定したViewを表示する形とします。
■管理側ユーザー一覧、詳細
php artisan make:controller admin/ManageUserController
app/Http/Controllers/admin/ManageUserController.php
<?php
namespace App\Http\Controllers\admin;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\User;
class ManageUserController extends Controller
{
function showUserList(){
$user_list = User::orderBy("id", "desc")->paginate(10);
return view("admin.user_list", [
"user_list" => $user_list
]);
}
function showUserDetail($id){
$user = User::find($id);
return view("admin.user_detail", [
"user" => $user
]);
}
}
ユーザー管理機能は一覧を表示するshowUserList()関数と、ユーザー詳細のshowUserDetail($id)関数を用意しています。
use App\User;
Userクラスを利用するので、use句を忘れないように書きましょう。
詳細を表示する関数についてこちらの記事の後半で解説しています。
# ミドルウェアの作成
管理側認証チェックのためのミドルウェアを作成します。
php artisan make:middleware AdminAuth
app/Http/Middleware/AdminAuth.php
<?php
namespace App\Http\Middleware;
use Closure;
class AdminAuth
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
//セッションの値を確認する
if(false == $request->session()->get("admin_auth")){
return redirect("admin/login");
}
return $next($request);
}
}
SimpleAuthクラスを参考に、admin_authの有無を追加しています。こちらも$request->session()を使っています。
次に、作成したAdminAuthクラスを有効にするため、Kernelに登録します。
app/Http/Kernel.php
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
//管理用ミドルウェアを追加
'auth.admin' => \App\Http\Middleware\AdminAuth::class
];
auth.adminという名前でAdminAuthクラスを登録しています。
# テンプレートの準備 レイアウトの作成
コントローラーで設定した下記の4つのテンプレートを作成していきます。
・admin.admin_login
・admin.admin_top
・admin.user_list
・admin.user_detail
まずは、レイアウトファイルの修正をしていきます。
laravel/uiの初期化で作成されたレイアウトファイル「resources/views/layouts/app.blade.php」を開いて次の場所を編集します。
resources/views/layouts/app.blade.php
■ app.jsの読み込みの削除
<!-- Scripts -->
<script src="{{ asset('js/app.js') }}" defer></script>
↓
削除
■ app.cssをbootstrap.cssに置き換え
<!-- Styles -->
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
↓
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" rel="stylesheet">
■ bootstrapに必要なjavascriptの読み込み
</body>
↓
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script>
</body>
app.jsの削除、app.cssの変更、</body>の直前にbootstrapに必要なJavaScriptを読み込む3点の修正を行います。これでbootstrapを利用することが出来るようになります。
この段階で「http://127.0.0.1:8000/register」などを開いてデザインが入っていることを確認してください。
こちらのapp.blade.phpをコピーして管理画面用のadmin.blade.phpを作成します。admin.blade.phpは区別がつくようにタイトルのテキストを書き換えておきます。
resources/views/layouts/admin.blade.php
<a class="navbar-brand" href="{{ url('/') }}">
{{ config('app.name', 'Laravel') }}
</a>
↓
<a class="navbar-brand" href="{{ url('admin') }}">
管理ページ
</a>
# テンプレートの準備 ログインページの作成
管理側のログインページを作成します。ユーザーログイン機能の作成のテンプレートをベースに次のようにファイルを作成します。
resources/views/admin/admin_login.blade.php
@extends('layouts.admin')
@section('content')
<div class="container">
<div class="card">
<div class="card-header">ログイン</div>
<div class="card-body">
@if ($errors->any())
<div style="color:red;">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<form method="post" action="{{ url('admin/login') }}">
@csrf
<div>
ID: <input class="form-control" type="text" name="user_id" value="" />
</div>
<div>
PASS: <input class="form-control" type="password" name="password" value="" />
</div>
<div class="mt-3">
<input class="btn btn-primary" type="submit" value="ログイン" />
</div>
</form>
</div>
</div>
</div>
@endsection
@extends('layouts.admin')
@extends構文は、先程作成したadminレイアウトを使う設定です。
レイアウトを使う場合は@section〜@endsectionで囲った中身にテンプレートの本体を記述します。
class="form-control"など、bootstrap用に最小限のクラスを指定しています。
ログインフォームの遷移先が「admin/login」になっていることを確認してください。
# テンプレートの準備 トップページの作成
トップページはユーザー一覧へのリンクと、ログアウトフォームを設置します。
resources/views/admin/admin_top.blade.php
@extends('layouts.admin')
@section('content')
<div class="container">
<div class="card">
<div class="card-header">管理側TOP</div>
<div class="card-body">
<div>
<a href="{{ url('admin/user_list') }}" class="btn btn-primary">ユーザー一覧</a>
</div>
<form method="post" action="{{ url('admin/logout') }}">
@csrf
<input type="submit" class="btn btn-danger" value="ログアウト" />
</form>
</div>
</div>
</div>
@endsection
ログアウトフォームの遷移先が「admin/logout」になっていることを確認してください。
# テンプレートの準備 ユーザー一覧の作成
ユーザー一覧のテンプレートはControllerから渡されたuser_listを表示します。
resources/views/admin/user_list.blade.php
@extends('layouts.admin')
@section('content')
<div class="container">
<div class="card">
<div class="card-header">ユーザー一覧</div>
<div class="card-body">
<ul class="list-group">
@foreach ($user_list as $user)
<li class="list-group-item">
<a href="{{ url('admin/user/' . $user->id) }}">
{{ $user->name }}
</a>
</li>
@endforeach
</ul>
<div class="mt-3">
{{ $user_list->links() }}
</div>
</div>
</div>
</div>
@endsection
ページャーを使うため、「{{ $user_list->links() }}」を追加しています。
<a href="{{ url('admin/user/' . $user->id) }}">
{{ $user->name }}
</a>
ユーザー詳細にリンクを設定するには「admin/user/{id}」の形にする必要があります。PHPで書いた時と同じく「.」でidを結合しています。
# テンプレートの準備 ユーザー詳細の作成
ユーザー詳細はUserモデルの値を表示します。
resources/views/admin/user_detail.blade.php
@extends('layouts.admin')
@section('content')
<div class="container">
<div class="card">
<div class="card-header">
<a href="{{ url('admin/user_list') }}">ユーザー一覧</a> > ユーザー詳細
</div>
<div class="card-body">
<ul class="list-group">
<li class="list-group-item">名前: {{ $user->name }}</li>
<li class="list-group-item">メール: {{ $user->email }}</li>
<li class="list-group-item">作成日: {{ $user->created_at->format('Y/m/d H:i:s') }}</li>
<li class="list-group-item">更新日: {{ $user->updated_at->format('Y/m/d H:i:s') }}</li>
</ul>
</div>
</div>
</div>
@endsection
Userテーブルには「name」「email」「created_at」「updated_at」とパスワード(暗号化されたもの)があります。今回はパスワード以外の値を出しています。
例えばこれをフォームの形にすると、管理画面からユーザーの情報を書き換える、といった機能を作ることが出来ます。
# ルーティングの設定
コントローラー、Viewの準備が終わったので、ルーティングを行います。
今回は「admin/」以下全てにAdminAuthミドルウェアを設定したいと考えています。一件一件Route::get()->middleware("auth.admin")と書いてもよいですが、Laravelのルーティングはもっと簡単にかけます。
複数のルーティングに対してミドルウェアを設定したい場合、Route::group()を使うと一気にMiddlewareを設定出来ます。
以下のような形式で設定します。
Route::group(['middleware' => ['auth.admin']], function () {
//ここにルーティングを記述
});
routes/web.phpを開いてルーティングを設定します。下記のコードを追記しましょう。
routes/web.php
//管理側
Route::group(['middleware' => ['auth.admin']], function () {
//管理側トップ
Route::get('/admin', 'admin\AdminTopController@show');
//ログアウト実行
Route::post('/admin/logout', 'admin\AdminLogoutController@logout');
//ユーザー一覧
Route::get('/admin/user_list', 'admin\ManageUserController@showUserList');
//ユーザー詳細
Route::get('/admin/user/{id}', 'admin\ManageUserController@showUserDetail');
});
//管理側ログイン
Route::get('/admin/login', 'admin\AdminLoginController@showLoginform');
Route::post('/admin/login', 'admin\AdminLoginController@login');
Route::group()の内部で、各コントローラーへの設定を行います。
ログイン画面とログイン実行はログイン前に呼ばれるので、Route::group()の外に書いています。
# サーバーで動作確認
ブラウザで管理画面を開いて動作確認をしてみてください。
・ログイン画面を開いてhogehoge, fugafugaでログイン出来ること
・ユーザー一覧、詳細の確認
・ログアウトの処理
・ユーザー側でログインしながら管理側でログイン・ログアウトができるか
# (補足)ページャーの確認用にUserをSeederで作成する
手動でユーザーを登録するのは大変なので、ページャーの動作確認用にUserを複数件追加するためにUserSeederを作成します。
php artisan make:seeder UserSeeder
作成されたdatabase/seeds/UserSeeder.phpを編集します。
database/seeds/UserSeeder.php
<?php
use Illuminate\Database\Seeder;
class UserSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
for($i=0; $i<30; $i++){
DB::table('users')->insert([
'name' => 'User-' . $i,
'email' => 'user-'.$i.'@example.com',
'password' => bcrypt('password-' . $i),
]);
}
}
}
UserSeederを実行するとで次のようなID/Passwordでログイン出来るユーザーが30人作成されます。
ID: user-10@example.com
Password: password-10
db:seedコマンドを実行して、作成したSeederを実行します。
php artisan db:seed --class=UserSeeder
実行後、管理側のユーザー一覧をみてページャーが動作しているかを確認してください。
# まとめ
一気に作成しましたが2個の認証方式を組み合わせる形なので新規に作る部分はほとんどないことがわかります。
このように、過去に作ったサンプルがいくつかあると新しくアプリケーションを作成する時に便利になります。
管理側機能はこのまま実際に公開した場合はセキュリティ的な問題もあるため、IPアドレス制限などをかけるといった対応が必要になります。
しかし、その場合もコードを修正することなく、IPアドレス制限を行うミドルウェアを作るだけで対応出来ます。
◆
本稿を通じて次の内容を確認してください。
・Route::group()を使ったミドルェアの設定方法
・レイアウトの利用方法
・Bootstrapの利用方法
おつかれさまでした!
編集履歴
・2020-09-04: サンプルコードに@endsectionが不足していたのを修正
完成まで突っ走る意気込みです。サポートしていただけると非常に嬉しいです。応援よろしくお願いします。