# 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:

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 API Platform 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.

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' ]
```
## Initialize the Input DTO For Partial Update
In order to be able to do a partial update (`PATCH`), it is needed to initialize the input DTO with the existing data before the deserialization process.
This way, the input DTO will be correctly validated with its old data and partial new data.
Create a class implementing the `DataTransformerInitializerInterface` instead of the `DataTransformerInterface`:
```php
author = $data->author;
return $existingBook;
}
/**
* {@inheritdoc}
*/
public function initialize(string $inputClass, array $context = [])
{
$existingBook = $context[AbstractItemNormalizer::OBJECT_TO_POPULATE] ?? null;
if (!$existingBook) {
return new BookInput();
}
$bookInput = new BookInput();
$bookInput->author = $existingBook->author;
return $bookInput;
}
/**
* {@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);
}
}
```
Register it:
```yaml
# api/config/services.yaml
services:
# ...
'App\DataTransformer\BookInputDataTransformerInitializer': ~
# 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
[
"method" => "POST",
"input" => CreateBook::class,
"output" => BookOutput::class
],
],
itemOperations: [
"update" => [
"method" => "PUT",
"input" => UpdateBook::class,
"output" => BookOutput::class,
],
],
)]
final class Book
{
}
```
```yaml
# api/config/api_platform/resources.yaml
resources:
App\Entity\Book:
collectionOperations:
create:
method: POST
input: App\Dto\CreateBook
output: App\Dto\BookOutput
itemOperations:
update:
method: PUT
input: App\Dto\UpdateBook
output: App\Dto\BookOutput
```
```xml
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);
}
}
```