@@ -213,3 +213,113 @@ final class BookRepresentationProcessor implements ProcessorInterface
213
213
}
214
214
}
215
215
```
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