🎃

LaravelのViewに渡した値をMiddlewareで書き換える摩訶不思議なコード

2023/06/09に公開

はじめに

こんなことを知ったところで、何も役には立たないかもしれないですが、
今回、LaravelのViewに渡した値をMiddlewareで書き換えるっていうことをやってみます。

環境

  • PHP 8.2.6
  • Laravel 10.13.5

準備

適当にViewに値を渡す処理とbladeテンプレートを用意しておきます。

routes/web.php
  use Illuminate\Support\Facades\Route;

  Route::get('/', function () {
      return view('user', ['user' => ['name' => '太郎']]);
  });
resources/views/user.blade.php
名前は {{ $user['name'] }} です。

一応、php artisan serveでサーバーを立ち上げて確認です。

実装

Middlewareを作成

まず、適当なMiddlewareをコマンドを使って作成します。

$ php artisan make:middleware ReplaceViewData

   INFO  Middleware [app/Http/Middleware/ReplaceViewData.php] created successfully. 

ルートにMiddlewareを登録

出来上がったら、サンプルコードのroutes/web.phpの内容を次のように変更します。

routes/web.php
  use App\Http\Middleware\ReplaceViewData;
  use Illuminate\Support\Facades\Route;

  Route::get('/', function () {
      return view('user', ['user' => ['name' => '太郎']]);
+ })->middleware(ReplaceViewData::class);
- });

Middlewareの実装

では、ReplaceViewDataMiddlewareの実装です。
何をやっているかは後で書くとして、まずは書いたコードをドン!!

app/Http/Middleware/ReplaceViewData.php
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class ReplaceViewData
{
    /**
     * Handle an incoming request.
     *
     * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
     */
    public function handle(Request $request, Closure $next): Response
    {
        $response = $next($request);

        // 1. \Illuminate\Http\Responseを想定
        if ($original instanceof \Illuminate\Http\Response) {
                $original = $response->original;
        
                // 2. originalプロパティの型はmixedなので、\Illuminate\View\Viewのインスタンスか確認
                if ($original instanceof \Illuminate\View\View) {
                    // 3. Viewに渡した値を取得し、書き換える
                    $data = $original->getData();
                    $user = $data['user'];
                    $user['name'] = '花子';
                    $original->with('user', $user);

                    // 4. レスポンスクラスに渡している値を書き換える
                    $response->setContent($original);
                }
        }
        
        return $response;
    }
}

Middlewareで何をやっているのか

順番に何をやっているのか書いていきます。

1. \Illuminate\Http\Responseを想定

レスポンスは次の3種類のクラスが返ってくると思われます(他にもあるかもですが。。。)

  • \Illuminate\Http\Response
  • \Illuminate\Http\JsonResponse
  • \Illuminate\Http\RedirectResponse

そこで今回は\Illuminate\Http\Responseだけを考えます。
\Illuminate\Http\JsonResponse\Illuminate\Http\RedirectResponseは考慮しない形です。

2. originalプロパティの型はmixedなので、\Illuminate\View\Viewのインスタンスか確認

レスポンスで返す値は\Illuminate\Http\ResponseTrait\Illuminate\Http\Responseが読み込んでいるTrait)の$originalプロパティに入っています。
レスポンスなので、string,intなど色々な型が想定できますね。

なので、今回はViewに渡す値を書き換えたいので、\Illuminate\View\Viewのインスタンスかどうか調べる必要があったりします。

3. Viewに渡した値を取得し、書き換える

Viewに渡す値は\Illuminate\View\ViewgetDataメソッドで取得することができます。
取得した値を加工は適当にnameの値を変えておきます。
書き換えた値を再度Viewに渡すにはwithメソッドを使います。

4. レスポンスクラスに渡している値を書き換える

Viewに渡している値を書き換えるだけではレスポンスの値には反映しないので、再度\Illuminate\Http\Responseに値を設定してあげる必要があります。

$originalプロパティはpublicなので、直接値を設定することも可能ですが、
最終的に返すのは、Symfony\Component\HttpFoundation\Response$contentプロパティになるので、$originalプロパティに値を設定しても意味がないです。

そこで\Illuminate\Http\Responseのインスタンス生成時にも呼ばれているsetContentメソッドを使うことで$contentプロパティにも値を設定しています。

確認

では、再度php artisan serveでサーバーを立ち上げて確認したいと思います。

無事に太郎が花子に変わりましたね

まとめ

実際にViewに渡した値を書き換える機会はほぼ無いかもしれないですが、
Middlewareクラスを使えば簡単に書き換えることは可能でした。

こんなこともできるってことがわかったことが今回の収穫ですね。

Discussion