Skip to content

Commit ecf50e4

Browse files
committed
docs(core/dtos): improve by adding other usages
1 parent c7cd42c commit ecf50e4

File tree

1 file changed

+110
-0
lines changed

1 file changed

+110
-0
lines changed

core/dto.md

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,3 +213,113 @@ final class BookRepresentationProcessor implements ProcessorInterface
213213
}
214214
}
215215
```
216+
217+
## Implementing one custom state provider with DTOs to support Get and GetCollection methods
218+
219+
We have a state provider named `BookProvider` which retrieves our data to serve them in our API using DTOs.
220+
If we want to combine both Get and GetCollection methods into a single state provider as follows:
221+
222+
```php
223+
<?php
224+
// api/src/Resource/Book.php with Symfony or app/Resource/Book.php with Laravel
225+
226+
declare(strict_types=1);
227+
228+
namespace App\Resource;
229+
230+
use ApiPlatform\Metadata\ApiResource;
231+
use App\State\BookProvider;
232+
use ApiPlatform\Metadata\Get;
233+
use ApiPlatform\Metadata\GetCollection;
234+
use Doctrine\ORM\Mapping as ORM;
235+
use Symfony\Component\Validator\Constraints as Assert;
236+
237+
#[Get(provider: BookProvider::class)]
238+
#[GetCollection (provider: BookProvider::class)]
239+
final class Book
240+
{
241+
#[ORM\Id]
242+
#[ORM\GeneratedValue]
243+
#[ORM\Column(type: 'integer')]
244+
private ?int $id = null;
245+
246+
#[ORM\Column(type: 'string', length: 255)]
247+
#[Assert\NotBlank]
248+
private string $title;
249+
250+
// Other fields, accessors and mutators...
251+
}
252+
```
253+
254+
To do that, we can create a state provider for both cases using this:
255+
256+
```php
257+
<?php
258+
// api/src/State/BookProvider.php with Symfony or app/State/BookProvider.php with Laravel
259+
260+
declare(strict_types=1);
261+
262+
namespace App\Resource;
263+
264+
use ApiPlatform\Metadata\ApiResource;
265+
use ApiPlatform\State\ProviderInterface;
266+
use ApiPlatform\Doctrine\Orm\State\CollectionProvider;
267+
268+
final class BookProvider implements ProviderInterface
269+
{
270+
public function __construct(
271+
#[Autowire(service: CollectionProvider::class)]
272+
private readonly ProviderInterface $collectionProvider,
273+
) {}
274+
275+
public function provide(Operation $operation, array $uriVariables = [], array $context = []): ?PostalAddress
276+
{
277+
$resources = $this->collectionProvider->provide($operation, $uriVariables, $context);
278+
279+
$dtos = [];
280+
foreach ($resources as $resource){
281+
$dtos[] = new Book(
282+
$resource->id,
283+
$resource->title,
284+
// ...
285+
)
286+
}
287+
288+
return $dtos;
289+
}
290+
}
291+
```
292+
293+
> [!TIP]
294+
> For more information and other advanced options, refer to the [screencast here](#using-data-transfer-objects-dtos).
295+
296+
> [!NOTICE]
297+
> As a best practice, a class should have only one primary responsibility.
298+
> Combining multiple distinct actions in a single class can make it harder to maintain, test, and understand.
299+
> Strive for classes with a single, well-defined purpose.
300+
301+
## DTO without response
302+
303+
Sometimes we need DTOs to process something, but we don't need to send an object in response.
304+
Assuming that, we can disable the output for our API Resource using the following code:
305+
306+
```php
307+
<?php
308+
// api/src/Resource/Book.php with Symfony or app/Resource/Book.php with Laravel
309+
310+
declare(strict_types=1);
311+
312+
namespace App\Resource;
313+
314+
use ApiPlatform\Metadata\ApiResource;
315+
use App\Dto\BookInput;
316+
317+
#[ApiResource(
318+
input: BookInput::class,
319+
output: false, // Disables the response
320+
)]
321+
final class Book
322+
{
323+
// ...
324+
}
325+
```

0 commit comments

Comments
 (0)