Skip to content

Commit 075913d

Browse files
authored
feat: add a documentation about the DataTransformerInitializer (#1440)
1 parent 52f8cb4 commit 075913d

File tree

1 file changed

+83
-4
lines changed

1 file changed

+83
-4
lines changed

core/dto.md

Lines changed: 83 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Using Data Transfer Objects (DTOs)
22

3-
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.
3+
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.
44

55
However, it's sometimes useful to use a specific class to represent the input or output data structure related to an operation.
66

@@ -81,7 +81,8 @@ We have the following `BookInput`:
8181

8282
namespace App\Dto;
8383

84-
final class BookInput {
84+
final class BookInput
85+
{
8586
/**
8687
* @var string
8788
*/
@@ -110,6 +111,7 @@ final class BookInputDataTransformer implements DataTransformerInterface
110111
{
111112
$book = new Book();
112113
$book->isbn = $data->isbn;
114+
113115
return $book;
114116
}
115117

@@ -148,7 +150,8 @@ To manage the output, it's exactly the same process. For example, we have the fo
148150

149151
namespace App\Dto;
150152

151-
final class BookOutput {
153+
final class BookOutput
154+
{
152155
/**
153156
* @var string
154157
*/
@@ -177,6 +180,7 @@ final class BookOutputDataTransformer implements DataTransformerInterface
177180
{
178181
$output = new BookOutput();
179182
$output->name = $data->name;
183+
180184
return $output;
181185
}
182186

@@ -219,7 +223,8 @@ With the following `BookInput`:
219223

220224
namespace App\Dto;
221225

222-
final class BookInput {
226+
final class BookInput
227+
{
223228
/**
224229
* @var \App\Entity\Author
225230
*/
@@ -248,6 +253,7 @@ final class BookInputDataTransformer implements DataTransformerInterface
248253
{
249254
$existingBook = $context[AbstractItemNormalizer::OBJECT_TO_POPULATE];
250255
$existingBook->author = $data->author;
256+
251257
return $existingBook;
252258
}
253259

@@ -274,6 +280,79 @@ services:
274280
#tags: [ 'api_platform.data_transformer' ]
275281
```
276282

283+
## Initialize the Input DTO For Partial Update
284+
285+
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.
286+
287+
This way, the input DTO will be correctly validated with its old data and partial new data.
288+
289+
Create a class implementing the `DataTransformerInitializerInterface` instead of the `DataTransformerInterface`:
290+
291+
```php
292+
<?php
293+
// src/DataTransformer/BookInputDataTransformerInitializer.php
294+
295+
namespace App\DataTransformer;
296+
297+
use ApiPlatform\Core\DataTransformer\DataTransformerInitializerInterface;
298+
use ApiPlatform\Core\Serializer\AbstractItemNormalizer;
299+
use App\Entity\Book;
300+
use App\Dto\BookInput;
301+
302+
final class BookInputDataTransformerInitializer implements DataTransformerInitializerInterface
303+
{
304+
/**
305+
* {@inheritdoc}
306+
*/
307+
public function transform($data, string $to, array $context = [])
308+
{
309+
$existingBook = $context[AbstractItemNormalizer::OBJECT_TO_POPULATE];
310+
$existingBook->author = $data->author;
311+
312+
return $existingBook;
313+
}
314+
315+
/**
316+
* {@inheritdoc}
317+
*/
318+
public function initialize(string $inputClass, array $context = [])
319+
{
320+
$existingBook = $context[AbstractItemNormalizer::OBJECT_TO_POPULATE] ?? null;
321+
if (!$existingBook) {
322+
return new BookInput();
323+
}
324+
325+
$bookInput = new BookInput();
326+
$bookInput->author = $existingBook->author;
327+
328+
return $bookInput;
329+
}
330+
331+
/**
332+
* {@inheritdoc}
333+
*/
334+
public function supportsTransformation($data, string $to, array $context = []): bool
335+
{
336+
if ($data instanceof Book) {
337+
return false;
338+
}
339+
340+
return Book::class === $to && null !== ($context['input']['class'] ?? null);
341+
}
342+
}
343+
```
344+
345+
Register it:
346+
347+
```yaml
348+
# api/config/services.yaml
349+
services:
350+
# ...
351+
'App\DataTransformer\BookInputDataTransformerInitializer': ~
352+
# Uncomment only if autoconfiguration is disabled
353+
#tags: [ 'api_platform.data_transformer' ]
354+
```
355+
277356
## Disabling the Input or the Output
278357

279358
Both the `input` and the `output` attributes can be set to `false`. If `input` is `false`, the deserialization process

0 commit comments

Comments
 (0)