Skip to content

認可

はじめに

Laravelは、組み込みの認証サービスに加えて、特定のリソースに対するユーザーアクションを認可する簡単な方法も提供しています。例えば、ユーザーが認証されていても、アプリケーションが管理する特定のEloquentモデルやデータベースレコードを更新または削除する権限がない場合があります。Laravelの認可機能は、この種の認可チェックを管理するための簡単で整理された方法を提供しています。

Laravelは、アクションを認可するための主な2つの方法を提供しています:ゲートポリシーです。ゲートとポリシーは、ルートとコントローラーのようなものと考えることができます。ゲートは、クロージャベースのシンプルな認可アプローチを提供し、ポリシーは、特定のモデルやリソースに関するロジックをグループ化します。このドキュメントでは、まずゲートを説明し、次にポリシーを説明します。

アプリケーションを構築する際に、ゲートのみを使用するか、ポリシーのみを使用するかを選択する必要はありません。ほとんどのアプリケーションは、ゲートとポリシーの組み合わせを含む可能性があり、それは全く問題ありません。ゲートは、モデルやリソースに関連しないアクション(例えば、管理者ダッシュボードの表示)に最も適しています。一方、特定のモデルやリソースに対するアクションを認可したい場合は、ポリシーを使用するべきです。

ゲート

ゲートの記述

Warning

ゲートは、Laravelの認可機能の基本を学ぶのに最適な方法です。ただし、堅牢なLaravelアプリケーションを構築する際には、認可ルールを整理するためにポリシーの使用を検討する必要があります。

ゲートは、ユーザーが特定のアクションを実行する権限があるかどうかを判断するクロージャです。通常、ゲートはGateファサードを使用して、App\Providers\AppServiceProviderクラスのbootメソッド内で定義されます。ゲートは常にユーザーインスタンスを最初の引数として受け取り、関連するEloquentモデルなどの追加の引数をオプションで受け取ることができます。

この例では、特定のApp\Models\Postモデルを更新できるかどうかを判断するゲートを定義します。ゲートは、ユーザーのidを投稿を作成したユーザーのuser_idと比較することでこれを実現します:

use App\Models\Post;
use App\Models\User;
use Illuminate\Support\Facades\Gate;

/**
 * 任意のアプリケーションサービスのブートストラップ
 */
public function boot(): void
{
    Gate::define('update-post', function (User $user, Post $post) {
        return $user->id === $post->user_id;
    });
}

コントローラーのように、ゲートはクラスコールバックの配列を使用して定義することもできます:

use App\Policies\PostPolicy;
use Illuminate\Support\Facades\Gate;

/**
 * 任意のアプリケーションサービスのブートストラップ
 */
public function boot(): void
{
    Gate::define('update-post', [PostPolicy::class, 'update']);
}

アクションの認可

ゲートを使用してアクションを認可するには、Gateファサードによって提供されるallowsまたはdeniesメソッドを使用する必要があります。現在認証されているユーザーをこれらのメソッドに渡す必要はありません。Laravelは自動的にユーザーをゲートクロージャに渡します。認可が必要なアクションを実行する前に、アプリケーションのコントローラー内でゲートの認可メソッドを呼び出すのが一般的です:

<?php

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use App\Models\Post;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;

class PostController extends Controller
{
    /**
     * 指定された投稿を更新します。
     */
    public function update(Request $request, Post $post): RedirectResponse
    {
        if (! Gate::allows('update-post', $post)) {
            abort(403);
        }

        // 投稿を更新...

        return redirect('/posts');
    }
}

現在認証されているユーザー以外のユーザーがアクションを実行する権限があるかどうかを判断したい場合は、GateファサードのforUserメソッドを使用できます:

if (Gate::forUser($user)->allows('update-post', $post)) {
    // ユーザーは投稿を更新できます...
}

if (Gate::forUser($user)->denies('update-post', $post)) {
    // ユーザーは投稿を更新できません...
}

anyまたはnoneメソッドを使用して、一度に複数のアクションを認可することもできます:

if (Gate::any(['update-post', 'delete-post'], $post)) {
    // ユーザーは投稿を更新または削除できます...
}

if (Gate::none(['update-post', 'delete-post'], $post)) {
    // ユーザーは投稿を更新または削除できません...
}

認可または例外のスロー

アクションを認可しようとして、ユーザーが指定されたアクションを実行できない場合に自動的にIlluminate\Auth\Access\AuthorizationExceptionをスローしたい場合は、Gateファサードのauthorizeメソッドを使用できます。AuthorizationExceptionのインスタンスは、Laravelによって自動的に403 HTTPレスポンスに変換されます:

Gate::authorize('update-post', $post);

// アクションは認可されました...

追加のコンテキストの提供

アクションを認可するためのゲートメソッド(allowsdeniescheckanynoneauthorizecancannot)と認可Bladeディレクティブ@can@cannot@canany)は、配列を2番目の引数として受け取ることができます。これらの配列要素はゲートクロージャにパラメーターとして渡され、認可決定を行う際の追加のコンテキストとして使用できます:

use App\Models\Category;
use App\Models\User;
use Illuminate\Support\Facades\Gate;

Gate::define('create-post', function (User $user, Category $category, bool $pinned) {
    if (! $user->canPublishToGroup($category->group)) {
        return false;
    } elseif ($pinned && ! $user->canPinPosts()) {
        return false;
    }

    return true;
});

if (Gate::check('create-post', [$category, $pinned])) {
    // ユーザーは投稿を作成できます...
}

ゲートのレスポンス

これまで、ゲートが単純なブール値を返す例を見てきました。しかし、場合によっては、より詳細なレスポンスを返したい場合があります。そのためには、ゲートからIlluminate\Auth\Access\Responseを返すことができます:

use App\Models\User;
use Illuminate\Auth\Access\Response;
use Illuminate\Support\Facades\Gate;

Gate::define('edit-settings', function (User $user) {
    return $user->isAdmin
                ? Response::allow()
                : Response::deny('You must be an administrator.');
});

ゲートから認可レスポンスを返しても、Gate::allowsメソッドは依然として単純なブール値を返します。ただし、Gate::inspectメソッドを使用して、ゲートから返された完全な認可レスポンスを取得することができます:

$response = Gate::inspect('edit-settings');

if ($response->allowed()) {
    // アクションは認可されました...
} else {
    echo $response->message();
}

Gate::authorizeメソッドを使用すると、アクションが認可されない場合にAuthorizationExceptionをスローし、認可レスポンスによって提供されたエラーメッセージがHTTPレスポンスに伝播されます:

Gate::authorize('edit-settings');

// アクションは認可されました...

HTTPレスポンスステータスのカスタマイズ

ゲートによってアクションが拒否された場合、403 HTTPレスポンスが返されます。ただし、失敗した認可チェックに対して別のHTTPステータスコードを返すと便利な場合があります。Illuminate\Auth\Access\ResponseクラスのdenyWithStatus静的コンストラクタを使用して、HTTPステータスコードをカスタマイズできます:

use App\Models\User;
use Illuminate\Auth\Access\Response;
use Illuminate\Support\Facades\Gate;

Gate::define('edit-settings', function (User $user) {
    return $user->isAdmin
                ? Response::allow()
                : Response::denyWithStatus(404);
});

Webアプリケーションでリソースを非表示にするために404レスポンスが一般的であるため、便宜上denyAsNotFoundメソッドが提供されています:

use App\Models\User;
use Illuminate\Auth\Access\Response;
use Illuminate\Support\Facades\Gate;

Gate::define('edit-settings', function (User $user) {
    return $user->isAdmin
                ? Response::allow()
                : Response::denyAsNotFound();
});

ゲートチェックのインターセプト

特定のユーザーに対して常に特定のアクションを許可または拒否したい場合があります。このために、ゲートチェックをインターセプトするためのbeforeメソッドを使用できます。このメソッドは、他のすべての認可チェックの前に実行されます:

use Illuminate\Support\Facades\Gate;

Gate::before(function ($user, $ability) {
    if ($user->isAdministrator()) {
        return true;
    }
});

beforeクロージャがnull以外の結果を返す場合、その結果はチェックの結果として扱われます。

ゲートチェックの後に実行されるafterメソッドを定義することもできます:

Gate::after(function ($user, $ability, $result, $arguments) {
    if ($user->isAdministrator()) {
        return true;
    }
});

beforeメソッドと同様に、afterクロージャがnull以外の結果を返す場合、その結果はチェックの結果として扱われます。

場合によっては、特定のユーザーにすべての権限を付与したいことがあります。すべての他の認可チェックの前に実行されるクロージャを定義するために、beforeメソッドを使用できます。

use App\Models\User;
use Illuminate\Support\Facades\Gate;

Gate::before(function (User $user, string $ability) {
    if ($user->isAdministrator()) {
        return true;
    }
});

beforeクロージャがnull以外の結果を返す場合、その結果は認可チェックの結果と見なされます。

すべての他の認可チェックの後に実行されるクロージャを定義するために、afterメソッドを使用できます。

use App\Models\User;

Gate::after(function (User $user, string $ability, bool|null $result, mixed $arguments) {
    if ($user->isAdministrator()) {
        return true;
    }
});

beforeメソッドと同様に、afterクロージャがnull以外の結果を返す場合、その結果は認可チェックの結果と見なされます。

インライン認可

時には、現在認証されているユーザーが特定のアクションを実行する権限があるかどうかを、専用のゲートを書かずに判断したいことがあります。Laravelでは、Gate::allowIfGate::denyIfメソッドを介して、この種の「インライン」認可チェックを実行できます。インライン認可は、定義された"before"または"after"の認可フックを実行しません。

use App\Models\User;
use Illuminate\Support\Facades\Gate;

Gate::allowIf(fn (User $user) => $user->isAdministrator());

Gate::denyIf(fn (User $user) => $user->banned());

アクションが認可されていない場合、または現在認証されているユーザーがいない場合、Laravelは自動的にIlluminate\Auth\Access\AuthorizationException例外をスローします。AuthorizationExceptionのインスタンスは、Laravelの例外ハンドラによって自動的に403 HTTPレスポンスに変換されます。

ポリシーの作成

ポリシーの生成

ポリシーは、特定のモデルまたはリソースに関する認可ロジックを整理するクラスです。例えば、アプリケーションがブログである場合、App\Models\Postモデルと、ユーザーが投稿の作成や更新などのアクションを認可するための対応するApp\Policies\PostPolicyがあるかもしれません。

make:policy Artisanコマンドを使用してポリシーを生成できます。生成されたポリシーはapp/Policiesディレクトリに配置されます。このディレクトリがアプリケーションに存在しない場合、Laravelはそれを作成します。

php artisan make:policy PostPolicy

make:policyコマンドは空のポリシークラスを生成します。リソースの表示、作成、更新、削除に関連するポリシーメソッドの例を含むクラスを生成したい場合、コマンドを実行する際に--modelオプションを指定できます。

php artisan make:policy PostPolicy --model=Post

ポリシーの登録

ポリシーの自動検出

デフォルトでは、Laravelはモデルとポリシーが標準のLaravel命名規則に従っている限り、ポリシーを自動的に検出します。具体的には、ポリシーはモデルを含むディレクトリと同じかそれ以上の階層にあるPoliciesディレクトリに配置する必要があります。例えば、モデルはapp/Modelsディレクトリに配置され、ポリシーはapp/Policiesディレクトリに配置されます。この場合、Laravelはまずapp/Models/Policiesでポリシーを探し、次にapp/Policiesで探します。さらに、ポリシー名はモデル名と一致し、Policyサフィックスを持つ必要があります。したがって、UserモデルはUserPolicyポリシークラスに対応します。

独自のポリシー検出ロジックを定義したい場合、Gate::guessPolicyNamesUsingメソッドを使用してカスタムポリシー検出コールバックを登録できます。通常、このメソッドはアプリケーションのAppServiceProviderbootメソッドから呼び出す必要があります。

use Illuminate\Support\Facades\Gate;

Gate::guessPolicyNamesUsing(function (string $modelClass) {
    // 指定されたモデルのポリシークラス名を返す...
});

手動でのポリシー登録

Gateファサードを使用して、アプリケーションのAppServiceProviderbootメソッド内でポリシーとそれに対応するモデルを手動で登録できます。

use App\Models\Order;
use App\Policies\OrderPolicy;
use Illuminate\Support\Facades\Gate;

/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    Gate::policy(Order::class, OrderPolicy::class);
}

ポリシーの記述

ポリシーメソッド

ポリシークラスが登録されたら、それぞれのアクションに対してメソッドを追加できます。例えば、PostPolicyupdateメソッドを定義し、指定されたApp\Models\Userが指定されたApp\Models\Postインスタンスを更新できるかどうかを判断します。

updateメソッドはUserPostインスタンスを引数として受け取り、ユーザーが指定されたPostを更新する権限があるかどうかを示すtrueまたはfalseを返す必要があります。したがって、この例では、ユーザーのidが投稿のuser_idと一致するかどうかを確認します。

<?php

namespace App\Policies;

use App\Models\Post;
use App\Models\User;

class PostPolicy
{
    /**
     * Determine if the given post can be updated by the user.
     */
    public function update(User $user, Post $post): bool
    {
        return $user->id === $post->user_id;
    }
}

必要に応じて、ポリシーに追加のメソッドを定義できます。例えば、viewdeleteメソッドを定義して、さまざまなPost関連のアクションを認可することができますが、ポリシーメソッドには好きな名前を付けることができます。

Artisanコンソールで--modelオプションを使用してポリシーを生成した場合、それにはviewAnyviewcreateupdatedeleterestore、およびforceDeleteアクションのメソッドが既に含まれています。

Note

すべてのポリシーはLaravelのサービスコンテナを介して解決されるため、ポリシーのコンストラクタで必要な依存関係をタイプヒントすることで、自動的に注入されます。

ポリシーのレスポンス

これまで、単純なブール値を返すポリシーメソッドのみを検討してきました。しかし、時にはより詳細なレスポンスを返したい場合があります。そのためには、ポリシーメソッドからIlluminate\Auth\Access\Responseインスタンスを返すことができます。

use App\Models\Post;
use App\Models\User;
use Illuminate\Auth\Access\Response;

/**
 * Determine if the given post can be updated by the user.
 */
public function update(User $user, Post $post): Response
{
    return $user->id === $post->user_id
                ? Response::allow()
                : Response::deny('You do not own this post.');
}

ポリシーから認可レスポンスを返す場合、Gate::allowsメソッドは依然として単純なブール値を返しますが、Gate::inspectメソッドを使用して、ゲートから返される完全な認可レスポンスを取得できます。

use Illuminate\Support\Facades\Gate;

$response = Gate::inspect('update', $post);

if ($response->allowed()) {
    // The action is authorized...
} else {
    echo $response->message();
}

Gate::authorizeメソッドを使用する場合、アクションが認可されていない場合にAuthorizationExceptionをスローし、認可レスポンスによって提供されるエラーメッセージはHTTPレスポンスに伝播されます。

Gate::authorize('update', $post);

// The action is authorized...

HTTPレスポンスステータスのカスタマイズ

ポリシーメソッドによってアクションが拒否された場合、403 HTTPレスポンスが返されますが、時には別のHTTPステータスコードを返すと便利な場合があります。Illuminate\Auth\Access\ResponseクラスのdenyWithStatus静的コンストラクタを使用して、失敗した認可チェックに対して返されるHTTPステータスコードをカスタマイズできます。

use App\Models\Post;
use App\Models\User;
use Illuminate\Auth\Access\Response;

/**
 * Determine if the given post can be updated by the user.
 */
public function update(User $user, Post $post): Response
{
    return $user->id === $post->user_id
                ? Response::allow()
                : Response::denyWithStatus(404);
}

Webアプリケーションでリソースを404レスポンスで隠すのが一般的なパターンであるため、便宜上denyAsNotFoundメソッドが提供されています。

use App\Models\Post;
use App\Models\User;
use Illuminate\Auth\Access\Response;

/**
 * Determine if the given post can be updated by the user.
 */
public function update(User $user, Post $post): Response
{
    return $user->id === $post->user_id
                ? Response::allow()
                : Response::denyAsNotFound();
}

モデルを持たないメソッド

一部のポリシーメソッドは、現在認証されているユーザーのインスタンスのみを受け取ります。この状況は、createアクションを認可する場合に最も一般的です。例えば、ブログを作成している場合、ユーザーが投稿を作成する権限があるかどうかを判断したい場合があります。このような状況では、ポリシーメソッドはユーザーインスタンスのみを受け取ります。

/**
 * Determine if the given user can create posts.
 */
public function create(User $user): bool
{
    return $user->role == 'writer';
}

ゲストユーザー

デフォルトでは、すべてのゲートとポリシーは、受信したHTTPリクエストが認証されたユーザーによって開始されていない場合、自動的にfalseを返します。しかし、ユーザー引数の定義に「オプション」のタイプヒントを宣言するか、nullのデフォルト値を提供することで、これらの認可チェックをゲートとポリシーに通過させることができます。

<?php

namespace App\Policies;

use App\Models\Post;
use App\Models\User;

class PostPolicy
{
    /**
     * 指定された投稿がユーザーによって更新できるかどうかを判定します。
     */
    public function update(?User $user, Post $post): bool
    {
        return $user?->id === $post->user_id;
    }
}

ポリシーフィルター

特定のユーザーに対して、特定のポリシー内のすべてのアクションを認可したい場合があります。これを実現するには、ポリシーにbeforeメソッドを定義します。beforeメソッドは、ポリシー内の他のメソッドよりも前に実行され、意図したポリシーメソッドが実際に呼び出される前にアクションを認可する機会を与えます。この機能は、アプリケーションの管理者がすべてのアクションを実行できるように認可するために最も一般的に使用されます。

use App\Models\User;

/**
 * 事前認可チェックを実行します。
 */
public function before(User $user, string $ability): bool|null
{
    if ($user->isAdministrator()) {
        return true;
    }

    return null;
}

特定のタイプのユーザーに対してすべての認可チェックを拒否したい場合は、beforeメソッドからfalseを返すことができます。nullが返された場合、認可チェックはポリシーメソッドに渡されます。

Warning

ポリシークラスに、チェックされる能力の名前と一致する名前のメソッドが含まれていない場合、ポリシークラスのbeforeメソッドは呼び出されません。

ポリシーを使用したアクションの認可

ユーザーモデル経由

Laravelアプリケーションに含まれるApp\Models\Userモデルには、アクションを認可するための2つの便利なメソッド、cancannotが含まれています。cancannotメソッドは、認可したいアクションの名前と関連するモデルを受け取ります。例えば、ユーザーが特定のApp\Models\Postモデルを更新する権限があるかどうかを判定します。通常、これはコントローラメソッド内で行われます。

<?php

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use App\Models\Post;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;

class PostController extends Controller
{
    /**
     * 指定された投稿を更新します。
     */
    public function update(Request $request, Post $post): RedirectResponse
    {
        if ($request->user()->cannot('update', $post)) {
            abort(403);
        }

        // 投稿を更新...

        return redirect('/posts');
    }
}

指定されたモデルに対してポリシーが登録されている場合、canメソッドは自動的に適切なポリシーを呼び出し、ブール値の結果を返します。モデルに対してポリシーが登録されていない場合、canメソッドは指定されたアクション名に一致するクロージャベースのゲートを呼び出そうとします。

モデルを必要としないアクション

createのようなポリシーメソッドは、モデルインスタンスを必要としない場合があります。このような状況では、canメソッドにクラス名を渡すことができます。クラス名は、アクションを認可する際に使用するポリシーを決定するために使用されます。

<?php

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use App\Models\Post;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;

class PostController extends Controller
{
    /**
     * 投稿を作成します。
     */
    public function store(Request $request): RedirectResponse
    {
        if ($request->user()->cannot('create', Post::class)) {
            abort(403);
        }

        // 投稿を作成...

        return redirect('/posts');
    }
}

Gateファサード経由

App\Models\Userモデルに提供される便利なメソッドに加えて、Gateファサードのauthorizeメソッドを介してアクションを認可することもできます。

canメソッドと同様に、このメソッドは認可したいアクションの名前と関連するモデルを受け取ります。アクションが認可されていない場合、authorizeメソッドはIlluminate\Auth\Access\AuthorizationException例外をスローします。この例外は、Laravelの例外ハンドラによって自動的に403ステータスコードのHTTPレスポンスに変換されます。

<?php

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use App\Models\Post;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;

class PostController extends Controller
{
    /**
     * 指定されたブログ投稿を更新します。
     *
     * @throws \Illuminate\Auth\Access\AuthorizationException
     */
    public function update(Request $request, Post $post): RedirectResponse
    {
        Gate::authorize('update', $post);

        // 現在のユーザーはブログ投稿を更新できます...

        return redirect('/posts');
    }
}

モデルを必要としないアクション

前述のように、createのようなポリシーメソッドはモデルインスタンスを必要としない場合があります。このような状況では、クラス名をauthorizeメソッドに渡す必要があります。クラス名は、アクションを認可する際に使用するポリシーを決定するために使用されます。

use App\Models\Post;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;

/**
 * 新しいブログ投稿を作成します。
 *
 * @throws \Illuminate\Auth\Access\AuthorizationException
 */
public function create(Request $request): RedirectResponse
{
    Gate::authorize('create', Post::class);

    // 現在のユーザーはブログ投稿を作成できます...

    return redirect('/posts');
}

ミドルウェア経由

Laravelには、受信リクエストがルートやコントローラに到達する前にアクションを認可できるミドルウェアが含まれています。デフォルトでは、Illuminate\Auth\Middleware\Authorizeミドルウェアは、canミドルウェアエイリアスを使用してルートにアタッチできます。これは、Laravelによって自動的に登録されます。ユーザーが投稿を更新できるかどうかを認可するためにcanミドルウェアを使用する例を見てみましょう。

use App\Models\Post;

Route::put('/post/{post}', function (Post $post) {
    // 現在のユーザーは投稿を更新できます...
})->middleware('can:update,post');

この例では、canミドルウェアに2つの引数を渡しています。最初の引数は認可したいアクションの名前で、2番目の引数はルートパラメータで、ポリシーメソッドに渡したいものです。この場合、暗黙のモデルバインディングを使用しているため、App\Models\Postモデルがポリシーメソッドに渡されます。ユーザーが指定されたアクションを実行する権限がない場合、ミドルウェアによって403ステータスコードのHTTPレスポンスが返されます。

便宜上、canメソッドを使用してルートにcanミドルウェアをアタッチすることもできます。

use App\Models\Post;

Route::put('/post/{post}', function (Post $post) {
    // 現在のユーザーは投稿を更新できます...
})->can('update', 'post');

モデルを必要としないアクション

繰り返しになりますが、createのようなポリシーメソッドはモデルインスタンスを必要としない場合があります。このような状況では、ミドルウェアにクラス名を渡すことができます。クラス名は、アクションを認可する際に使用するポリシーを決定するために使用されます。

Route::post('/post', function () {
    // 現在のユーザーは投稿を作成できます...
})->middleware('can:create,App\Models\Post');

文字列のミドルウェア定義内で完全なクラス名を指定することは、扱いにくい場合があります。そのため、canメソッドを使用してルートにcanミドルウェアをアタッチすることを選択できます。

use App\Models\Post;

Route::post('/post', function () {
    // 現在のユーザーは投稿を作成できます...
})->can('create', Post::class);

Bladeテンプレート経由

Bladeテンプレートを書いているとき、特定のアクションを実行する権限がある場合にのみページの一部を表示したい場合があります。例えば、ユーザーが実際に投稿を更新できる場合にのみ、ブログ投稿の更新フォームを表示したい場合があります。このような状況では、@can@cannotディレクティブを使用できます。

@can('update', $post)
    <!-- 現在のユーザーは投稿を更新できます... -->
@elsecan('create', App\Models\Post::class)
    <!-- 現在のユーザーは新しい投稿を作成できます... -->
@else
    <!-- ... -->
@endcan

@cannot('update', $post)
    <!-- 現在のユーザーは投稿を更新できません... -->
@elsecannot('create', App\Models\Post::class)
    <!-- 現在のユーザーは新しい投稿を作成できません... -->
@endcannot

これらのディレクティブは、@if@unlessステートメントを書くための便利なショートカットです。上記の@can@cannotステートメントは、以下のステートメントと同等です。

@if (Auth::user()->can('update', $post))
    <!-- 現在のユーザーは投稿を更新できます... -->
@endif

@unless (Auth::user()->can('update', $post))
    <!-- 現在のユーザーは投稿を更新できません... -->
@endunless

また、ユーザーが指定されたアクションの配列から任意のアクションを実行する権限があるかどうかを判定することもできます。これを実現するには、@cananyディレクティブを使用します。

@canany(['update', 'view', 'delete'], $post)
    <!-- 現在のユーザーは投稿を更新、表示、または削除できます... -->
@elsecanany(['create'], \App\Models\Post::class)
    <!-- 現在のユーザーは新しい投稿を作成できます... -->
@endcanany
@canany(['update', 'view', 'delete'], $post)
    <!-- 現在のユーザーは投稿を更新、閲覧、または削除できます... -->
@elsecanany(['create'], \App\Models\Post::class)
    <!-- 現在のユーザーは投稿を作成できます... -->
@endcanany

モデルを必要としないアクション

他のほとんどの認可メソッドと同様に、アクションがモデルインスタンスを必要としない場合は、@can および @cannot ディレクティブにクラス名を渡すことができます。

@can('create', App\Models\Post::class)
    <!-- 現在のユーザーは投稿を作成できます... -->
@endcan

@cannot('create', App\Models\Post::class)
    <!-- 現在のユーザーは投稿を作成できません... -->
@endcannot

追加のコンテキストを提供する

ポリシーを使用してアクションを認可する場合、さまざまな認可関数やヘルパーに2番目の引数として配列を渡すことができます。配列の最初の要素は呼び出すべきポリシーを決定するために使用され、残りの配列要素はポリシーメソッドにパラメーターとして渡され、認可決定を行う際の追加のコンテキストとして使用できます。例えば、以下の PostPolicy メソッド定義には追加の $category パラメータが含まれています。

/**
 * 指定された投稿をユーザーが更新できるかどうかを判定します。
 */
public function update(User $user, Post $post, int $category): bool
{
    return $user->id === $post->user_id &&
           $user->canUpdateCategory($category);
}

認証されたユーザーが特定の投稿を更新できるかどうかを判断しようとする場合、このポリシーメソッドを次のように呼び出すことができます。

/**
 * 指定されたブログ投稿を更新します。
 *
 * @throws \Illuminate\Auth\Access\AuthorizationException
 */
public function update(Request $request, Post $post): RedirectResponse
{
    Gate::authorize('update', [$post, $request->category]);

    // 現在のユーザーはブログ投稿を更新できます...

    return redirect('/posts');
}

認可とInertia

認可は常にサーバー側で処理される必要がありますが、アプリケーションのUIを適切にレンダリングするためにフロントエンドアプリケーションに認可データを提供することが便利な場合がよくあります。Laravelは、Inertiaを使用したフロントエンドに認可情報を公開するための必須の規約を定義していません。

ただし、LaravelのInertiaベースのスターターキットの1つを使用している場合、アプリケーションにはすでに HandleInertiaRequests ミドルウェアが含まれています。このミドルウェアの share メソッド内で、アプリケーション内のすべてのInertiaページに提供される共有データを返すことができます。この共有データは、ユーザーの認可情報を定義するための便利な場所として機能します。

<?php

namespace App\Http\Middleware;

use App\Models\Post;
use Illuminate\Http\Request;
use Inertia\Middleware;

class HandleInertiaRequests extends Middleware
{
    // ...

    /**
     * デフォルトで共有されるプロパティを定義します。
     *
     * @return array<string, mixed>
     */
    public function share(Request $request)
    {
        return [
            ...parent::share($request),
            'auth' => [
                'user' => $request->user(),
                'permissions' => [
                    'post' => [
                        'create' => $request->user()->can('create', Post::class),
                    ],
                ],
            ],
        ];
    }
}

ユーザーノート