Using Data Transfer Objects (DTOs)

As stated in the general design considerations, in most cases the DTO pattern should be implemented using an API Resource class representing the public data model exposed through the API and a custom State Provider. In such cases, the class marked with #[ApiResource] will act as a DTO.

一般的な設計上の考慮事項で述べたように、ほとんどの場合、DTO パターンは、API およびカスタム状態プロバイダーを通じて公開されるパブリック データ モデルを表す API リソース クラスを使用して実装する必要があります。このような場合、#[ApiResource] でマークされたクラスが DTO として機能します。

However, it's sometimes useful to use a specific class to represent the input or output data structure related to an operation. These techniques are very useful to document your API properly (using Hydra or OpenAPI) and will often be used on POST operations.

ただし、特定のクラスを使用して、操作に関連する入力または出力データ構造を表すと便利な場合があります。これらの手法は、(Hydra または OpenAPI を使用して) API を適切に文書化するのに非常に役立ち、POST 操作でよく使用されます。

Implementing a Write Operation With an Input Different From the Resource

Using an input, the request body will be denormalized to the input instead of your resource class.

入力を使用すると、リクエスト本文はリソース クラスではなく入力に非正規化されます。

<?php
// api/src/Dto/UserResetPasswordDto.php
namespace App\Dto;

use Symfony\Component\Validator\Constraints as Assert;

final class UserResetPasswordDto
{
    #[Assert\Email]
    public $email;
}
<?php
// api/src/Model/User.php
namespace App\Model;

use ApiPlatform\Metadata\Post;
use App\Dto\UserResetPasswordDto;
use App\State\UserResetPasswordProcessor;

#[Post(input: UserResetPasswordDto::class, processor: UserResetPasswordProcessor::class)]
final class User {}

And the processor:

そしてプロセッサ:

<?php

namespace App\State;

use App\Dto\UserResetPasswordDto;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

final class UserResetPasswordProcessor implements ProcessorInterface
{
    /**
     * @param UserResetPasswordDto $data
     */
    public function process($data, Operation $operation, array $uriVariables = [], array $context = [])
    {
        if ('user@example.com' === $data->email) {
            return new User(email: $data->email, id: 1);
        }

        throw new NotFoundHttpException();
    }
}

In some cases, using an input DTO is a way to avoid serialization groups.

場合によっては、入力 DTO を使用すると、シリアライゼーション グループを回避できます。

Use Messenger With an Input DTO

Let's use a message that will be processed by Symfony Messenger. API Platform has an integration with messenger, to use a DTO as input you need to specify the input attribute:

Symfony Messenger によって処理されるメッセージを使用しましょう。 API プラットフォームにはメッセンジャーとの統合があり、DTO を入力として使用するには、入力属性を指定する必要があります。

<?php

namespace App\Model;

use ApiPlatform\Metadata\Post;
use ApiPlatform\Symfony\Messenger\Processor as MessengerProcessor;
use App\Dto\Message;

#[Post(input: Message::class, processor: MessengerProcessor::class)]
class SendMessage {}

This will dispatch the App\Dto\Message via Symfony Messenger.

これにより、Symfony Messenger を介して App\Dto\Message がディスパッチされます。

Implementing a Read Operation With an Output Different From the Resource

To return another representation of your data in a State Provider we advise to specify the output attribute of the resource. Note that this technique works without any changes to the resource but your API documentation would be wrong.

State Provider でデータの別の表現を返すには、リソースの出力属性を指定することをお勧めします。この手法はリソースを変更しなくても機能しますが、API ドキュメントは間違っていることに注意してください。

<?php

namespace App\Entity;

use ApiPlatform\Metadata\Get;
use App\Dto\AnotherRepresentation;
use App\State\BookRepresentationProvider;

#[Get(output: AnotherRepresentation::class, provider: BookRepresentationProvider::class)]
class Book {}
<?php

namespace App\State;

use App\Dto\AnotherRepresentation;
use App\Model\Book;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;

final class BookRepresentationProvider implements ProviderInterface
{
    public function provide(Operation $operation, array $uriVariables = [], array $context = [])
    {
        return new AnotherRepresentation();
    }
}