LaravelのJSON出力をPrettyPrintしたかった


Laravelのコントローラは、配列を返せば勝手にJSONにして表示してくれます。
ここをPrettyPrintしたかったので`Illuminate\Http\Response`を覗いてみたのですが、

```php:Response疑似コード
public function setContent($content)
    if ($this->shouldBeJson($content)) {
        $this->header('Content-Type', 'application/json');
        if ($content instanceof Jsonable) {
            return $content->toJson();
        } elseif ($content instanceof Arrayable) {
            return json_encode($content->toArray());
        }
        return json_encode($content);
    }
}
```

とかなってて手を出せませんでした。

公式の[過去](https://github.com/laravel/framework/issues/3929)の[議論](https://github.com/laravel/framework/pull/7223)を漁ってみたところ、『そんなものはいらん』『自分で`Response::json()`って書け』みたいなかんじになってました。

もっとよい答えはないか探していたところ、そのあたりミドルウェアで作った人を見つけました。
以下は[Aaron Saray](https://www.aaronsaray.com/)によるブログ記事、[Laravel Pretty Print JSON Middleware](https://www.aaronsaray.com/2017/laravel-pretty-print-middleware)の日本語訳です。

# Laravel Pretty Print JSON Middleware

私はテストをするために[Postman](https://www.getpostman.com/)をよく使います。
これはプレビューモードにおいて、APIのJSONレスポンスを整形・解読して表示してくれます。
しかし先日、外部ツールを使用したり環境全体を変更したりせず、ただ単にJSONレスポンスをPrettyPrintしたいだけの人がいるということを小耳に挟みました。

知ってのとおり`json_encode`はエンコードオプションに対応していて、そのうちのひとつが`JSON_PRETTY_PRINT`です。
これは改行とスペースを挿入し、整形されたJSONを出力するというオプションです。
私は現在Laravelのプロジェクトに取り組んでいるため、Laravelで同じことを行いたくなりました。
これを行うにはどうすればよいでしょうか？
簡単にできるちょっとしたソリューションですか？

最初に試した方法は、`response()->json()`のようにJSONで返しているところを全て探し出して、そこにオプションを追加する方法でした。
それは全くスケーラブルでなく、そしていとも簡単に見逃してしまいます。
そこで私はミドルウェアを使うことを思いつきました。

```php
<?php
/**
 * クエリに'?pretty=true'と入れるとJSONがPrettyPrintで返ってくるミドルウェア
 */
declare(strict_types=1);

namespace App\Http\Middleware;

use Illuminate\Http\JsonResponse;

/**
 * Class PrettyPrintMiddleware
 * @package App\Http\Middleware
 */
class PrettyPrintMiddleware
{
  /**
   * @var string 監視対象のクエリパラメータ
   */
  const QUERY_PARAMETER = 'pretty';

  /**
   * 指定があればJSON_PRETTY_PRINTして返す
   * 
   * @param $request
   * @param \Closure $next
   * @return mixed
   */
  public function handle($request, \Closure $next)
  {
    $response = $next($request);
    
    if ($response instanceof JsonResponse) {
      if ($request->query(self::QUERY_PARAMETER) == 'true') {
        $response->setEncodingOptions(JSON_PRETTY_PRINT);
      }
    }
    
    return $response;
  }
}
```

このミドルウェアを割り当てたのち、クエリパラメータ`pretty=true` (QUERY_PARAMETERで他の値に変更可能) を指定するとレスポンスにエンコードオプションが追加されます。
他には何もする必要なく、あなたの要求を満足するでしょう。

通常のJSONはこのようになります。

```json
[{"id":123,"name":"first item"},{"id":124,"name":"first item"}]
```

今後はこうなります。

```json
[
    {
        "id": 123,
        "name": "first item"
    },
    {
        "id": 123,
        "name": "first item"
    }
]
```

念頭に置いておくべきことは、これは非常に巨大な破壊的アプローチであるということです。
もし他のJSONエンコードオプションを使用している場合は、単に設定するだけではなく、設定をスタックに積む必要があります。

このコードをパッケージ化する場合は、パラメータの`query`をコンフィグで設定するようにするとよいでしょう。
また、値にも`1`や`TRUE`、`on`なども受け付けるようにするとよいでしょう。


# 感想

アクションごと個別にPrettyPrintしたいのであれば、

```php
  return response()->json($output, 200, [], \JSON_PRETTY_PRINT);
```

で事足ります。

しかし、全てのJSONレスポンスに対して一括でPrettyPrintしたい場合、全てのreturn部分を書き換えるとか、ResponseクラスをextendsしてsetContentを書き換えるとか、そういうのはちょっとやりたくない方法です。
そんな場合はこのミドルウェアが役立ちますね。

なお記事にもあるとおり、この書き方では既に他のオプションが設定されていた場合、それが消えてしまいます。
該当部分を以下のように書き換えるとよいでしょう。

```php
  $response->setEncodingOptions($response->getEncodingOptions() | JSON_PRETTY_PRINT);
```


[JSON_PRETTY_PRINT](https://www.google.com/search?q=JSON_PRETTY_PRINT+Laravel)で全く日本語記事が見当たらなかったのでここまで苦労して調べたのですが、[JSON_UNESCAPED_UNICODE](https://www.google.com/search?q=JSON_UNESCAPED_UNICODE+Laravel)だと[山ほど見つかった](https://nextat.co.jp/staff/archives/203)とかいう。