はじめに

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

Laravelは、アクションを承認するための2つの主要な方法を提供します:ゲートポリシー。ゲートとポリシーは、ルートとコントローラーのようなものと考えてください。ゲートは、承認に対するシンプルでクロージャベースのアプローチを提供し、ポリシーはコントローラーのように、特定のモデルやリソースに関連するロジックをグループ化します。このドキュメントでは、まずゲートを探求し、その後ポリシーを検討します。

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

ゲート

ゲートの作成

ゲートは、Laravelの承認機能の基本を学ぶための素晴らしい方法ですが、堅牢なLaravelアプリケーションを構築する際には、承認ルールを整理するためにポリシーを使用することを検討すべきです。

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

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

  1. use App\Models\Post;
  2. use App\Models\User;
  3. use Illuminate\Support\Facades\Gate;
  4. /**
  5. * Bootstrap any application services.
  6. */
  7. public function boot(): void
  8. {
  9. Gate::define('update-post', function (User $user, Post $post) {
  10. return $user->id === $post->user_id;
  11. });
  12. }

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

  1. use App\Policies\PostPolicy;
  2. use Illuminate\Support\Facades\Gate;
  3. /**
  4. * Bootstrap any application services.
  5. */
  6. public function boot(): void
  7. {
  8. Gate::define('update-post', [PostPolicy::class, 'update']);
  9. }

アクションの承認

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

  1. <?php
  2. namespace App\Http\Controllers;
  3. use App\Http\Controllers\Controller;
  4. use App\Models\Post;
  5. use Illuminate\Http\RedirectResponse;
  6. use Illuminate\Http\Request;
  7. use Illuminate\Support\Facades\Gate;
  8. class PostController extends Controller
  9. {
  10. /**
  11. * Update the given post.
  12. */
  13. public function update(Request $request, Post $post): RedirectResponse
  14. {
  15. if (! Gate::allows('update-post', $post)) {
  16. abort(403);
  17. }
  18. // Update the post...
  19. return redirect('/posts');
  20. }
  21. }

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

  1. if (Gate::forUser($user)->allows('update-post', $post)) {
  2. // The user can update the post...
  3. }
  4. if (Gate::forUser($user)->denies('update-post', $post)) {
  5. // The user can't update the post...
  6. }
  1. ``````php
  2. if (Gate::any(['update-post', 'delete-post'], $post)) {
  3. // The user can update or delete the post...
  4. }
  5. if (Gate::none(['update-post', 'delete-post'], $post)) {
  6. // The user can't update or delete the post...
  7. }
  8. `

承認または例外のスロー

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

  1. Gate::authorize('update-post', $post);
  2. // The action is authorized...

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

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

  1. use App\Models\Category;
  2. use App\Models\User;
  3. use Illuminate\Support\Facades\Gate;
  4. Gate::define('create-post', function (User $user, Category $category, bool $pinned) {
  5. if (! $user->canPublishToGroup($category->group)) {
  6. return false;
  7. } elseif ($pinned && ! $user->canPinPosts()) {
  8. return false;
  9. }
  10. return true;
  11. });
  12. if (Gate::check('create-post', [$category, $pinned])) {
  13. // The user can create the post...
  14. }

ゲートのレスポンス

これまで、単純なブール値を返すゲートのみを検討してきました。しかし、時にはエラーメッセージを含むより詳細なレスポンスを返したい場合があります。そのためには、ゲートからIlluminate\Auth\Access\Responseを返すことができます:

  1. use App\Models\User;
  2. use Illuminate\Auth\Access\Response;
  3. use Illuminate\Support\Facades\Gate;
  4. Gate::define('edit-settings', function (User $user) {
  5. return $user->isAdmin
  6. ? Response::allow()
  7. : Response::deny('You must be an administrator.');
  8. });

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

  1. $response = Gate::inspect('edit-settings');
  2. if ($response->allowed()) {
  3. // The action is authorized...
  4. } else {
  5. echo $response->message();
  6. }

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

  1. Gate::authorize('edit-settings');
  2. // The action is authorized...

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

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

  1. use App\Models\User;
  2. use Illuminate\Auth\Access\Response;
  3. use Illuminate\Support\Facades\Gate;
  4. Gate::define('edit-settings', function (User $user) {
  5. return $user->isAdmin
  6. ? Response::allow()
  7. : Response::denyWithStatus(404);
  8. });

リソースを404レスポンスで隠すことは、Webアプリケーションにとって一般的なパターンであるため、denyAsNotFoundメソッドが便利に提供されています:

  1. use App\Models\User;
  2. use Illuminate\Auth\Access\Response;
  3. use Illuminate\Support\Facades\Gate;
  4. Gate::define('edit-settings', function (User $user) {
  5. return $user->isAdmin
  6. ? Response::allow()
  7. : Response::denyAsNotFound();
  8. });

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

特定のユーザーにすべての能力を付与したい場合があります。beforeメソッドを使用して、すべての他の承認チェックの前に実行されるクロージャを定義できます:

  1. use App\Models\User;
  2. use Illuminate\Support\Facades\Gate;
  3. Gate::before(function (User $user, string $ability) {
  4. if ($user->isAdministrator()) {
  5. return true;
  6. }
  7. });
  1. `````after`````メソッドを使用して、すべての他の承認チェックの後に実行されるクロージャを定義できます:
  2. ``````php
  3. use App\Models\User;
  4. Gate::after(function (User $user, string $ability, bool|null $result, mixed $arguments) {
  5. if ($user->isAdministrator()) {
  6. return true;
  7. }
  8. });
  9. `
  1. <a name="inline-authorization"></a>
  2. ### 認証
  3. - [はじめに](#introduction)
  4. - [ゲート](#gates)
  5. - [ゲートの作成](#writing-gates)
  6. - [アクションの認可](#authorizing-actions-via-gates)
  7. - [ゲートの応答](#gate-responses)
  8. - [ゲートチェックのインターセプト](#intercepting-gate-checks)
  9. - [インライン認可](#inline-authorization)
  10. - [ポリシーの作成](#creating-policies)
  11. - [ポリシーの生成](#generating-policies)
  12. - [ポリシーの登録](#registering-policies)
  13. - [ポリシーの作成](#writing-policies)
  14. - [ポリシーメソッド](#policy-methods)
  15. - [ポリシーの応答](#policy-responses)
  16. - [モデルなしのメソッド](#methods-without-models)
  17. - [ゲストユーザー](#guest-users)
  18. - [ポリシーフィルター](#policy-filters)
  19. - [ポリシーを使用したアクションの認可](#authorizing-actions-using-policies)
  20. - [ユーザーモデルを介して](#via-the-user-model)
  21. - [ゲートファサードを介して](#via-the-gate-facade)
  22. - [ミドルウェアを介して](#via-middleware)
  23. - [Bladeテンプレートを介して](#via-blade-templates)
  24. - [追加のコンテキストを提供する](#supplying-additional-context)
  25. - [認可とイナーシャ](#authorization-and-inertia)
  26. <a name="introduction"></a>
  27. ## ポリシーの作成
  28. <a name="generating-policies"></a>
  29. ### ポリシーの生成
  30. ポリシーは、特定のモデルまたはリソースに関する認可ロジックを整理するクラスです。たとえば、アプリケーションがブログである場合、`````App\Models\Post`````モデルと、投稿の作成や更新などのユーザーアクションを認可するための対応する`````App\Policies\PostPolicy`````があるかもしれません。
  31. ポリシーは、`````make:policy````` Artisanコマンドを使用して生成できます。生成されたポリシーは、`````app/Policies`````ディレクトリに配置されます。このディレクトリがアプリケーションに存在しない場合、Laravelは自動的に作成します:
  32. ``````shell
  33. php artisan make:policy PostPolicy
  34. `
  1. ``````shell
  2. php artisan make:policy PostPolicy --model=Post
  3. `

ポリシーの登録

ポリシーの発見

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

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

  1. use Illuminate\Support\Facades\Gate;
  2. Gate::guessPolicyNamesUsing(function (string $modelClass) {
  3. // Return the name of the policy class for the given model...
  4. });

ポリシーの手動登録

  1. ``````php
  2. use App\Models\Order;
  3. use App\Policies\OrderPolicy;
  4. use Illuminate\Support\Facades\Gate;
  5. /**
  6. * Bootstrap any application services.
  7. */
  8. public function boot(): void
  9. {
  10. Gate::policy(Order::class, OrderPolicy::class);
  11. }
  12. `

ポリシーの作成

ポリシーメソッド

ポリシークラスが登録されると、そのポリシーが承認する各アクションのためのメソッドを追加できます。たとえば、updateメソッドをPostPolicyに定義し、特定のApp\Models\Userが特定のApp\Models\Postインスタンスを更新できるかどうかを判断します。

  1. ``````php
  2. <?php
  3. namespace App\Policies;
  4. use App\Models\Post;
  5. use App\Models\User;
  6. class PostPolicy
  7. {
  8. /**
  9. * Determine if the given post can be updated by the user.
  10. */
  11. public function update(User $user, Post $post): bool
  12. {
  13. return $user->id === $post->user_id;
  14. }
  15. }
  16. `

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

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

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

ポリシーレスポンス

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

  1. use App\Models\Post;
  2. use App\Models\User;
  3. use Illuminate\Auth\Access\Response;
  4. /**
  5. * Determine if the given post can be updated by the user.
  6. */
  7. public function update(User $user, Post $post): Response
  8. {
  9. return $user->id === $post->user_id
  10. ? Response::allow()
  11. : Response::deny('You do not own this post.');
  12. }

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

  1. use Illuminate\Support\Facades\Gate;
  2. $response = Gate::inspect('update', $post);
  3. if ($response->allowed()) {
  4. // The action is authorized...
  5. } else {
  6. echo $response->message();
  7. }

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

  1. Gate::authorize('update', $post);
  2. // The action is authorized...

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

ポリシーメソッドによってアクションが拒否された場合、403 HTTPレスポンスが返されます。ただし、失敗した承認チェックに対して代替のHTTPステータスコードを返すことが有用な場合があります。denyWithStatus静的コンストラクタを使用して、失敗した承認チェックに対して返されるHTTPステータスコードをカスタマイズできます:

  1. use App\Models\Post;
  2. use App\Models\User;
  3. use Illuminate\Auth\Access\Response;
  4. /**
  5. * Determine if the given post can be updated by the user.
  6. */
  7. public function update(User $user, Post $post): Response
  8. {
  9. return $user->id === $post->user_id
  10. ? Response::allow()
  11. : Response::denyWithStatus(404);
  12. }

リソースを404レスポンスで隠すことは、Webアプリケーションにとって一般的なパターンであるため、denyAsNotFoundメソッドが便利に提供されています:

  1. use App\Models\Post;
  2. use App\Models\User;
  3. use Illuminate\Auth\Access\Response;
  4. /**
  5. * Determine if the given post can be updated by the user.
  6. */
  7. public function update(User $user, Post $post): Response
  8. {
  9. return $user->id === $post->user_id
  10. ? Response::allow()
  11. : Response::denyAsNotFound();
  12. }

モデルなしのメソッド

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

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

ゲストユーザー

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

  1. <?php
  2. namespace App\Policies;
  3. use App\Models\Post;
  4. use App\Models\User;
  5. class PostPolicy
  6. {
  7. /**
  8. * Determine if the given post can be updated by the user.
  9. */
  10. public function update(?User $user, Post $post): bool
  11. {
  12. return $user?->id === $post->user_id;
  13. }
  14. }

ポリシーフィルター

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

  1. use App\Models\User;
  2. /**
  3. * Perform pre-authorization checks.
  4. */
  5. public function before(User $user, string $ability): bool|null
  6. {
  7. if ($user->isAdministrator()) {
  8. return true;
  9. }
  10. return null;
  11. }

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

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

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

ユーザーモデル経由

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

  1. <?php
  2. namespace App\Http\Controllers;
  3. use App\Http\Controllers\Controller;
  4. use App\Models\Post;
  5. use Illuminate\Http\RedirectResponse;
  6. use Illuminate\Http\Request;
  7. class PostController extends Controller
  8. {
  9. /**
  10. * Update the given post.
  11. */
  12. public function update(Request $request, Post $post): RedirectResponse
  13. {
  14. if ($request->user()->cannot('update', $post)) {
  15. abort(403);
  16. }
  17. // Update the post...
  18. return redirect('/posts');
  19. }
  20. }

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

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

覚えておいてください、一部のアクションは、モデルインスタンスを必要としないポリシーメソッドに対応する場合があります。これらの状況では、canメソッドにクラス名を渡すことができます。クラス名は、アクションを承認する際に使用するポリシーを決定するために使用されます:

  1. <?php
  2. namespace App\Http\Controllers;
  3. use App\Http\Controllers\Controller;
  4. use App\Models\Post;
  5. use Illuminate\Http\RedirectResponse;
  6. use Illuminate\Http\Request;
  7. class PostController extends Controller
  8. {
  9. /**
  10. * Create a post.
  11. */
  12. public function store(Request $request): RedirectResponse
  13. {
  14. if ($request->user()->cannot('create', Post::class)) {
  15. abort(403);
  16. }
  17. // Create the post...
  18. return redirect('/posts');
  19. }
  20. }

ゲートファサード経由

  1. `````can`````メソッドと同様に、このメソッドは、承認したいアクションの名前と関連するモデルを受け取ります。アクションが承認されていない場合、`````authorize`````メソッドは`````Illuminate\Auth\Access\AuthorizationException`````例外をスローし、Laravelの例外ハンドラーは自動的に403ステータスコードのHTTPレスポンスに変換します:
  2. ``````php
  3. <?php
  4. namespace App\Http\Controllers;
  5. use App\Http\Controllers\Controller;
  6. use App\Models\Post;
  7. use Illuminate\Http\RedirectResponse;
  8. use Illuminate\Http\Request;
  9. use Illuminate\Support\Facades\Gate;
  10. class PostController extends Controller
  11. {
  12. /**
  13. * Update the given blog post.
  14. *
  15. * @throws \Illuminate\Auth\Access\AuthorizationException
  16. */
  17. public function update(Request $request, Post $post): RedirectResponse
  18. {
  19. Gate::authorize('update', $post);
  20. // The current user can update the blog post...
  21. return redirect('/posts');
  22. }
  23. }
  24. `

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

前述のように、createのような一部のポリシーメソッドは、モデルインスタンスを必要としません。これらの状況では、authorizeメソッドにクラス名を渡すべきです。クラス名は、アクションを承認する際に使用するポリシーを決定するために使用されます:

  1. use App\Models\Post;
  2. use Illuminate\Http\RedirectResponse;
  3. use Illuminate\Http\Request;
  4. use Illuminate\Support\Facades\Gate;
  5. /**
  6. * Create a new blog post.
  7. *
  8. * @throws \Illuminate\Auth\Access\AuthorizationException
  9. */
  10. public function create(Request $request): RedirectResponse
  11. {
  12. Gate::authorize('create', Post::class);
  13. // The current user can create blog posts...
  14. return redirect('/posts');
  15. }

ミドルウェア経由

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

  1. use App\Models\Post;
  2. Route::put('/post/{post}', function (Post $post) {
  3. // The current user may update the post...
  4. })->middleware('can:update,post');

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

便利なことに、canミドルウェアをcanメソッドを使用してルートに添付することもできます:

  1. use App\Models\Post;
  2. Route::put('/post/{post}', function (Post $post) {
  3. // The current user may update the post...
  4. })->can('update', 'post');

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

再度、createのような一部のポリシーメソッドは、モデルインスタンスを必要としません。これらの状況では、ミドルウェアにクラス名を渡すことができます。クラス名は、アクションを承認する際に使用するポリシーを決定するために使用されます:

  1. Route::post('/post', function () {
  2. // The current user may create posts...
  3. })->middleware('can:create,App\Models\Post');

文字列ミドルウェア定義内で完全なクラス名を指定することは面倒になる可能性があります。そのため、canミドルウェアをcanメソッドを使用してルートに添付することを選択できます:

  1. use App\Models\Post;
  2. Route::post('/post', function () {
  3. // The current user may create posts...
  4. })->can('create', Post::class);

Bladeテンプレート経由

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

  1. @can('update', $post)
  2. <!-- The current user can update the post... -->
  3. @elsecan('create', App\Models\Post::class)
  4. <!-- The current user can create new posts... -->
  5. @else
  6. <!-- ... -->
  7. @endcan
  8. @cannot('update', $post)
  9. <!-- The current user cannot update the post... -->
  10. @elsecannot('create', App\Models\Post::class)
  11. <!-- The current user cannot create new posts... -->
  12. @endcannot

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

  1. @if (Auth::user()->can('update', $post))
  2. <!-- The current user can update the post... -->
  3. @endif
  4. @unless (Auth::user()->can('update', $post))
  5. <!-- The current user cannot update the post... -->
  6. @endunless

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

  1. @canany(['update', 'view', 'delete'], $post)
  2. <!-- The current user can update, view, or delete the post... -->
  3. @elsecanany(['create'], \App\Models\Post::class)
  4. <!-- The current user can create a post... -->
  5. @endcanany

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

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

  1. @can('create', App\Models\Post::class)
  2. <!-- The current user can create posts... -->
  3. @endcan
  4. @cannot('create', App\Models\Post::class)
  5. <!-- The current user can't create posts... -->
  6. @endcannot

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

ポリシーを使用してアクションを承認する際、さまざまな承認関数やヘルパーに第二引数として配列を渡すことができます。配列の最初の要素は、どのポリシーを呼び出すべきかを決定するために使用され、配列の残りの要素はポリシーメソッドにパラメータとして渡され、承認決定を行う際の追加のコンテキストとして使用できます。たとえば、次のPostPolicyメソッド定義を考えてみましょう。これは追加の$categoryパラメータを含んでいます:

  1. /**
  2. * Determine if the given post can be updated by the user.
  3. */
  4. public function update(User $user, Post $post, int $category): bool
  5. {
  6. return $user->id === $post->user_id &&
  7. $user->canUpdateCategory($category);
  8. }

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

  1. /**
  2. * Update the given blog post.
  3. *
  4. * @throws \Illuminate\Auth\Access\AuthorizationException
  5. */
  6. public function update(Request $request, Post $post): RedirectResponse
  7. {
  8. Gate::authorize('update', [$post, $request->category]);
  9. // The current user can update the blog post...
  10. return redirect('/posts');
  11. }

承認とInertia

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

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

  1. <?php
  2. namespace App\Http\Middleware;
  3. use App\Models\Post;
  4. use Illuminate\Http\Request;
  5. use Inertia\Middleware;
  6. class HandleInertiaRequests extends Middleware
  7. {
  8. // ...
  9. /**
  10. * Define the props that are shared by default.
  11. *
  12. * @return array<string, mixed>
  13. */
  14. public function share(Request $request)
  15. {
  16. return [
  17. ...parent::share($request),
  18. 'auth' => [
  19. 'user' => $request->user(),
  20. 'permissions' => [
  21. 'post' => [
  22. 'create' => $request->user()->can('create', Post::class),
  23. ],
  24. ],
  25. ],
  26. ];
  27. }
  28. }