エロクエント: API リソース
はじめに
API を構築する際、Eloquent モデルと実際にアプリケーションのユーザーに返される JSON レスポンスの間に位置する変換レイヤーが必要になる場合があります。たとえば、特定の属性を一部のユーザーに表示したい場合や、常にモデルの JSON 表現に特定のリレーションシップを含めたい場合があります。Eloquent のリソースクラスを使用すると、モデルやモデルコレクションを JSON に表現的かつ簡単に変換することができます。
もちろん、Eloquent モデルやコレクションをその toJson
メソッドを使用して常に JSON に変換することができます。ただし、Eloquent リソースは、モデルとそのリレーションシップの JSON シリアル化に対してより詳細で堅牢な制御を提供します。
リソースの生成
リソースクラスを生成するには、make:resource
Artisan コマンドを使用します。デフォルトでは、リソースはアプリケーションの app/Http/Resources
ディレクトリに配置されます。リソースは Illuminate\Http\Resources\Json\JsonResource
クラスを拡張します:
php artisan make:resource UserResource
リソースコレクション
個々のモデルを変換するリソースを生成するだけでなく、モデルのコレクションを変換する責任があるリソースを生成することもできます。これにより、JSON レスポンスには、指定されたリソースのコレクション全体に関連するリンクやその他のメタ情報を含めることができます。
リソースコレクションを作成するには、リソースを作成する際に --collection
フラグを使用するか、リソース名に Collection
という単語を含めると、Laravel にコレクションリソースを作成するよう指示されます。コレクションリソースは Illuminate\Http\Resources\Json\ResourceCollection
クラスを拡張 します:
php artisan make:resource User --collection
php artisan make:resource UserCollection
コンセプトの概要
これはリソースとリソースコレクションの高レベルな概要です。リソースによって提供されるカスタマイズとパワーの理解を深めるために、このドキュメントの他のセクションを読むことを強くお勧めします。
リソースを記述する際に利用可能なすべてのオプションに立ち入る前に、Laravel内でリソースがどのように使用されるかを高レベルで見てみましょう。リソースクラスは、JSON構造に変換する必要がある単一のモデルを表します。たとえば、次のような単純な UserResource
リソースクラスがあります:
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class UserResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
];
}
}
すべてのリソースクラスは、リソースがルートまたはコントローラメソッドから応答として返されるときにJSONに変換される属性の配列を返す toArray
メソッドを定義します。
モデルのプロパティには $this
変数から直接アクセスできることに注意してください。これは、リソースクラスが便利なアクセスのためにプロパティとメソッドアクセスを基礎となるモデルに自動的にプロキシするためです。リソースが定義されると、ルートまたはコントローラから返される可能性があります。リソースは、基礎となるモデルインスタンスをそのコンストラクタ経由で受け入れます:
use App\Http\Resources\UserResource;
use App\Models\User;
Route::get('/user/{id}', function (string $id) {
return new UserResource(User::findOrFail($id));
});
リソースコレクション
リソースのコレクションまたはページネーションされた応答を返す場合は、ルートまたはコントローラでリソースインスタンスを作成する際に、リソースクラスが提供する collection
メソッドを使用する必要があります:
use App\Http\Resources\UserResource;
use App\Models\User;
Route::get('/users', function () {
return UserResource::collection(User::all());
});
これにより、コレクションと一緒に返す必要があるカスタムメタデータの追加が許可されないことに注意してください。リソースコレクション応答をカスタマイズしたい場合は、コレクションを表す専用のリソースを作成できます:
php artisan make:resource UserCollection
リソースコレクションクラスを定義した後、応答に含めるべきメタデータを簡単に定義できます:
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\ResourceCollection;
class UserCollection extends ResourceCollection
{
/**
* Transform the resource collection into an array.
*
* @return array<int|string, mixed>
*/
public function toArray(Request $request): array
{
return [
'data' => $this->collection,
'links' => [
'self' => 'link-value',
],
];
}
}
リソースコレクションを定義した後、ルートまたはコントローラから返すことができます。
use App\Http\Resources\UserCollection;
use App\Models\User;
Route::get('/users', function () {
return new UserCollection(User::all());
});
コレクションキーの保存
ルートからリソースコレクションを返すとき、Laravel はコレクションのキーを数値順にリセットします。ただし、コレクションの元のキーを保存するかどうかを示す preserveKeys
プロパティをリソースクラスに追加することができます:
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class UserResource extends JsonResource
{
/**
* Indicates if the resource's collection keys should be preserved.
*
* @var bool
*/
public $preserveKeys = true;
}
preserveKeys
プロパティが true
に設定されている場合、コレクションのキーは、ルートやコントローラーから返される際に保存されます:
use App\Http\Resources\UserResource;
use App\Models\User;
Route::get('/users', function () {
return UserResource::collection(User::all()->keyBy->id);
});
基礎となるリソースクラスのカスタマイズ
通常、リソースコレクションの $this->collection
プロパティは、コレクションの各アイテムをその単数形リソースクラスにマッピングした結果で自動的に埋められます。単数形リソースクラスは、クラス名の末尾にある Collection
部分を除いたクラス名として想定されます。また、個人の好みに応じて、単数形リソースクラスに Resource
を付けるかどうかも異なります。
例えば、UserCollection
は、与えられたユーザーインスタンスを UserResource
リソースにマップしようとします。この動作をカスタマイズするには、リソースコレクションの $collects
プロパティをオーバーライドすることができます:
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class UserCollection extends ResourceCollection
{
/**
* The resource that this resource collects.
*
* @var string
*/
public $collects = Member::class;
}
リソースの記述
もし 概念の概要 をまだ読んでいない場合は、このドキュメントを進める前に読むことを強くお勧めします。
リソースは与えられたモデルを配列に変換するだけです。そのため、各リソースには、モデルの属性を API フレンドリーな配列に変換する toArray
メソッドが含まれています。この配列は、アプリケーションのルートやコントローラーから返すことができます:
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class UserResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
];
}
}
リソースが定義されると、それを直接ルートやコントローラーから返すことができます:
use App\Http\Resources\UserResource;
use App\Models\User;
Route::get('/user/{id}', function (string $id) {
return new UserResource(User::findOrFail($id));
});
関連性
リソースの `toArray` メソッドが返す配列に関連リソースを含めたい場合は、この例では、`PostResource` リソースの `collection` メソッドを使用して、ユーザーのブログ投稿をリソースレスポンスに追加します:
use App\Http\Resources\PostResource;
use Illuminate\Http\Request;
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'posts' => PostResource::collection($this->posts),
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
];
}
関連リソースを含めたい場合は、すでに読み込まれている場合にのみ関連を含めることができます。条件付きリレーションシップのドキュメントをご覧ください。
リソースコレクション
リソースは単一のモデルを配列に変換しますが、リソースコレクションは複数のモデルを配列に変換します。ただし、すべてのリソースは「アドホック」リソースコレクションを生成するための collection
メソッドを提供しているため、すべてのモデルに対してリソースコレクションクラスを定義する必要はありません:
use App\Http\Resources\UserResource;
use App\Models\User;
Route::get('/users', function () {
return UserResource::collection(User::all());
});
ただし、コレクションと一緒に返されるメタデータをカスタマイズする必要がある場合は、独自のリソースコレクションを定義する必要があります:
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\ResourceCollection;
class UserCollection extends ResourceCollection
{
/**
* Transform the resource collection into an array.
*
* @return array<string, mixed>
*/
public function toArray(Request $request): array
{
return [
'data' => $this->collection,
'links' => [
'self' => 'link-value',
],
];
}
}
単数のリソースと同様に、リソースコレクションは直接ルートやコントローラから返すことができます:
use App\Http\Resources\UserCollection;
use App\Models\User;
Route::get('/users', function () {
return new UserCollection(User::all());
});
データのラッピング
デフォルトでは、リソースレスポンスが JSON に変換されるとき、最も外側のリソースは data
キーでラップされます。したがって、典型的なリソースコレクションのレスポンスは次のようになります:
{
"data": [
{
"id": 1,
"name": "Eladio Schroeder Sr.",
"email": "therese28@example.com"
},
{
"id": 2,
"name": "Liliana Mayert",
"email": "evandervort@example.com"
}
]
}
最も外側のリソースのラッピングを無効にしたい場合は、基本となる Illuminate\Http\Resources\Json\JsonResource
クラスの withoutWrapping
メソッドを呼び出す必要があります。通常、このメソッドは、アプリケーションの各リクエストで読み込まれる AppServiceProvider
や他のサービスプロバイダから呼び出すべきです:
<?php
namespace App\Providers;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
// ...
}
/**
* Bootstrap any application services.
*/
public function boot(): void
{
JsonResource::withoutWrapping();
}
}
withoutWrapping
メソッドは最も外側のレスポンスにのみ影響し、独自のリソースコレクションに手動で追加した data
キーを削除しません。
ネストされたリソースのラッピング
リソースの関連性をどのようにラップするかは完全に自由です。リソースコレクションを、ネストの有無にかかわらずすべてdata
キーでラップしたい場合は、各リソースに対してリソースコレクションクラスを定義し、コレクションをdata
キー内に返す必要があります。
最も外側のリソースが2つのdata
キーでラップされることになるのではないかと心配しているかもしれません。心配しないでください。Laravelはリソースが誤って2重にラップされることは決してありませんので、変換するリソースコレクションのネストレベルについて心配する必要はありません。
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class CommentsCollection extends ResourceCollection
{
/**
* Transform the resource collection into an array.
*
* @return array<string, mixed>
*/
public function toArray(Request $request): array
{
return ['data' => $this->collection];
}
}
データのラッピングとページネーション
リソースレスポンス経由でページネートされたコレクションを返す場合、LaravelはwithoutWrapping
メソッドが呼び出されていても、リソースデータをdata
キーでラップします。これは、ページネーションされたレスポンスには常に、ページネータの状態に関する情報を含むmeta
およびlinks
キーが含まれるためです。
{
"data": [
{
"id": 1,
"name": "Eladio Schroeder Sr.",
"email": "therese28@example.com"
},
{
"id": 2,
"name": "Liliana Mayert",
"email": "evandervort@example.com"
}
],
"links":{
"first": "http://example.com/users?page=1",
"last": "http://example.com/users?page=1",
"prev": null,
"next": null
},
"meta":{
"current_page": 1,
"from": 1,
"last_page": 1,
"path": "http://example.com/users",
"per_page": 15,
"to": 10,
"total": 10
}
}