LoginSignup
6
3

More than 3 years have passed since last update.

Laravel6.0で管理者用画面を作ったメモ

Last updated at Posted at 2019-10-22

概要

前回の続き。
今回は、ログインユーザに管理者権限を与えて、管理者専用画面を表示する。

データベース

ER図

今回、DBは以下のようにする。
管理者クーポンが発行されたユーザが管理者権限を持つとする。

image.png

マイグレーション

以下のコマンドでマイグレーションファイルを作成できる。

php artisan make:migration create_coupons_table

上記で作成したファイルに含まれる$table->timestamps();はnullableなcreated_atupdated_atを作成する。
DBに作ってもらいたかったので、作成時、更新時の時刻を挿入する設定にしている。

whitemap/database/migrations/create_coupons_table.php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateCouponsTable extends Migration
{
    public function up()
    {
        Schema::create('coupons', function (Blueprint $table) {
            $table->string('id');
            $table->integer('type')->default(config('const.Coupons.TYPE_GET', 1))->comment('1:取得, 2:使用');
            $table->string('name');
            $table->boolean('is_display')->default(true);
            $table->timestamp('created_at')->useCurrent();
            $table->timestamp('updated_at')->default(DB::raw('CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP'));
        });
    }
    public function down()
    {
        Schema::dropIfExists('coupons');
    }
}

クーポン種別は定数で定義してみた。

whitemap/config/const.php
<?php

return [
    // Couponsで使う定数
    'Coupons' => [
        'TYPE_GET' => 1,
        'TYPE_USE' => 2,
    ],
];

ユーザとクーポンの紐づけテーブルでは外部キーを設定し、
ユーザテーブルやクーポンテーブルにないものは登録できないようにした。

whitemap/database/create_user_coupons_table.php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateUserCouponsTable extends Migration
{
    public function up()
    {
        Schema::create('user_coupons', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('coupon_id')->comment('クーポンID');
            $table->integer('subscribe_user_id')->unsigned()->comment('受取ユーザID');
            $table->integer('publish_user_id')->unsigned()->comment('発行ユーザID');
            $table->dateTime('expire')->nullable()->comment('利用期限');
            $table->timestamp('created_at')->useCurrent();
            $table->timestamp('updated_at')->default(DB::raw('CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP'));
            $table->softDeletes();

            $table->foreign('subscribe_user_id')->references('id')->on('users');
            $table->foreign('publish_user_id')->references('id')->on('users');
            $table->foreign('coupon_id')->references('id')->on('coupons');
        });
    }

    public function down()
    {
        Schema::dropIfExists('user_coupons');
    }
}

php artisan migareteでデータベース更新。

シーダの設定

php artisan make:seeder UsersTableSeeder 

システム用のユーザと初期ユーザを作成。

whitemap/database/seeds/UserSeeder.php
<?php
use Illuminate\Database\Seeder;

class UserSeeder extends Seeder
{
    public function run()
    {
        $user = [
            'id' => 1,
            'firebase_uid' => 'system',
            'name' => 'システム管理者',
            'twitter_screen_name' => '',
            'twitter_profile_image_url_https' => '',
        ];
        DB::table('users')->insert($user);
        $user = [
            'id' => 2,
            'firebase_uid' => env('FIRST_USER_FIREBASE_UID'),
            'name' => env('FIRST_USER_NAME'),
            'twitter_screen_name' => env('FIRST_USER_TWITTER_SCREEN_NAME'),
            'twitter_profile_image_url_https' => env('FIRST_USER_TWITTER_PROFILE_IMAGE_URL'),
        ];
        DB::table('users')->insert($user);
    }
}

管理者クーポンを追加。

whitemap/database/seeds/Couponseeder.php
<?php

use Illuminate\Database\Seeder;
use App\Enums\Coupon\CouponIds;

class CouponsSeeder extends Seeder
{
    public function run()
    {        
        $coupon = [
            'id' => CouponIds::ADMIN(),
            'name' => '管理者クーポン',
            'is_display' => false,
        ];
        DB::table('coupons')->insert($coupon);
    }
}

クーポンIDはEnumで登録してみた。

composer require myclabs/php-enum
whitemap/app/Enums/Coupon.php
<?php
namespace App\Enums\Coupon;
use MyCLabs\Enum\Enum;

class CouponIds extends Enum
{
    const ADMIN = 'admin';
}

システムユーザから初期ユーザに向けて管理者クーポンを発行

whitemap/database/seeds/UserCouponsSeeder.php
<?php
use Illuminate\Database\Seeder;
use App\Enums\Coupon\CouponIds;

class UserCouponsSeeder extends Seeder
{
    public function run()
    {
        $user_coupon = [
            'id' => 1,
            'coupon_id' => CouponIds::ADMIN(),
            'subscribe_user_id' => 2,
            'publish_user_id' => 1,
        ];
        DB::table('user_coupons')->insert($user_coupon);
    }
}

実行するSeederを指定

whitemap/database/seeds/DatabaseSeeder.php
<?php
use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    public function run()
    {
        // $this->call(UsersTableSeeder::class);
        $this->call([
            UserSeeder::class,
            CouponsSeeder::class,
            UserCouponsSeeder::class,
        ]);
    }
}

シーダの実行

php artisan db:seed 

アプリケーション

モデルの設定

php artisan make:model Models/UserCoupon
whitemap/app/Models//UserCoupon.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;

class UserCoupon extends Model
{
    protected $table = 'user_coupons';
}

subscribe_user_idでユーザクーポンとユーザを紐づける。

whitemap/app/User.php
<?php
namespace App;
use Laravel\Passport\HasApiTokens; // 追加
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use App\Enums\Coupon\CouponIds;

class User extends Authenticatable
{
    use Notifiable, HasApiTokens; // HasApiTokens を追加
    public function __construct(array $attributes = []){
    }

    protected $fillable = [
        'name', 'twitter_screen_name','twitter_profile_image_url_https', 'firebase_uid'
    ];
    protected $hidden = [
        'password', 'remember_token',
    ];
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];


+    public function userCoupons(){
+        return $this->hasMany('App\Models\UserCoupon',  'subscribe_user_id');
+    }
}

ゲートの設定

whitemap/app/Gate/AdminAccess
<?php 
namespace App\Gate;
use App\User;
use App\Enums\Coupon\CouponIds;

final class AdminAccess
{
    public function __invoke(User $user): bool 
    {
        // 管理者用のクーポンを持っているかDBに問い合わせる。
        return $user->userCoupons()->where('coupon_id',CouponIds::ADMIN())->exists();
    }
}

上記で設定したゲートをadmin-accessの名前で登録する。

whitemap/app/Providers/AuthServiceProvider.php
<?php
namespace App\Providers;
use App\Auth\MySessionGuard; 
use App\Auth\MyEloquentUserProvider; 
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Gate;
use Auth; 
use App\Gate\UserAccess; 
use App\Gate\AdminAccess; 
use \Psr\Log\LoggerInterface;

class AuthServiceProvider extends ServiceProvider
{
    protected $policies = [
        // 'App\Model' => 'App\Policies\ModelPolicy',
    ];

    public function boot(LoggerInterface $logger)
    {
        $this->registerPolicies();

        Auth::provider('my_eloquent', function($app, array $config) {
            return new MyEloquentUserProvider($app['hash'], $config['model']);
        });


+        // 認可
+        Gate::define('admin-access', new AdminAccess);

        // 認可の前にロギング
        Gate::before(function ($user, $ability) use ($logger) {
            // Log::info("Hello my log,");
            $logger->info($ability, ['firebase_uid'=>$user->getAuthIdentifier()]);
        });
    }
}

ルーティング

whitemap/routes/web.php
Route::group(['middleware' => ['auth', 'can:admin-access']], function () {
    // この中は管理者権限の場合のみルーティングされる
    Route::get('/admin', function (Illuminate\Http\Request $request) {
        return view('admin/dashboard');
    });
});

管理者ページへのリンク

管理者権限があるときだけ、管理者ページへのリンクを表示。

whitemap/resources/views/home.blade.php
@extends('layouts.app') 
@section('title') マイページ @endsection
@section('head-scripts')
@if(Auth::check())
<script src="https://www.gstatic.com/firebasejs/7.2.0/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/7.2.0/firebase-auth.js"></script>
<script src="{{ mix('js/home/index.js') }}"></script>
@endif
@endsection

@section('content')
<main role="main" class="container">
<div class="starter-template">
    <h1>マイページ</h1>
    @if(Auth::check())
      <ul>
+        @can('admin-access')
+          <li><a href="/admin">管理者画面へ</a></li>
+        @endcan
        <li><a href="#" id="logout">ログアウト</a></li>
      </ul>
    @else 
      こんにちは!  ゲストさん <br />
      <a href="/login">ログイン</a>
    @endif
</div></main>
@endsection

参考

Laravel で定数をつかうよ
Laravel で Enum を使う
Laravel 6.0 Artisan コンソール
全 68 種類!Laravel 5.6 の artisan コマンドまとめ
【メモ】【Laravel】外部キー制約付き Migrate がさっぱり動かないときのチェック・ポイント(Mysql)
Laravel の DB migration で日付のデフォルトを指定
Laravel(Eloquent)の save メソッドを使ったら MySQL の timestamp 型で謎な挙動が発生した話
管理者クーポンによるタグ画面の制御
blade テンプレートでの切替
Laravel 6.0 基本のタスクリスト
laravel
readouble laravel
【Laravel】 認証や認可に関する補足資料
Laravel 6.0 認可
Laravel 6.0 ルーティング
Laravel 6.0 ミドルウェア
Laravel の Gate(ゲート)機能で権限(ロール)によるアクセス制限を実装する
Laravel 6.0 バリデーション

6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3