OHTA412

LavavelのGateを使ってユーザー権限に応じて表示させる内容を変える

管理者・編集者・閲覧者のように各ユーザーに権限を持たせて、その権限に応じて表示させる内容を変える方法です。この記事では、Lavavel6のGate(ゲート)を使って実装します。

Gate(ゲート)とは?
Gateとは、特定のユーザーのみにあるアクションを許可する機能です。「管理者のみ」という条件を定義しておけば、管理者のみ記事削除ボタンを表示させたりできます。

ユーザーテーブルに権限カラムを追加する

ユーザー情報を保存するusersテーブルに権限識別用のカラムを追加します。今回の例ではroleという名前で追加しています。

// app/database/migrations/20XX_XX_XX_000000_create_users_table.php:14行目付近

public function up()
{
    Schema::create('users', function (Blueprint $table) {
        $table->bigIncrements('id');
        $table->string('name');
        $table->string('email')->unique();
        $table->timestamp('email_verified_at')->nullable();
        $table->string('password');
        $table->string('role'); // ここを追加
        $table->rememberToken();
        $table->timestamps();
    });
}

カラムを追加したら、マイグレーションします。

ユーザー認証機能を構築する

ユーザー認証機能を構築することによって、アプリケーション上でユーザー登録とログイン・ログアウトができるようになります。Laravel6にはユーザー認証機能が用意されていないので、パッケージを使って構築していきます。

下記コマンドを実行し、composerでlaravel/uiパッケージをインストールします。laravel/uiパッケージは、フロントエンドのフレームワークを組み込むときのベースになる機能を提供してくれるものです。これを使って、ユーザー認証機能のフロントエンド部分を生成します。Laravel6の場合はバージョン1にしか対応していないので、バージョン1をインストールします。

$ composer require laravel/ui "^1.0" --dev

次に、下記artisanコマンドでユーザー認証機能を追加します。

$ php artisan ui vue --auth

ここまででLavavelにユーザー認証機能を追加できましたが、まだフロントエンド側が残っています。下記コマンドで、npmをインストールしてビルドします。Node.jsが必要なので、PCにインストールしていない場合は別途インストールします。

$ npm install
$ npm run dev

これでフロントページ右上に「LOGIN」と「REGISTER」が表示されて、ユーザー認証機能が使えるようになります。

ユーザー登録して権限を管理者にする

先ほど作ったユーザー認証機能を使って、新しくユーザーを登録します。phpMyAdminを使ってusersテーブルを開き、登録したユーザーの権限(roleカラム)を変更します。今回は管理者という意味で「administrator」という文字列にします。

roleが変更できればいいので、必ずしもphpMyAdminを使う必要はありません。

Gateを定義する

今回は「管理者のみ」という条件をGateで定義します。App\Providers内にあるAuthServiceProvider.phpというファイルのboot()内にGateファサードを使って定義します。

// App\Providers\AuthServiceProvider.php:24行目付近

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

    Gate::define('isAdmin', function($user){
        return $user->role === 'administrator';
    });
}

上記コードの7~9行目で「isAdmin」という名前でGateを定義しているので、ここの部分を追記します。Gateは第1引数にユーザーインスタンスを受け取るので、そのユーザーインスタンスのroleがadministratorと等しいかをreturnで返しています。

Bladeディレクティブ@canでGateを使う

先ほど定義したGateはBladeディレクティブで使用できます。管理者ユーザー(roleがasministrator)のみ削除ボタンを表示させたい場合は、下記のようになります。

// blade

@can('isAdmin')
    <button>削除する</button>
@else
    <p>管理者しか削除できません</p>
@endcan

コントローラーの処理でGateを使う

コントローラーの処理でGateを使用することもできます。管理者ユーザーのみ実行できる処理を書く場合は、下記のようになります。

use Gate;

class PostController extends Controller
{
    public function index()
    {
        Gate::authorize('isAdmin');

        // 管理者ユーザーのみ以下処理が実行される
        $posts = Post::all();
        return view('posts.index', compact('posts'));
    }
}

まず、1行目のようにuseでGateを読み込みます。7行目で管理者ユーザーの認証を行っています。管理者ユーザーでない場合は403ページに飛ばされて、それ以降の処理は実行されません。

条件を追加する

「管理者、あるいは記事を投稿したユーザーのみ」という条件に変更してみます。投稿記事のテーブルには、投稿者ID(user_idカラム)がある想定です。

// App\Providers\AuthServiceProvider.php:24行目付近

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

    Gate::define('isAdmin', function($user, $post){
        return $user->role === 'administrator' || $user->id === $post->user_id;
    });
}

7行目に$postという引数を追加しました。のちにbladeディレクティブの第2引数に記事のコレクションを指定します。その値がここに入ってきます。

8行目で、「ログインしているユーザーのIDと記事のuser_idが等しいか」という条件を追加しています。

// blade

@foreach ($posts as $post)
    @can('isAdmin', $post)
        <button>削除する</button>
    @else
        <p>管理者か、この記事の投稿者しか削除できません</p>
    @endcan
@endforeach

4行目のように、第2引数に記事のコレクションを指定することで、先ほど定義したGate内で判定に使用することができます。