【Laravel】API Resourceクラスにメタデータを追加する方法


```php
{
	"data": {
		//メインのデータ
	},
	"meta": {
     "key": "メタデータを表示します。"
	}
}
```

上記のようにResource、あるいはResourceCollectionのトップレベルにメタデータを追加する方法は、[公式ドキュメント（日本語訳）](https://readouble.com/laravel/9.x/ja/eloquent-resources.html)の**メタデータの追加**の項でも一応説明されているが、少し補足すると下記のようになる。

- JsonResourceを継承したクラスでもResourceCollectionを継承したクラスのどちらでも、トップレベルの階層にメタデータを追加することができる。

- メタデータを追加する方法として、Resourceクラスインスタンスを新しく生成する際にadditionalメソッドをチェインするか、リソースクラス内でwithメソッドをオーバーライドする2パターンがある。

- ケースによってメタデータを含めたり含めなかったりする場合はadditionalメソッドを使用して個別にメタデータを指定し、常に共通のメタデータを使用する場合はwithメソッドを使うという使い分けで良いと思われる。

公式ドキュメントにも説明はあるが、さらっとしているのでもう少し深掘りする。


## 指定したデータがメタデータとして追加される仕組み

```php:Illuminate\Http\Resources\Json\ResoureResponse
protected function wrap($data, $with = [], $additional = [])
    {
        if ($data instanceof Collection) {
            $data = $data->all();
        }

        if ($this->haveDefaultWrapperAndDataIsUnwrapped($data)) {
            $data = [$this->wrapper() => $data];
        } elseif ($this->haveAdditionalInformationAndDataIsUnwrapped($data, $with, $additional)) {
            $data = [($this->wrapper() ?? 'data') => $data];
        }

        return array_merge_recursive($data, $with, $additional);
		}
```

$dataはレスポンスのメインとなるコレクションや配列のデータが格納される。

withメソッドで指定した配列が変数withに格納され、additionalメソッドで指定した配列が$additionalメソッドに格納されてarray_merge_recursiveによって全てマージされる。

### additionalメソッドを使った場合

```php
class HogeController extends Controller
{
    public function hoge()
    {
				//何らかの処理
				$data = ...
				$value = 'メタデータを表示します。';
        return (new FugaResource($data))->additional([
            'meta' => [
                'key' => $value,
            ],
        ]);
    }
}
```

新しいリソースクラスのインスタンスを生成しているnew FugaResource()の箇所を丸括弧で囲み、additionalメソッドをチェインする形でメタデータを配列形式で渡す。

すると上記で渡した配列が先述した変数$additionalに格納され、最後に
array_merge_recursiveが実行されることで、レスポンスJsonに含まれるようになる。

### withメソッドを使った場合

```php
class HogeCollection extends ResourceCollection
{
    public function with($request)
    {
        $value = 'メタデータを表示します。';
        return [
            'meta' => [
                'key' => $value,
            ],
        ];
    }
}
```

## withメソッドで条件分岐をつけたい場合

```php
class JsonResource implements ArrayAccess, JsonSerializable, Responsable, UrlRoutable
{
	...

	public $with = [];

	...	
	public function with($request)
  {
    return $this->with;
  }
	...
}
```

上記のように$withの初期値は空の配列なので、APIレスポンスにメタデータを付与したくない場合は同じように空の配列を返せば良い。

```php
class JsonResource implements ArrayAccess, JsonSerializable, Responsable, UrlRoutable
{
	...

	public $with = [];

	...	
	public function with($request)
  {
        $value = 'meta';
        if( //メタデータを返したい条件 ){
            'meta' => [
                'key' => $value,
            ],
        }else{
            return []; //メタデータが不要な場合は空の配列を返す。
        }

  }
	...
}
```

配列の形で返さないと冒頭のarray_merge_recursiveに引っかかってエラーが発生する。

## 両方指定するとどうなるか

array_merge_recursiveによって結合された配列がアウトプットされる。
キーが重複する場合は上書きされるのではなく、配列に追加していく形となる。


```php

class HogeController extends Controller
{
    public function hoge()
    {
				//何らかの処理
				$data = ...
				$value = 'メタデータを表示します。';
        return (new FugaResource($data))->additional([
            'meta' => [
                'key' => $value,
            ],
        ]);
    }
}

class FugaResource extends ResourceCollection
{
    public function with($request)
    {
        $value = 'メタデータを表示します。';
        return [
            'meta' => [
                'key' => $value,
            ],
        ];
    }
}
```

▼ 結果
```json
{
	"data": [],
	"meta": {
		"key": [
			"メタデータを表示します。",
			"メタデータを表示します。"
		]
	}
}
```