💨

Laravel で API 実装する際の Controller の返り値は"なんでも"良い

2023/06/10に公開

みなさん Laravel 使ってますか?
PHP のフレームワークの中でも巨大なフレームワークで独自の書き方や機能がたくさんあります。

今回はその中でも API を実装する際の Controller の返り値について言及したいと思います。

まず生の PHP で API 実装するとどうなるか

まずは生の PHP で API 実装する際のことを考えてみます。

だいたいのケースで以下のようなコードが必要になると思います。

<?php

use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
.
.
.
$data = [
    'message' => 'success',
    'data' => [
	'id' => 1, 
	'name' => 'Andy'
    ]
];

// json 形式で返したいので、配列を json に
$responseBody = json_encode($data);
// body に書き込み
$response->getBody()->write($responseBody);

// Header に Content-Type: application/json を定義
return $response->withHeader('Content-Type', 'application/json; cahrset=UTF-8');

json を返す API を実装したい場合、json_encode() して Header に application/json を付与するケースが多いかなと思います。(これ以外にこういう実装方法がある等あれば知りたいです。)

これを Laravel で書き直してみます。

response()->json()

Laravel では独自の Response オブジェクトが提供されます。(そのクラスを使える選択肢があるだけで、SymfonyResponse や Psr7 も使えます。)

それを使えば簡単に API を実装できます。

class MyController
{
    public function index(): JsonResponse
    {
        return response()->json([
	    'message' => 'success',
	    'data' => [
		'id' => 1, 
		'name' => 'Andy'
	    ]
	]);
    }
}

response()->json() を呼び出すと内部で json_encode() し Header に application/json を付与します。

そのため自分で json_encode せずとも json() の引数に配列などのデータを渡すだけで完結します。

これだけでもかなり簡単に思えますが、もっと楽な方法があります。

Controller の返り値は"なんでも"良い

タイトル回収です👍🏻

先ほど response()->json() を用いれば楽に json 形式のデータを返すようにできると書きましたが、もっと簡単な方法があります。
それは Controller の返り値にそのまま配列などを返す方法です。

class MyController
{
    public function index(): array
    {
        return [
	    'message' => 'success',
	    'data' => [
		'id' => 1, 
		'name' => 'Andy'
	    ]
	]);
    }
}

こうすると内部で勝手に json_encode し application/json を付与します。
これは配列だけではなく stdClass や その他クラスも許容されています。

実際に Xdebug を使って中身を見てみましょう。

コードリーディング用に repo を用意し、Controller に以下を作成しました。
https://github.com/UserKazun/my-laravel/blob/f948b2c4a2e1afc8ca7b40b999f96b187642dfe7/src/app/Http/Controllers/ResponseCheckController.php#L36-L53

38行目にブレイクポイントを置いて、ステップオーバーします。
すると以下の処理に飛びます。

toResponse() が呼ばれているようなので、ステップインしてみます。

すると上記のコードに辿り着きます。
897〜904行目を見ると Arrayable || Jsonable || ArrayObject || JsonSerializable || stdClass || is_array() となっているので、これらは全て許容されています。

904行目以降まで進めてその時点での $response の中身を見てみると

上記のようになっており、header には content-type: application/json が付与されて、data の中には Controller で記述した配列の中身が json 形式として入っていることが確認できます。

念の為 stdClass を返す関数を実装して中身を見てみます。

https://github.com/UserKazun/my-laravel/blob/f948b2c4a2e1afc8ca7b40b999f96b187642dfe7/src/app/Http/Controllers/ResponseCheckController.php#L14-L28

postman から実行して response を確認します。

json 形式でデータが得られ、Header にも application/json がくっついていますね。
今回は array と stdClass しか確認しませんでしたが、コードを見る限り他のオブジェクトでも同様の結果が得られるはずです。

このように Controller に実装した関数の戻り値は"なんでも"とはいかなかったですが、6種類の型が許容されており選択肢の幅が広いことがわかりました。

最後に

ひとつ疑問に思ったのが、SymfonyResponse は拒否されているところです。 Symfony だけ別の処理に飛ばされるのでしょうか?
ここはぜひ確認してみたいですね。

参考

https://readouble.com/laravel/8.x/ja/responses.html

Discussion