Laravelで認可(Policy)

# 目次
Laravelの記事一覧は下記
[PHPフレームワークLaravelの使い方](https://qiita.com/toontoon/items/c4d0371e504c37f6576e)

# Laravelバージョン
動作確認はLaravel Framework 7.19.1で行っています

# 認可
簡単に言うと
認証はアクセスしてきているユーザーが誰なのかを認識するもの
認可はアクセスしてきたユーザーに操作を許可するかどうかを判定するもの
です

# Policyを使った認可
簡単に言うと
EloquentのModelに紐づく真偽値を返す関数(Policy)を作っておき、それを簡単に呼び出せるようにしておくようなものです

# 前提条件
[eclipseでLaravel開発環境を構築する。デバッグでブレークポイントをつけて止める。(WindowsもVagrantもdockerも)](https://qiita.com/toontoon/items/76fa7f8845b20776077c)
本記事は上記が完了している前提で書かれています
プロジェクトの作成もapacheの設定も上記で行っています

[Laravelで認可(Gate)](https://qiita.com/toontoon/items/f6206760d62ee2e66c5f)
本記事は上記が完了している前提で書かれています
今回使うカラムも上記記事で作成済みです

# Policyの作成
今回は下記記事で作成したTable2というModelを使います
[Laravelでデータベースを操作する(Eloquent編)](https://qiita.com/toontoon/items/97a49d1ea94e30bb8b48)

コマンドラインで
`cd sample`
`php artisan make:policy Table2Policy --model=Models\Table2`
xdebugの設定をしているとeclipseが実行していいですかというプロンプトを出すのでOKを押します
eclipseプロジェクトを右クリック→リフレッシュ
/sample/app/Policies/Table2Policy.phpが現れます

/sample/app/Policies/Table2Policy.php修正

```Table2Policy.php
‥‥
    public function create(User $user)
    {
        return $user->rank >= 2;
    }

    public function update(User $user, Table2 $table2)
    {
        return $user->rank >= $table2->int_col;
    }
‥‥
```
createメソッドとupdateメソッドを修正しました
Policyのメソッドの第一引数は必ず、ユーザーインスタンスになります

下記7メソッドが自動的にできあがっていると思います
viewAny
view
create
update
delete
restore
forceDelete
これらはコントローラーをつくるときに`php artisan make:controller XXXController --resource`のように`--resource`オプションを付けて作成したリソースコントローラーの各メソッドに対応しています

|コントローラメソッド|ポリシーメソッド    |
|:------------------:|:------------------:|
|index|viewAny|
|show|view|
|create|create|
|store|create|
|edit|update|
|update|update|
|destroy|delete|

コントローラーのindexメソッドではPolicyのviewAnyメソッドを使ってください
コントローラーのshowメソッドではPolicyのviewメソッドを使ってください
ということです

# Policyの登録
/sample/app/Providers/AuthServiceProvider.php修正

```AuthServiceProvider.php
‥‥
    protected $policies = [
        'App\Models\Table2' => 'App\Policies\Table2Policy',
    ];
‥‥
```
`$policies`配列に要素を追記しました
keyがModel、valueがPolicyになります
これでLaravelアプリケーションの中でPolicyを簡単に呼び出せるようになります

# Controllerにメソッド追加
(1) use文追記
`use Illuminate\Support\Facades\Auth;`
`use App\Models\User;`
`use App\Models\Table2;`

(2) /sample/app/Http/Controllers/SampleController.phpにgate1メソッド、gate2メソッドを追記

```
    public function policy1()
    {
        return view('sample.policy1');
    }

    public function policy2(Request $request)
    {
        $int_col = $request->input('int_col');

        $user = Auth::user();

        $table2 = new Table2();
        $table2->int_col = $int_col;

        if (is_null($user) || !$user->can('create', Table2::class)) {
            return redirect('sample/policy1')->withInput();
        }
        if (is_null($user) || !$user->can('update', $table2)) {
            return redirect('sample/policy1')->withInput();
        }

        $data = ['table2' => $table2];

        return view('sample.policy2', $data);

    }
```

Policyは`$user->can`で呼べます
第一引数に与えた文字列はPolicyに定義したメソッド名です
第二引数はPolicyに定義したメソッドの第二引数になります
第二引数を定義していないPolicyの場合第二引数にはクラス名を渡します
Policyの第一引数は`$user`になります
`$user->can`はPolicyが返す真偽値がそのまま戻り値になります

(2) /sample/routes/web.phpに下記を追記
`Route::get('sample/policy1', 'SampleController@policy1');`
`Route::get('sample/policy2', 'SampleController@policy2');`

# viewの作成
(1) /sample/resources/views/sample/policy1.blade.php

```policy1.blade.php
<html>
    <head>
        <title>sample</title>
    </head>
    <body>

        <form action="{{ url('sample/policy2') }}" method="get">
            <div>int_col<input type="text" name="int_col" value="{{ old('int_col') }}"></div>
            <input type="submit" >
        </form>

    </body>
</html>
```

(2) /sample/resources/views/sample/policy2.blade.php

```policy2.blade.php
<html>
    <head>
        <title>sample</title>
    </head>
    <body>

        <div>
        @can('create', App\Models\Table2::class)
            can create
        @elsecan('update', $table2)
            can update
        @else
            else
        @endcan
        </div>

        <div>
        @cannot('create', App\Models\Table2::class)
            cannot create
        @elsecannot('update', $table2)
            cannot update
        @else
            else
        @endcannot
        </div>

    </body>
</html>
```
blade内でPolicyを使うにはcanやcannotで呼び出せます
`@can`や`@cannot`を使うことで、更新ボタンを特定のユーザーにだけ表示させたり、または、逆に非表示にしてりできます

# 動作確認
usersテーブルのrankカラムの値を適当な値にupdateします

```
update users set rank = 4;
```

ログインします
`http://localhost/laravelSample/`
右上のLOGINからログインします

`http://localhost/laravelSample/sample/policy1`
rankを4にupdateしてので
5以上の値を入力して送信ボタンをクリックするとpolicy1にリダイレクトされ先に進めないと思います
4以下の値を入力して送信ボタンをクリックするとpolicy2に遷移できます
今回はサンプルのため入力値のエラーチェックを一切行っていないので、gate1の入力欄には数値を入れてください。数値でない場合、制御されていない型エラーになります
実際にシステムを開発するときはちゃんとエラーチェックを入れましょう
エラーチェックの実装の仕方は下記に書いてあります
[Laravelで入力値エラーチェック(validate)を実装する](https://qiita.com/toontoon/items/abf5c52eb0c3fe11003c)