たった3ステップ!Laravel Gateで実行権限を導入する実例

さてさて、以前公開した「シンプル!Laravelで「権限つき」ログインさせる方法」では、ユーザーごとにアクセスできるページを決め、権限が無い場合は強制的にリダイレクトするというテクニックを紹介しました。

このテクニックを使えば、とてもシンプルにユーザータイプでアクセスページを分けることができます。ただ、アクセスではなく実行権限となると話は少し違ってきます。

例えば、

  • 社長だけがデータ削除できる
  • 社員は変更が可能
  • アルバイトは見るだけ

というような、より細かい権限をつけたい場合です。

しかし、そこは人気フレームワークのLaravelです。
実は、このようなより複雑な実行権限をつけることができるLaravel Gateという機能を標準搭載しているんですね。

そこで!

今回はこのLaravel Gateを使って役職ごとに実行権限をつける実例を紹介します。ぜひ参考にしてみてくださいね。

※ 開発環境: Laravel 5.7

やりたいこと

今回は以下3つのユーザータイプがある会社やお店のウェブサイトだと考えてください。

  • 社長
  • 従業員
  • アルバイト

そして、これらのユーザーがアクセスできる「業務連絡」ページがあるとして、ここに次の実行権限をつけることにします。

  • 社長 ・・・ 閲覧、追加、変更、削除
  • 従業員 ・・・ 閲覧、追加、変更
  • アルバイト ・・・ 閲覧

では実際に開発していきましょう!

テストデータをつくる

まずは、次のコマンドでLaravelでログイン機能が使えるようにします。

php artisan make:auth

そして、作成されたマイグレーションCreateUsersTable.phpを開いてroleフィールドを追加します。

public function up()
{
    Schema::create('users', function (Blueprint $table) {
        $table->increments('id');
        $table->string('name');
        $table->string('email')->unique();
        $table->string('role'); // ユーザータイプ
        $table->timestamp('email_verified_at')->nullable();
        $table->string('password');
        $table->rememberToken();
        $table->timestamps();
    });
}

続いて、テストデータを追加するUsersTableSeederを作ります。

php artisan make:seed UsersTableSeeder

作成されたUsersTableSeeder.phpを開いて中身を次のように記述します。

public function run()
{
    $users = [
        [
            'name' => '社長',
            'role' => 'president',
            'email' => 'president@example.com'
        ],
        [
            'name' => '従業員',
            'role' => 'employee',
            'email' => 'employee@example.com'
        ],
        [
            'name' => 'アルバイト',
            'role' => 'part_timer',
            'email' => 'part_timer@example.com'
        ]
    ];

    foreach ($users as $user) {

        \App\User::create([
            'name' => $user['name'],
            'email' => $user['email'],
            'role' => $user['role'],
            'password' => bcrypt('xxxxxxxx')
        ]);

    }
}

次に忘れてはいけないのが、このUsersTableSeederを有効にするためのDatabaseSeeder.phpへの登録です。

<?php

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    public function run()
    {
        $this->call(UsersTableSeeder::class);
    }
}

ではマイグレーションを実行してみましょう。

php artisan migrate:fresh --seed

DBテーブルこのようになりました。

Laravel Gateの実行権限をつくる

ではLaravel Gateを使って「やりたいこと」に書いた次の権限をつくっていきましょう。

  • 社長 ・・・ 閲覧、追加、変更、削除
  • 従業員 ・・・ 閲覧、追加、変更
  • アルバイト ・・・ 閲覧

コードを書くのは、app/Providers/AuthServiceProvider.phpboot()内ですです。

Policyをつくる

権限が少なければ、そのまま次のようにAuthServiceProvider.phpに直書きしても構いません。

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

    Gate::define('delete-announcement', function ($user) {

        return ($user->role == 'president'); // 社長だけ削除OK

    });
}

ただ、Laravelには保守管理しやすいように実行権限を管理するPolicyという機能がありますので、今回はこちらで実装します。

次のコマンドを実行してください。

php artisan make:policy AnnouncementPolicy

すると、app/Policies/AnnoucementPolicy.phpというファイルが作成されるので、このファイルを開いて次のようにします。

<?php

namespace App\Policies;

use App\User;
use Illuminate\Auth\Access\HandlesAuthorization;

class AnnouncementPolicy
{
    use HandlesAuthorization;

    /* 閲覧 */
    public function view(User $user)
    {
        $user_types = [
            'president', // 社長
            'employee',  // 従業員
            'part_timer' // アルバイト
        ];
        return (in_array($user->role, $user_types));
    }

    /* 追加 */
    public function create(User $user)
    {
        $user_types = [
            'president', // 社長
            'employee'   // 従業員
        ];
        return (in_array($user->role, $user_types));
    }

    /* 変更 */
    public function update(User $user)
    {
        $user_types = [
            'president', // 社長
            'employee'   // 従業員
        ];
        return (in_array($user->role, $user_types));
    }

    /* 削除 */
    public function delete(User $user)
    {
        return ($user->role == 'president'); // 社長だけOK
    }
}

やっていることは、それぞれ$user->roleの中身をチェックしてreturnしているだけです。

では、AuthServiceProvider.phpで、AnnouncementPolicyを登録しましょう。

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

    Gate::resource('announcements', 'App\Policies\AnnouncementPolicy');
}

ちなみにGate::resource()は、以下4つの権限を一気に作ってくれるショートカットの書き方です。

  • view
  • create
  • update
  • delete

そのため、上のコードは次のコードと同じになります。
お好みで使い分けてください。

Gate::define('announcements.view', 'App\Policies\AnnouncementPolicy@view');
Gate::define('announcements.create', 'App\Policies\AnnouncementPolicy@create');
Gate::define('announcements.update', 'App\Policies\AnnouncementPolicy@update');
Gate::define('announcements.delete', 'App\Policies\AnnouncementPolicy@delete');

実行権限を設置する

では、実際に作成した権限を設置するテストコードを作ってみましょう。

$user = auth()->loginUsingId(1);

/* 閲覧 */
if(Gate::allows('announcements.view', $user)) {

    echo '閲覧できます。';

} else {

    echo '閲覧できません!';

}

/* 追加 */
if(Gate::allows('announcements.create', $user)) {

    echo '追加できます。';

} else {

    echo '追加できません!';

}

/* 変更 */
if(Gate::allows('announcements.update', $user)) {

    echo '変更できます。';

} else {

    echo '変更できません!';

}

/* 削除 */
if(Gate::allows('announcements.delete', $user)) {

    echo '削除できます。';

} else {

    echo '削除できません!';

}

まず1行目はユーザー(ここでは社長ユーザー)を取得しています。

そして、Gate::allows()を使って権限が「ある人だけ」をチェックして分岐させています。もし、逆に権限が「ない人だけ」をチェックしたい場合はGate::denies()を使ってください。

実行結果

では実際にどのようになるか各ユーザーの実行結果を見てみましょう。

社長の場合

従業員の場合

アルバイトの場合

Bladeで権限がある人にだけリンクを表示する

Laravel Gateは通常のPHPコードだけでなく、テンプレート・エンジンBladeの中でも利用することができます。

例えば、「業務連絡の権限がある人にだけリンクを表示する」といった使い方です。

では、実際に見てみましょう。

<!-- 業務連絡 -->
@can('announcements.view', auth()->user())
    <a href="/announcements">業務連絡</a>
@endcan

使い方は先ほどのallows()とほぼ同じで@canを使います。(逆は@cannot

これをLaravelに始めから用意されているwelcom.blade.phpで実行すると次のようになります。

まずはログインしていない状態です。

何も表示されていません。

次にログインして表示したものです。

業務連絡というリンクが表示されました。
もちろん、権限をもっていなければ、ログインしていても表示されないパターンもあります。

お疲れ様でした!

おわりに

ということで、今回はLaravel Gateを使って実行権限を管理する方法をご紹介しました。

特に会社組織のサイトなど、ユーザータイプが多い場合に重宝するのではないでしょうか。また、ユーザーが仮登録しかしていない場合は投稿を拒否したり、投稿した本人だけ変更/削除できるようにするという設定もできます。

なお、実行権限をPolicyとして分割管理しておけば以前使ったものを再利用できますので、その後の開発効率がグンっとあがるというメリットもあります。

ぜひ活用してみてはいかがでしょうか。

ではでは〜!