# Using Data Transfer Objects (DTOs) As stated in [the general design considerations](design.md), in most cases [the DTO pattern](https://en.wikipedia.org/wiki/Data_transfer_object) should be implemented using an API Resource class representing the public data model exposed through the API and [a custom data provider](data-providers.md). In such cases, the class marked with `@ApiResource` will act as a DTO. However, it's sometimes useful to use a specific class to represent the input or output data structure related to an operation. ## Specifying an Input or an Output Data Representation For a given resource class, you may want to have a different representation of this class as input (write) or output (read). To do so, a resource can take an input and/or an output class: ```php App\Dto\BookInput App\Dto\BookOutput ``` The `input` attribute is used during [the deserialization process](serialization.md), when transforming the user-provided data to a resource instance. Similarly, the `output` attribute is used during [the serialization process](serialization.md). This class represents how the `Book` resource will be represented in the `Response`. The `input` and `output` attributes are taken into account by all the documentation generators (GraphQL and OpenAPI, Hydra). Note that `Book` entity needs an id property. The simplest way is adding a public property called `$id`, as in the example. However, as in any other entity, you can use a private property, with getter and setter functions, and/or named it as you wish, provided you annotate it with `@ApiProperty(identifier=true)`. For instance, you could have a property called `$code`. So the `InputDataTransformer` actually transforms the isbn into a code. And then in the `OutputDataTransformer`, from this code into the name. To create a `Book`, we `POST` a data structure corresponding to the `BookInput` class and get back in the response a data structure corresponding to the `BookOutput` class: ![Diagram post input output](images/diagrams/api-platform-post-i-o.svg) To simplify object transformations we have to implement a Data Transformer that will convert the input into a resource or a resource into an output. We have the following `BookInput`: ```php isbn = $data->isbn; return $book; } /** * {@inheritdoc} */ public function supportsTransformation($data, string $to, array $context = []): bool { // in the case of an input, the value given here is an array (the JSON decoded). // if it's a book we transformed the data already if ($data instanceof Book) { return false; } return Book::class === $to && null !== ($context['input']['class'] ?? null); } } ``` We now register it: ```yaml # api/config/services.yaml services: # ... 'App\DataTransformer\BookInputDataTransformer': ~ # Uncomment only if autoconfiguration is disabled #tags: [ 'api_platform.data_transformer' ] ``` To manage the output, it's exactly the same process. For example, we have the following `BookOutput`: ```php name = $data->name; return $output; } /** * {@inheritdoc} */ public function supportsTransformation($data, string $to, array $context = []): bool { return BookOutput::class === $to && $data instanceof Book; } } ``` We now register it: ```yaml # api/config/services.yaml services: # ... 'App\DataTransformer\BookOutputDataTransformer': ~ # Uncomment only if autoconfiguration is disabled #tags: [ 'api_platform.data_transformer' ] ``` ## Updating a Resource with a Custom Input When performing an update (e.g. `PUT` operation), the resource to be updated is read by ApiPlatform before the deserialization phase. To do so, it uses a [data provider](data-providers.md) with the `:id` parameter given in the URL. The *body* of the request is the JSON object sent by the client, it is deserialized and is used to update the previously found resource. ![Diagram put input output](images/diagrams/api-platform-put-i-o.svg) Now, we will update our resource by using a different input representation. With the following `BookInput`: ```php author = $data->author; return $existingBook; } /** * {@inheritdoc} */ public function supportsTransformation($data, string $to, array $context = []): bool { if ($data instanceof Book) { return false; } return Book::class === $to && null !== ($context['input']['class'] ?? null); } } ``` ```yaml # api/config/services.yaml services: # ... 'App\DataTransformer\BookInputDataTransformer': ~ # Uncomment only if autoconfiguration is disabled #tags: [ 'api_platform.data_transformer' ] ``` ## Disabling the Input or the Output Both the `input` and the `output` attributes can be set to `false`. If `input` is `false`, the deserialization process will be skipped. If `output` is `false`, the serialization process will be skipped. ## Per Operation `input` and `output` `input` and `output` attributes can be set on a per operation basis: ```php POST App\Dto\CreateBook App\Dto\BookOutput PUT App\Dto\UpdateBook App\Dto\BookOutput ``` ## Input/Output Metadata When specified, `input` and `output` attributes support: - a string representing the class to use - a falsy boolean to disable them - an array to specify more metadata for example `['class' => BookInput::class, 'name' => 'BookInput', 'iri' => '/book_input']` ## Using Objects As Relations Inside Resources Because API Platform can (de)normalize anything in the supported formats (`jsonld`, `jsonapi`, `hal`, etc.), you can use any object you want inside resources. For example, let's say that the `Book` has an `attribute` property that can't be represented by a resource, we can do the following: ```php validator = $validator; } /** * {@inheritdoc} */ public function transform($data, string $to, array $context = []): Book { $this->validator->validate($data); $book = new Book(); $book->isbn = $data->isbn; return $book; } /** * {@inheritdoc} */ public function supportsTransformation($data, string $to, array $context = []): bool { if ($data instanceof Book) { return false; } return Book::class === $to && null !== ($context['input']['class'] ?? null); } } ```