Laravel プログラミング

【Laravel】Policyで特定のユーザーのみ閲覧できるアクセス制限領域を作る

Laravelのbladeファイル上で特定のユーザーだけが閲覧できる要素を作るためによく使われているのが、以下のようなユーザーデータによって条件分岐する方法です。

@if ($user->flag == true)
  <div>投稿者だけ見れる</div>
@end
PHP

この方法でもいいのですが、bladeファイル内で条件分岐があまりにも多くなってくるとメンテナンス性が悪くなってきます。

そこでおすすめなのが、Laravel標準機能の「Policy」です。

Policyを作ることで、上記のような条件分岐と同じアクセス制限を設けられる上にbladeファイルがスッキリし、メンテナンス性が増します。

今回はこのPolicyについて解説します。

解説

Policyには以下のような特徴があります。

  • モデルやリソースに特有の認可ロジックを表現するためのもの
  • 通常、特定のモデルに関連する複数の認可ロジック(例:view, create, update, deleteなど)をまとめるために使用される
  • artisanコマンドを使用して生成され、AuthServiceProviderに登録される
  • それぞれのメソッドは通常、特定のアクションに対する認可を判断するためのロジックを持っている

と書きましたが、簡単にまとめると、特定のモデルのデータに対して操作する権限を変化させるメソッドを設けることができるというものです。

例えば、ブログ記事を投稿したユーザーのみが閲覧できるとします。

この場合、if文で書くとなると似たような箇所にif文を書いて、変数を渡して、全体的にぱっと見分かりずらい状態となります。

これをPolicyで表現すると以下のようになります。

@can('viewOwnPost', $post)
  <div>投稿者だけ見れる</div>
@endcan
PHP

@canはPolicyのディレクティブですが、上記の方がif文と比べて何をしているのかがメソッド名(viewOwnPost)から一目瞭然かと思います。

では、上記の例を使いながら実装する方法を順に説明していきます。

ブログ記事を投稿する用のPostモデルがある前提で進めます。

まず、PostPolicyを以下のコマンドを実行することで作成します。

php artisan make:policy --model PostPolicy
ShellScript

app/Policies/PostPolicy.phpが生成されるので、これに以下のように投稿者のみが閲覧できるメソッドを追加します。

[app/Policies/PostPolicy.php]

<?php

namespace App\Policies;

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

class PostPolicy
{
    /**
     * ログイン中のUser自身が投稿した記事のみ表示
     *
     * @return bool
     */
    public function viewOwnPost(User $user, Post $post)
    {
        return $user->id == $post->user->id;
    }
}
PHP

実際に使う際に改めて見て頂きたいのですが、$user部分にはログインユーザーのモデルが入ります。

これをメソッド内で囲うことで開発時にメソッド名だけみて判断でき、余計な条件部分を見る必要がなくなります。

このPostPolicyを使えるようにするためには、AuthServiceProviderに登録する必要がありますので、以下のように$policiesに追加します。

[app/Providers/AuthServiceProvider.php]

class AuthServiceProvider extends ServiceProvider
{
    protected $policies = [
        Post::class => PostPolicy::class,
    ];
}
PHP

これで最低限使うのに必要な準備が整いましたので、実際にview上で使えるようにします。

controllerでブログ記事にアクセスできるルーティング先showを以下のように書きます。

これでブログ記事にアクセスすると、showメソッドが実行されるようになり、resources/views/user/post/show.blade.phpが呼び出されます。

$postはアクセスする記事のモデルが入るようにします。

[app/Http/Controllers/User/PostController.php]

<?php

namespace App\Http\Controllers\User;

use App\Models\Post;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Illuminate\View\View;

class PostController extends Controller
{
    public function show(Request $request): View
    {
        $post = Post::find($request->id);

        return view('user.post.show', [
            'post' => $post
        ]);
    }
}
PHP

最後に画面に表示するbladeファイルの中で以下のように@canディレクティブで投稿者だけが閲覧可能になる領域を設けることで、投稿者のみ閲覧でき、それ以外のユーザーは閲覧できないアクセス制限を設けることができました。

[resources/views/user/post/show.blade.php]

...
@can('viewOwnPost', $post)
  <div>投稿者だけ見れる</div>
@endcan
...
HTML

今回の例では、bladeファイル内で使いましたが、bladeファイルだけでなく、他のビジネスロジックのところでも使えるので非常に便利です。

  • この記事を書いた人

タツヤ

フリーのweb系エンジニアです。 プログラミング系やShopifyなどについて発信しています。

-Laravel, プログラミング