Eloquent: APIリソース
イントロダクション
APIを構築する際、Eloquentモデルとアプリケーションのユーザーに実際に返されるJSONレスポンスの間に位置する変換レイヤーが必要になる場合があります。たとえば、一部のユーザーには特定の属性を表示し、他のユーザーには表示しないようにしたり、モデルのJSON表現に特定のリレーションシップを常に含めたりしたい場合があります。Eloquentのリソースクラスを使用すると、モデルやモデルコレクションを表現力豊かで簡単にJSONに変換できます。
もちろん、EloquentモデルやコレクションはいつでもtoJsonメソッドを使用してJSONに変換できます。しかし、Eloquentリソースは、モデルとそのリレーションシップのJSONシリアライズをよりきめ細かく、堅牢に制御する機能を提供します。
リソースの生成
リソースクラスを生成するには、make:resource Artisanコマンドを使用します。デフォルトでは、リソースはアプリケーションのapp/Http/Resourcesディレクトリに配置されます。リソースはIlluminate\Http\Resources\Json\JsonResourceクラスを拡張します。
1php artisan make:resource UserResource
リソースコレクション
個々のモデルを変換するリソースを生成するだけでなく、モデルのコレクションを変換する責任を持つリソースを生成することもできます。これにより、JSONレスポンスに、指定されたリソースのコレクション全体に関連するリンクやその他のメタ情報を含めることができます。
リソースコレクションを作成するには、リソースを作成する際に--collectionフラグを使用する必要があります。または、リソース名にCollectionという単語を含めると、Laravelはコレクションリソースを作成すべきであると判断します。コレクションリソースは、Illuminate\Http\Resources\Json\ResourceCollectionクラスを拡張します。
1php artisan make:resource User --collection2 3php artisan make:resource UserCollection
概念の概要
これは、リソースとリソースコレクションの概要です。リソースが提供するカスタマイズとパワーをより深く理解するために、このドキュメントの他のセクションを読むことを強くお勧めします。
リソースを記述する際に利用できるすべてのオプションを詳しく説明する前に、まずLaravel内でリソースがどのように使用されるかを大まかに見てみましょう。リソースクラスは、JSON構造に変換する必要がある単一のモデルを表します。たとえば、以下は単純なUserResourceリソースクラスです。
1<?php 2 3namespace App\Http\Resources; 4 5use Illuminate\Http\Request; 6use Illuminate\Http\Resources\Json\JsonResource; 7 8class UserResource extends JsonResource 9{10 /**11 * Transform the resource into an array.12 *13 * @return array<string, mixed>14 */15 public function toArray(Request $request): array16 {17 return [18 'id' => $this->id,19 'name' => $this->name,20 'email' => $this->email,21 'created_at' => $this->created_at,22 'updated_at' => $this->updated_at,23 ];24 }25}
すべてのリソースクラスは、リソースがルートやコントローラメソッドからのレスポンスとして返されるときにJSONに変換されるべき属性の配列を返すtoArrayメソッドを定義します。
$this変数からモデルのプロパティに直接アクセスできることに注意してください。これは、リソースクラスがプロパティやメソッドへのアクセスを、便利なアクセスのために自動的に基になるモデルにプロキシするためです。リソースを定義したら、ルートやコントローラから返すことができます。リソースは、コンストラクタを介して基になるモデルインスタンスを受け入れます。
1use App\Http\Resources\UserResource;2use App\Models\User;3 4Route::get('/user/{id}', function (string $id) {5 return new UserResource(User::findOrFail($id));6});
リソースコレクション
リソースのコレクションまたはページ分割されたレスポンスを返す場合は、ルートまたはコントローラでリソースインスタンスを作成するときに、リソースクラスが提供するcollectionメソッドを使用する必要があります。
1use App\Http\Resources\UserResource;2use App\Models\User;3 4Route::get('/users', function () {5 return UserResource::collection(User::all());6});
この方法では、コレクションと一緒に返す必要があるカスタムメタデータを追加できないことに注意してください。リソースコレクションのレスポンスをカスタマイズしたい場合は、そのコレクションを表す専用のリソースを作成できます。
1php artisan make:resource UserCollection
リソースコレクションクラスが生成されたら、レスポンスに含めるべきメタデータを簡単に定義できます。
1<?php 2 3namespace App\Http\Resources; 4 5use Illuminate\Http\Request; 6use Illuminate\Http\Resources\Json\ResourceCollection; 7 8class UserCollection extends ResourceCollection 9{10 /**11 * Transform the resource collection into an array.12 *13 * @return array<int|string, mixed>14 */15 public function toArray(Request $request): array16 {17 return [18 'data' => $this->collection,19 'links' => [20 'self' => 'link-value',21 ],22 ];23 }24}
リソースコレクションを定義した後、ルートやコントローラから返すことができます。
1use App\Http\Resources\UserCollection;2use App\Models\User;3 4Route::get('/users', function () {5 return new UserCollection(User::all());6});
コレクションキーの保持
ルートからリソースコレクションを返すとき、Laravelはコレクションのキーをリセットして、それらが数値順になるようにします。しかし、リソースクラスにpreserveKeysプロパティを追加して、コレクションの元のキーを保持するかどうかを指定できます。
1<?php 2 3namespace App\Http\Resources; 4 5use Illuminate\Http\Resources\Json\JsonResource; 6 7class UserResource extends JsonResource 8{ 9 /**10 * Indicates if the resource's collection keys should be preserved.11 *12 * @var bool13 */14 public $preserveKeys = true;15}
preserveKeysプロパティがtrueに設定されている場合、コレクションがルートやコントローラから返されるときにコレクションのキーは保持されます。
1use App\Http\Resources\UserResource;2use App\Models\User;3 4Route::get('/users', function () {5 return UserResource::collection(User::all()->keyBy->id);6});
基になるリソースクラスのカスタマイズ
通常、リソースコレクションの$this->collectionプロパティは、コレクションの各項目をその単数形リソースクラスにマッピングした結果で自動的に設定されます。単数形リソースクラスは、コレクションのクラス名から末尾のCollection部分を除いたものと想定されます。さらに、個人の好みに応じて、単数形リソースクラスにはResourceの接尾辞が付いている場合と付いていない場合があります。
たとえば、UserCollectionは、与えられたユーザーインスタンスをUserResourceリソースにマッピングしようとします。この動作をカスタマイズするには、リソースコレクションの$collectsプロパティをオーバーライドします。
1<?php 2 3namespace App\Http\Resources; 4 5use Illuminate\Http\Resources\Json\ResourceCollection; 6 7class UserCollection extends ResourceCollection 8{ 9 /**10 * The resource that this resource collects.11 *12 * @var string13 */14 public $collects = Member::class;15}
リソースの記述
概念の概要をまだ読んでいない場合は、このドキュメントを進める前に読むことを強くお勧めします。
リソースは、与えられたモデルを配列に変換するだけで済みます。したがって、各リソースには、モデルの属性をアプリケーションのルートやコントローラから返すことができるAPIフレンドリーな配列に変換するtoArrayメソッドが含まれています。
1<?php 2 3namespace App\Http\Resources; 4 5use Illuminate\Http\Request; 6use Illuminate\Http\Resources\Json\JsonResource; 7 8class UserResource extends JsonResource 9{10 /**11 * Transform the resource into an array.12 *13 * @return array<string, mixed>14 */15 public function toArray(Request $request): array16 {17 return [18 'id' => $this->id,19 'name' => $this->name,20 'email' => $this->email,21 'created_at' => $this->created_at,22 'updated_at' => $this->updated_at,23 ];24 }25}
リソースを定義したら、ルートやコントローラから直接返すことができます。
1use App\Http\Resources\UserResource;2use App\Models\User;3 4Route::get('/user/{id}', function (string $id) {5 return new UserResource(User::findOrFail($id));6});
リレーションシップ
レスポンスに関連リソースを含めたい場合は、リソースのtoArrayメソッドが返す配列に追加できます。この例では、PostResourceリソースのcollectionメソッドを使用して、ユーザーのブログ投稿をリソースレスポンスに追加します。
1use App\Http\Resources\PostResource; 2use Illuminate\Http\Request; 3 4/** 5 * Transform the resource into an array. 6 * 7 * @return array<string, mixed> 8 */ 9public function toArray(Request $request): array10{11 return [12 'id' => $this->id,13 'name' => $this->name,14 'email' => $this->email,15 'posts' => PostResource::collection($this->posts),16 'created_at' => $this->created_at,17 'updated_at' => $this->updated_at,18 ];19}
リレーションシップがすでにロードされている場合にのみリレーションシップを含めたい場合は、条件付きリレーションシップに関するドキュメントを確認してください。
リソースコレクション
リソースは単一のモデルを配列に変換しますが、リソースコレクションはモデルのコレクションを配列に変換します。ただし、すべてのリソースがその場で「アドホック」なリソースコレクションを生成するためのcollectionメソッドを提供しているため、モデルごとにリソースコレクションクラスを定義することは必ずしも必要ではありません。
1use App\Http\Resources\UserResource;2use App\Models\User;3 4Route::get('/users', function () {5 return UserResource::collection(User::all());6});
しかし、コレクションと共に返されるメタデータをカスタマイズする必要がある場合は、独自のリソースコレクションを定義する必要があります。
1<?php 2 3namespace App\Http\Resources; 4 5use Illuminate\Http\Request; 6use Illuminate\Http\Resources\Json\ResourceCollection; 7 8class UserCollection extends ResourceCollection 9{10 /**11 * Transform the resource collection into an array.12 *13 * @return array<string, mixed>14 */15 public function toArray(Request $request): array16 {17 return [18 'data' => $this->collection,19 'links' => [20 'self' => 'link-value',21 ],22 ];23 }24}
単数形のリソースと同様に、リソースコレクションもルートやコントローラから直接返すことができます。
1use App\Http\Resources\UserCollection;2use App\Models\User;3 4Route::get('/users', function () {5 return new UserCollection(User::all());6});
データラッピング
デフォルトでは、リソースレスポンスがJSONに変換されるとき、最も外側のリソースはdataキーでラップされます。したがって、たとえば、典型的なリソースコレクションのレスポンスは次のようになります。
1{ 2 "data": [ 3 { 4 "id": 1, 5 "name": "Eladio Schroeder Sr.", 7 }, 8 { 9 "id": 2,10 "name": "Liliana Mayert",12 }13 ]14}
最も外側のリソースのラッピングを無効にしたい場合は、ベースのIlluminate\Http\Resources\Json\JsonResourceクラスのwithoutWrappingメソッドを呼び出す必要があります。通常、このメソッドはAppServiceProviderまたはアプリケーションへのすべてのリクエストでロードされる他のサービスプロバイダから呼び出す必要があります。
1<?php 2 3namespace App\Providers; 4 5use Illuminate\Http\Resources\Json\JsonResource; 6use Illuminate\Support\ServiceProvider; 7 8class AppServiceProvider extends ServiceProvider 9{10 /**11 * Register any application services.12 */13 public function register(): void14 {15 // ...16 }17 18 /**19 * Bootstrap any application services.20 */21 public function boot(): void22 {23 JsonResource::withoutWrapping();24 }25}
withoutWrappingメソッドは最も外側のレスポンスにのみ影響し、独自のリソースコレクションに手動で追加したdataキーは削除しません。
ネストされたリソースのラッピング
リソースのリレーションシップをどのようにラップするかは完全に自由に決めることができます。すべてのリソースコレクションをネストのレベルに関係なくdataキーでラップしたい場合は、各リソースのリソースコレクションクラスを定義し、そのコレクションをdataキー内で返す必要があります。
これにより、最も外側のリソースが2つのdataキーでラップされるのではないかと疑問に思うかもしれません。心配しないでください。Laravelはリソースが誤って二重にラップされることを決して許さないので、変換しているリソースコレクションのネストレベルを気にする必要はありません。
1<?php 2 3namespace App\Http\Resources; 4 5use Illuminate\Http\Resources\Json\ResourceCollection; 6 7class CommentsCollection extends ResourceCollection 8{ 9 /**10 * Transform the resource collection into an array.11 *12 * @return array<string, mixed>13 */14 public function toArray(Request $request): array15 {16 return ['data' => $this->collection];17 }18}
データラッピングとページネーション
リソースレスポンスを介してページ分割されたコレクションを返す場合、withoutWrappingメソッドが呼び出されていても、Laravelはリソースデータをdataキーでラップします。これは、ページ分割されたレスポンスには、ページネータの状態に関する情報を含むmetaキーとlinksキーが常に含まれるためです。
1{ 2 "data": [ 3 { 4 "id": 1, 5 "name": "Eladio Schroeder Sr.", 7 }, 8 { 9 "id": 2,10 "name": "Liliana Mayert",12 }13 ],14 "links":{15 "first": "http://example.com/users?page=1",16 "last": "http://example.com/users?page=1",17 "prev": null,18 "next": null19 },20 "meta":{21 "current_page": 1,22 "from": 1,23 "last_page": 1,24 "path": "http://example.com/users",25 "per_page": 15,26 "to": 10,27 "total": 1028 }29}
ペジネーション
Laravelのページネーターインスタンスをリソースのcollectionメソッドまたはカスタムリソースコレクションに渡すことができます。
1use App\Http\Resources\UserCollection;2use App\Models\User;3 4Route::get('/users', function () {5 return new UserCollection(User::paginate());6});
ページ分割されたレスポンスには、ページネータの状態に関する情報を含むmetaキーとlinksキーが常に含まれます。
1{ 2 "data": [ 3 { 4 "id": 1, 5 "name": "Eladio Schroeder Sr.", 7 }, 8 { 9 "id": 2,10 "name": "Liliana Mayert",12 }13 ],14 "links":{15 "first": "http://example.com/users?page=1",16 "last": "http://example.com/users?page=1",17 "prev": null,18 "next": null19 },20 "meta":{21 "current_page": 1,22 "from": 1,23 "last_page": 1,24 "path": "http://example.com/users",25 "per_page": 15,26 "to": 10,27 "total": 1028 }29}
ページネーション情報のカスタマイズ
ページネーションレスポンスのlinksまたはmetaキーに含まれる情報をカスタマイズしたい場合は、リソースにpaginationInformationメソッドを定義できます。このメソッドは、$paginatedデータと$default情報の配列(linksとmetaキーを含む配列)を受け取ります。
1/** 2 * Customize the pagination information for the resource. 3 * 4 * @param \Illuminate\Http\Request $request 5 * @param array $paginated 6 * @param array $default 7 * @return array 8 */ 9public function paginationInformation($request, $paginated, $default)10{11 $default['links']['custom'] = 'https://example.com';12 13 return $default;14}
条件付き属性
特定の条件が満たされた場合にのみ、リソースレスポンスに属性を含めたい場合があります。たとえば、現在のユーザーが「管理者」である場合にのみ値を含めたい場合があります。Laravelはこの状況を支援するためのさまざまなヘルパーメソッドを提供しています。whenメソッドを使用して、リソースレスポンスに条件付きで属性を追加できます。
1/** 2 * Transform the resource into an array. 3 * 4 * @return array<string, mixed> 5 */ 6public function toArray(Request $request): array 7{ 8 return [ 9 'id' => $this->id,10 'name' => $this->name,11 'email' => $this->email,12 'secret' => $this->when($request->user()->isAdmin(), 'secret-value'),13 'created_at' => $this->created_at,14 'updated_at' => $this->updated_at,15 ];16}
この例では、認証されたユーザーのisAdminメソッドがtrueを返す場合にのみ、secretキーが最終的なリソースレスポンスで返されます。メソッドがfalseを返す場合、secretキーはクライアントに送信される前にリソースレスポンスから削除されます。whenメソッドを使用すると、配列を構築する際に条件文に頼ることなく、リソースを表現力豊かに定義できます。
whenメソッドは、2番目の引数としてクロージャも受け入れ、指定された条件がtrueの場合にのみ結果の値を計算できます。
1'secret' => $this->when($request->user()->isAdmin(), function () {2 return 'secret-value';3}),
whenHasメソッドは、属性が実際に基になるモデルに存在する場合にその属性を含めるために使用できます。
1'name' => $this->whenHas('name'),
さらに、whenNotNullメソッドは、属性がnullでない場合にリソースレスポンスにその属性を含めるために使用できます。
1'name' => $this->whenNotNull($this->name),
条件付き属性のマージ
同じ条件に基づいてリソースレスポンスに含めるべき複数の属性がある場合があります。この場合、mergeWhenメソッドを使用して、指定された条件がtrueの場合にのみレスポンスに属性を含めることができます。
1/** 2 * Transform the resource into an array. 3 * 4 * @return array<string, mixed> 5 */ 6public function toArray(Request $request): array 7{ 8 return [ 9 'id' => $this->id,10 'name' => $this->name,11 'email' => $this->email,12 $this->mergeWhen($request->user()->isAdmin(), [13 'first-secret' => 'value',14 'second-secret' => 'value',15 ]),16 'created_at' => $this->created_at,17 'updated_at' => $this->updated_at,18 ];19}
ここでも、指定された条件がfalseの場合、これらの属性はクライアントに送信される前にリソースレスポンスから削除されます。
mergeWhenメソッドは、文字列キーと数値キーが混在する配列内では使用しないでください。さらに、連続して順序付けられていない数値キーを持つ配列内でも使用しないでください。
条件付きリレーションシップ
条件付きで属性をロードすることに加えて、リレーションシップがモデルに既にロードされているかどうかに基づいて、リソースレスポンスにリレーションシップを条件付きで含めることができます。これにより、コントローラはモデルにどのリレーションシップをロードするかを決定でき、リソースはそれらが実際にロードされた場合にのみ簡単に含めることができます。最終的に、これによりリソース内での「N+1」クエリ問題を回避しやすくなります。
whenLoadedメソッドを使用して、リレーションシップを条件付きでロードできます。不必要にリレーションシップをロードするのを避けるために、このメソッドはリレーションシップ自体ではなく、リレーションシップの名前を受け入れます。
1use App\Http\Resources\PostResource; 2 3/** 4 * Transform the resource into an array. 5 * 6 * @return array<string, mixed> 7 */ 8public function toArray(Request $request): array 9{10 return [11 'id' => $this->id,12 'name' => $this->name,13 'email' => $this->email,14 'posts' => PostResource::collection($this->whenLoaded('posts')),15 'created_at' => $this->created_at,16 'updated_at' => $this->updated_at,17 ];18}
この例では、リレーションシップがロードされていない場合、postsキーはクライアントに送信される前にリソースレスポンスから削除されます。
条件付きリレーションシップカウント
リレーションシップを条件付きで含めることに加えて、リレーションシップの「カウント」がモデルにロードされているかどうかに基づいて、リソースレスポンスにリレーションシップのカウントを条件付きで含めることができます。
1new UserResource($user->loadCount('posts'));
whenCountedメソッドを使用して、リソースレスポンスにリレーションシップのカウントを条件付きで含めることができます。このメソッドは、リレーションシップのカウントが存在しない場合に属性を不必要に含めることを避けます。
1/** 2 * Transform the resource into an array. 3 * 4 * @return array<string, mixed> 5 */ 6public function toArray(Request $request): array 7{ 8 return [ 9 'id' => $this->id,10 'name' => $this->name,11 'email' => $this->email,12 'posts_count' => $this->whenCounted('posts'),13 'created_at' => $this->created_at,14 'updated_at' => $this->updated_at,15 ];16}
この例では、postsリレーションシップのカウントがロードされていない場合、posts_countキーはクライアントに送信される前にリソースレスポンスから削除されます。
avg、sum、min、maxなどの他の種類の集計も、whenAggregatedメソッドを使用して条件付きでロードできます。
1'words_avg' => $this->whenAggregated('posts', 'words', 'avg'),2'words_sum' => $this->whenAggregated('posts', 'words', 'sum'),3'words_min' => $this->whenAggregated('posts', 'words', 'min'),4'words_max' => $this->whenAggregated('posts', 'words', 'max'),
条件付きピボット情報
リソースレスポンスにリレーションシップ情報を条件付きで含めることに加えて、whenPivotLoadedメソッドを使用して、多対多リレーションシップの中間テーブルからのデータを条件付きで含めることができます。whenPivotLoadedメソッドは、最初の引数としてピボットテーブルの名前を受け入れます。2番目の引数は、ピボット情報がモデルで利用可能な場合に返される値を返すクロージャである必要があります。
1/** 2 * Transform the resource into an array. 3 * 4 * @return array<string, mixed> 5 */ 6public function toArray(Request $request): array 7{ 8 return [ 9 'id' => $this->id,10 'name' => $this->name,11 'expires_at' => $this->whenPivotLoaded('role_user', function () {12 return $this->pivot->expires_at;13 }),14 ];15}
リレーションシップがカスタム中間テーブルモデルを使用している場合、中間テーブルモデルのインスタンスをwhenPivotLoadedメソッドの最初の引数として渡すことができます。
1'expires_at' => $this->whenPivotLoaded(new Membership, function () {2 return $this->pivot->expires_at;3}),
中間テーブルがpivot以外のアクセサを使用している場合は、whenPivotLoadedAsメソッドを使用できます。
1/** 2 * Transform the resource into an array. 3 * 4 * @return array<string, mixed> 5 */ 6public function toArray(Request $request): array 7{ 8 return [ 9 'id' => $this->id,10 'name' => $this->name,11 'expires_at' => $this->whenPivotLoadedAs('subscription', 'role_user', function () {12 return $this->subscription->expires_at;13 }),14 ];15}
メタデータの追加
一部のJSON API標準では、リソースおよびリソースコレクションのレスポンスにメタデータを追加する必要があります。これには、リソースまたは関連リソースへのlinksや、リソース自体に関するメタデータなどが含まれることがよくあります。リソースに関する追加のメタデータを返す必要がある場合は、それをtoArrayメソッドに含めます。たとえば、リソースコレクションを変換するときにlinks情報を含めることができます。
1/** 2 * Transform the resource into an array. 3 * 4 * @return array<string, mixed> 5 */ 6public function toArray(Request $request): array 7{ 8 return [ 9 'data' => $this->collection,10 'links' => [11 'self' => 'link-value',12 ],13 ];14}
リソースから追加のメタデータを返す場合、ページ分割されたレスポンスを返すときにLaravelが自動的に追加するlinksまたはmetaキーを誤って上書きする心配はありません。定義した追加のlinksは、ページネータが提供するリンクとマージされます。
トップレベルのメタデータ
リソースが返される最も外側のリソースである場合にのみ、特定のメタデータをリソースレスポンスに含めたい場合があります。通常、これにはレスポンス全体に関するメタ情報が含まれます。このメタデータを定義するには、リソースクラスにwithメソッドを追加します。このメソッドは、リソースが変換される最も外側のリソースである場合にのみ、リソースレスポンスに含めるメタデータの配列を返す必要があります。
1<?php 2 3namespace App\Http\Resources; 4 5use Illuminate\Http\Resources\Json\ResourceCollection; 6 7class UserCollection extends ResourceCollection 8{ 9 /**10 * Transform the resource collection into an array.11 *12 * @return array<string, mixed>13 */14 public function toArray(Request $request): array15 {16 return parent::toArray($request);17 }18 19 /**20 * Get additional data that should be returned with the resource array.21 *22 * @return array<string, mixed>23 */24 public function with(Request $request): array25 {26 return [27 'meta' => [28 'key' => 'value',29 ],30 ];31 }32}
リソース構築時のメタデータ追加
ルートやコントローラでリソースインスタンスを構築する際に、トップレベルのデータを追加することもできます。すべてのリソースで利用可能なadditionalメソッドは、リソースレスポンスに追加すべきデータの配列を受け入れます。
1return (new UserCollection(User::all()->load('roles')))2 ->additional(['meta' => [3 'key' => 'value',4 ]]);
リソースレスポンス
すでに読んだように、リソースはルートやコントローラから直接返すことができます。
1use App\Http\Resources\UserResource;2use App\Models\User;3 4Route::get('/user/{id}', function (string $id) {5 return new UserResource(User::findOrFail($id));6});
ただし、クライアントに送信する前に、送信されるHTTPレスポンスをカスタマイズする必要がある場合があります。これを実現するには2つの方法があります。まず、リソースにresponseメソッドをチェーンすることができます。このメソッドはIlluminate\Http\JsonResponseインスタンスを返し、レスポンスのヘッダーを完全に制御できます。
1use App\Http\Resources\UserResource;2use App\Models\User;3 4Route::get('/user', function () {5 return (new UserResource(User::find(1)))6 ->response()7 ->header('X-Value', 'True');8});
あるいは、リソース自体の中にwithResponseメソッドを定義することもできます。このメソッドは、リソースがレスポンスの最も外側のリソースとして返されるときに呼び出されます。
1<?php 2 3namespace App\Http\Resources; 4 5use Illuminate\Http\JsonResponse; 6use Illuminate\Http\Request; 7use Illuminate\Http\Resources\Json\JsonResource; 8 9class UserResource extends JsonResource10{11 /**12 * Transform the resource into an array.13 *14 * @return array<string, mixed>15 */16 public function toArray(Request $request): array17 {18 return [19 'id' => $this->id,20 ];21 }22 23 /**24 * Customize the outgoing response for the resource.25 */26 public function withResponse(Request $request, JsonResponse $response): void27 {28 $response->header('X-Value', 'True');29 }30}