laravel ミドルウェア、認可と認証について、使用例の一つ「ユーザープロフィール」を挙げる

基本的な使い方だが、記録としておく。

##要件：

1.ユーザー自身で自分のプロフィールのみ編集することはできる。
2.ゲストとユーザーが他人のプロフィールを閲覧することのみ、許可される。

###まず、要件②はミドルウェアの追加で満たすことはできる。
ゲストは「.../user/5/edit」にアクセスしたらログインページにリダイレクトする仕組みになっている。



```php
class UsersController extends Controller
{
    //プロフィールを閲覧すること以外の動作は認証済のユーザーのみ行われる
    public function __construct()
    {
        $this->middleware('auth',['except'=>['show']]);
    }

    public function show(User $user)
    {
        return view('users.show',compact('user'));
    }

    public function edit(User $user)
    {
        return view('users.edit',compact('user'));
    }

    public function update(UserRequest $userRequest,ImageUploadHandler $uploader,User $user)
    {
        $data = $userRequest ->all();
        if($userRequest->avatar){
            $result = $uploader->save($userRequest->avatar,'avatars',$user->id,env('UPLOAD_SIZE'));
            if($result){
                $data['avatar'] = $result['path'];
            }
        }
        $user->update($data);
        return redirect()->route('users.show',$user->id)
            ->with('success','プロフィールを更新しました！');
    }
}
```

###次の要件①に、本人以外のプロフィール操作を防ぐために、laravelの[ポリシー](https://readouble.com/laravel/7.x/ja/authorization.html)を使用します。

```
//ポリシーを生成
php artisan make:policy UserPolicy
```


作成されたユーザーポリシーに下記で記述



```php
<?php

namespace App\Policies;

use App\Models\User;
use Illuminate\Auth\Access\HandlesAuthorization;

class UserPolicy
{
    use HandlesAuthorization;

     /**
     * ユーザー本人が更新可能か決める
     * @param User $currentUser
     * @param User $user
     * @return bool
     */
    public function update(User $currentUser,User $user)
    {
        return $currentUser->id === $user->id;
    }
}

```

次に、ポリシーを登録する。Userモデルは「app」配下でない場合、下記でモデルの設定します。

```php
<?php

namespace App\Providers;

use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Gate;

class AuthServiceProvider extends ServiceProvider
{
    /**
     * The policy mappings for the application.
     *
     * @var array
     */
    protected $policies = [
        // 'App\Model' => 'App\Policies\ModelPolicy',
    ];

    /**
     * Register any authentication / authorization services.
     *
     * @return void
     */
    public function boot()
    {
        $this->registerPolicies();

        // 'App\Model\User' => 'App\Policies\UserPolicy',
        Gate::guessPolicyNamesUsing(function ($modelClass){
            return 'App\Policies\\'.class_basename($modelClass).'Policy';

        });
    }
}
```

最後にコントローラヘルパによる認可を設定する

UserControllerに、「edit」「update」メソッドに下記のラインを追加。

```php
$this->authorize('update', $user);

```
アクションが認可されない場合、authorizeメソッドはIlluminate\Auth\Access\AuthorizationException例外を投げ、これはデフォルトでLaravelの例外ハンドラにより、403ステータスコードのHTTPレスポンスへ変換されます。


```php
<?php

namespace App\Http\Controllers;

use App\Handlers\ImageUploadHandler;
use App\Http\Requests\UserRequest;
use App\Models\User;
use Illuminate\Http\Request;

class UsersController extends Controller
{
    public function __construct()
    {
        $this->middleware('auth',['except'=>['show']]);
    }

    public function show(User $user)
    {
        return view('users.show',compact('user'));
    }

    public function edit(User $user)
    {
        $this->authorize('update',$user);
        return view('users.edit',compact('user'));
    }

    public function update(UserRequest $userRequest,ImageUploadHandler $uploader,User $user)
    {
        $this->authorize('update',$user);
        $data = $userRequest ->all();
        if($userRequest->avatar){
            $result = $uploader->save($userRequest->avatar,'avatars',$user->id,env('UPLOAD_SIZE'));
            if($result){
                $data['avatar'] = $result['path'];
            }
        }
        $user->update($data);
        return redirect()->route('users.show',$user->id)
            ->with('success','プロフィールを更新しました！');
    }
}

```