【Laravel6.x】ポリシークラスの手順

## 問題
ポリシークラス(認証処理)を使って
「権限のないコンテンツ」として403エラーを返したい！

## 手順
⓪ルートモデルバインディング
①ポリシークラスを作成
②ポリシークラスの中身を編集
③App/Providers/AuthServiceProviderを修正
④web.phpでミドルウェアを使用

### ⓪ルートモデルバインディング
```index.blade.php
<a href="{{ route('spending.show', ['id' => $spending['id']]) }}" class="btn btn-info text-white btn-responsive">詳細</a>
↓
↓ // view(index.blade.php)とルーティングのパラメータをモデル名(spending)を合わせる
↓ // リソースコントローラーならターミナルで「php artisan route:list」を叩き、「Name」に記載されている
↓
<a href="{{ route('spending.show', ['spending' => $spending['id']]) }}" class="btn btn-info text-white btn-responsive">詳細</a>
```

```SpendingController.php
// public function show(int $id)
// ↓ ⭐️修正
public function show(Spending $spending)
{
    // ⭐️ルートモデルバインディングのため削除
    // user所持か否かはポリシークラスで判別
    // $spending = Auth::user()->spending()->find($id);

    // ⭐️ルートモデルバインディングのため削除
    // URLでユーザーが所持しないidを入力された時に表示
    // if(is_null($spending)) {
    //    abort(404);
    // }

    return view('original.spending.show', [
        'spending' => $spending,
    ]);
}

public function edit(Spending $spending)
{
    // $spending = Auth::user()->spending()->find($id);

    // その他処理は省略
}

public function update(CreateData $request, Spending $spending)
{
    // $record = Auth::user()->spending()->find($id);

    // その他処理は省略
}

public function destroy(Spending $spending)
{
    // $record = Auth::user()->spending()->find($id);

    // その他処理は省略
}
```
【解説】
ルートモデルバインディング = ルーティング時点でインスタンスを取得しメソッドに渡す処理
ルーティングにID値を渡すと、find($id)の結果を渡してくれる
↓
要するに
```
public function show(int $id)
{
    $spending = Auth::user()->spending()->find($id);
```
上記コードのようにshow関数に届いたidをfind()メソッドを使って該当情報を探していた処理を
```index.blade.php
<a href="{{ route('spending.show', ['spending' => $spending['id']]) }}" class="btn btn-info text-white btn-responsive">詳細</a>
```
```web.php
Route::resource('/spending', 'SpendingController');
(リソースコントローラーなので変更なし)
(リソースコントローラーならターミナルで「php artisan route:list」を叩き、「Name」に記載されているバインディングを使う)
```
```SpendingController.php
public function show(Spending $spending)
{
```
上記のaタグ→ルーティング→コントローラーの該当箇所を修正すれば
ルーティング時点でfind($id)やってくれるようになっているということ

※注意
現在のユーザー情報は取得していないため
ログインユーザー以外の情報も取得できてしまう。
例えばURLに直接他社ユーザーの情報を入力すると取得できてしまう。

そのためポリシークラスで
```
$spending = Auth::user()->spending()->find($id);
```
の、Auth::user()->spending()の部分のような認証処理をつけていく



### ①ポリシークラスを作成
ポリシークラス = 権限認可の処理を管理する
```
ターミナル

php artisan make:policy SpendingPolicy --model=Spending
```
「App/Policies」ディレクトリが生成されて、ファイルも生成される

### ②ポリシークラスの中身を編集
```App/Policies/SpendingPolicy.php
public function view(User $user, Spending $spending)
{
    return $user->id === $spending->user_id;
}
```
【解説】
ファイル内にはデフォルトで複数の関数がある
閲覧や更新などどのように認可をするか細かく設定ができる
上記はviewにまとめて設定してある

view関数の引数には
・第一引数でUserのインスタンス
・第二引数で対象とするモデル(Spending)のインスタンス
  
戻り値はbool型
true→許可、false→拒否

### ③App/Providers/AuthServiceProviderを修正
```App/Providers/AuthServiceProvider.php
protected $policies = [
    // 'App\Model' => 'App\Policies\ModelPolicy',
    'App\Spending' => 'App\Policies\SpendingPolicy',
];
```
【解説】
$policies内に「モデル=>紐づけるポリシー」で設定
これによって「Spendingモデルに関する処理はSpendingPolicyを通す」ということになる

### ④web.phpでミドルウェアを使用
```web.php
  //支出ルート以外は省略

Route::group(['middleware' => 'auth'], function() {
  // 支出登録
  // ⭐️create、storeは、引数でidを必要としないためUser->とSpending->idの一致を確認するポリシークラスで弾かれてしまう
  // そのため「create」「store」のみ外側&先に処理する 
  // ポリシークラスではexceptをしっかり記述
  Route::resource('/spending', 'SpendingController', ['only' => ['create', 'store']]);

  // ⭐️ポリシークラス
  Route::group(['middleware' => 'can:view,spending'], function() {
    // 支出登録、支出詳細、支出削除、支出編集
    Route::resource('/spending', 'SpendingController', ['except' => ['create', 'store']]);
  });
});
```
【解説　⭐️ポリシークラス】
canミドルウェア = 引数の認可処理を見て、trueを返せば処理を進め、falseなら403(権限)エラーを返す
view = 今回使用するポリシーの関数
spending = バインディングしているルートのパラメータ
↓
パラメータからモデル(Spending)を判断し、使用するポリシーの関数(view)を参照している