Laravel Middlewareを自作する

LaravelのMiddleware覚書。
今回はviewにアクセス日時を表示するViewDateMiddlewareを作成します。

Middleware作成用のartisanコマンドが用意されているのでそれを使います。
引数にミドルウェア名を渡せばokです。
`php artisan make:middleware ミドルウェア名`

```lang:help
$ php artisan make:middleware --help
Description:
  Create a new middleware class

Usage:
  make:middleware <name>

Arguments:
  name                  The name of the class
```


以下のコマンドで`app/Http/Middleware/ViewDateMiddleware.php`が作成されます。

```lang:ViewDateMiddleware.phpを作成
$ php artisan make:middleware ViewDateMiddleware
Middleware created successfully.
```

自動生成されたMiddlewareにはhandle関数のみ定義されています。
第一引数にRequest, 第二引数にClosureが渡されます。Requestはhttpリクエストでinput値等が取得できます。
Closureを実行することで次のMiddlewareが実行されます。
Controllerロジックより前に処理を入れたい場合は`$next($request)`より前にコードを記述してください。
Controllerロジックの後に処理を入れたい場合は`$next($request)`より後ろにコードを記述してください。

```lang:ViewDateMiddleware.php
<?php

namespace App\Http\Middleware;

use Closure;

class ViewDateMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        return $next($request);
    }
}
```

MiddlewareはApplicationサービスコンテナによってインスタンス化されるため、コントラクタ引数に任意のクラスを指定することでDIされます。
今回はviewにアクセス日時を表示できる変数を渡すのでViewのFactoryを引数に指定します。

```lang:ViewDateMiddleware
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Contracts\View\Factory;

class ViewDateMiddleware
{

    private $view;

    public function __construct(Factory $view)
    {
        $this->view = $view;
    }

　　省略
}

```

handle関数内でFactoryのshare関数を使うことでviewで使える変数を定義できます。

```lang:ViewDateMiddleware
    public function handle($request, Closure $next)
    {
        $this->view->share('access_date', time());
        return $next($request);
    }
```

index.blade.php等のviewファイルで `{{ $access_date }}` で使えます。

```lang:index.blade.php
{{ $access_date }}
```

そしてブラウザで見てみるとエラーがでました。
Undefined variableなのでaccess_date変数の定義に失敗しています。
<img width="1381" alt="スクリーンショット 2020-04-09 1.45.31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278474/d961f849-3ed8-94a4-8e9c-9009b1cd909a.png">

Middlewareはlaravelライフサイクルの中で読み込まれるようkernelやroute, controllerの中で指定する必要があります。
kernelにはMiddlewareを指定する箇所は3箇所あります。
・$middleware
全てのhttp通信で処理されるミドルウェアを追加

・$middlewareGroups
webやapi等の特定routeGroupで使用するミドルウェアを追加

・$routeMiddleware
ミドルウェアのalias名を指定できる。個々のrouteでalias名を使ってmiddlewareを指定できる。

今回は全てのwebリクエストで読み込まれるようにしたいのでkernelのmiddlewareGroupsに追記します。

```lang:App/Http/Kernel.php
    protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            // \Illuminate\Session\Middleware\AuthenticateSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
            \App\Http\Middleware\ViewDateMiddleware::class, //ここに追加
        ],

        'api' => [
            'throttle:60,1',
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],
    ];
```

これでミドエルウェアが実行されてviewでaccess_date変数が使用できるようになりました。
↓結果html
<img width="118" alt="スクリーンショット 2020-04-09 2.26.26.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278474/3d20cc03-cda4-bc75-ee1a-6c1d3d316573.png">


# middlewareに引数を渡す
routeで個別にmiddlewareを指定する際にmiddlewareに引数を渡せます。
先ほどのmiddlewareを改造し、routeごとに時刻フォーマットを指定できるようにします。

まずはkernel.phpの$middlewareGroupsに追記したmiddlewareを$routeMiddlewareに移動させます。
`viewData`aliasでmiddlewareを指定できるようにしました。

```
    protected $routeMiddleware = [
        'auth' => \App\Http\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
        'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
        'can' => \Illuminate\Auth\Middleware\Authorize::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
        'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
        'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
        'ViewDate' => \App\Http\Middleware\ViewDateMiddleware::class, //追加
    ];
```

続いてrouteでmiddlewareを指定します。
`middleware名:引数`のフォーマットで指定することでmiddlewareに引数を渡すことができます。
`viewDate-Ymd`にアクセスしたら`Y/m/d`を、
`viewDate-Ym`にアクセスしたら`Y/m`を引数にするように指定しました。

```web.php
Route::get('/viewDate-Ymd', function() {
    return view('index');
})->middleware('viewDate:Y/m/d');


Route::get('/viewDate-Ym', function () {
    return view('index');
})->middleware('viewDate:Y/m');
```

これで
http://127.0.0.1:8000/viewDate-Ymd
にアクセスすると
<img width="116" alt="スクリーンショット 2020-04-09 2.43.29.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278474/12070540-e803-6a27-8bfe-8bc9027a0d7f.png">

http://127.0.0.1:8000/viewDate-Ym
にアクセスすると
<img width="83" alt="スクリーンショット 2020-04-09 2.43.43.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278474/66a49e8b-fa44-3324-21d1-fa0366c027f1.png">

と表示されるようになりました。