Skip to content

Commit 3811502

Browse files
committed
Merge 3.2
2 parents 5448f9c + 2a7ba96 commit 3811502

25 files changed

+486
-219
lines changed

admin/schema.org.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ To configure which property should be shown to represent your entity, map the pr
1818
// api/src/Entity/Person.php
1919
...
2020

21-
#[ApiProperty(types: ["https://schema.org/name"])]
21+
#[ApiProperty(iris: ["https://schema.org/name"])]
2222
private $name;
2323

2424
...

core/dto.md

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Using an input, the request body will be denormalized to the input instead of yo
1111
```php
1212
<?php
1313
// api/src/Dto/UserResetPasswordDto.php
14+
1415
namespace App\Dto;
1516

1617
use Symfony\Component\Validator\Constraints as Assert;
@@ -25,6 +26,7 @@ final class UserResetPasswordDto
2526
```php
2627
<?php
2728
// api/src/Model/User.php
29+
2830
namespace App\Model;
2931

3032
use ApiPlatform\Metadata\Post;
@@ -39,6 +41,7 @@ And the processor:
3941

4042
```php
4143
<?php
44+
// api/src/State/UserResetPasswordProcessor.php
4245

4346
namespace App\State;
4447

@@ -47,12 +50,17 @@ use ApiPlatform\Metadata\Operation;
4750
use ApiPlatform\State\ProcessorInterface;
4851
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
4952

53+
/**
54+
* @implements ProcessorInterface<UserResetPasswordDto, User>
55+
*/
5056
final class UserResetPasswordProcessor implements ProcessorInterface
5157
{
5258
/**
5359
* @param UserResetPasswordDto $data
60+
*
61+
* @throws NotFoundHttpException
5462
*/
55-
public function process($data, Operation $operation, array $uriVariables = [], array $context = [])
63+
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): User
5664
{
5765
if ('[email protected]' === $data->email) {
5866
return new User(email: $data->email, id: 1);
@@ -71,6 +79,7 @@ Let's use a message that will be processed by [Symfony Messenger](https://symfon
7179

7280
```php
7381
<?php
82+
// api/src/Model/SendMessage.php
7483

7584
namespace App\Model;
7685

@@ -90,6 +99,7 @@ To return another representation of your data in a [State Provider](./state-prov
9099

91100
```php
92101
<?php
102+
// api/src/Entity/Book.php
93103

94104
namespace App\Entity;
95105

@@ -103,6 +113,7 @@ class Book {}
103113

104114
```php
105115
<?php
116+
// api/src/State/BookRepresentationProvider.php
106117

107118
namespace App\State;
108119

@@ -111,9 +122,12 @@ use App\Model\Book;
111122
use ApiPlatform\Metadata\Operation;
112123
use ApiPlatform\State\ProviderInterface;
113124

125+
/**
126+
* @implements ProviderInterface<AnotherRepresentation>
127+
*/
114128
final class BookRepresentationProvider implements ProviderInterface
115129
{
116-
public function provide(Operation $operation, array $uriVariables = [], array $context = [])
130+
public function provide(Operation $operation, array $uriVariables = [], array $context = []): AnotherRepresentation
117131
{
118132
return new AnotherRepresentation();
119133
}
@@ -128,6 +142,7 @@ For returning another representation of your data in a [State Processor](./state
128142

129143
```php
130144
<?php
145+
// api/src/Entity/Book.php
131146

132147
namespace App\Entity;
133148

@@ -171,6 +186,7 @@ Here the `$data` attribute represents an instance of your resource.
171186

172187
```php
173188
<?php
189+
// api/src/State/BookRepresentationProcessor.php
174190

175191
namespace App\State;
176192

@@ -179,12 +195,15 @@ use ApiPlatform\State\ProcessorInterface;
179195
use App\Dto\AnotherRepresentation;
180196
use App\Model\Book;
181197

198+
/**
199+
* @implements ProcessorInterface<Book, AnotherRepresentation>
200+
*/
182201
final class BookRepresentationProcessor implements ProcessorInterface
183202
{
184203
/**
185204
* @param Book $data
186205
*/
187-
public function process($data, Operation $operation, array $uriVariables = [], array $context = [])
206+
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): AnotherRepresentation
188207
{
189208
return new AnotherRepresentation(
190209
$data->getId(),

core/errors.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ This can also be configured on an `ApiResource` or in an `HttpOperation`, for ex
2525

2626
## Exception status code decision
2727

28-
There are many ways of configuring the exception status code we recommend reading the guides on how to use an [Error Provider](/docs/guides/error-provider) or create an [Error Resource](/docs/guides/error-resource).
28+
There are many ways of configuring the exception status code we recommend reading the guides on how to use an [Error Provider](https://api-platform.com/docs/guides/error-provider/) or create an [Error Resource](https://api-platform.com/docs/guides/error-resource/).
2929

3030
1. we look at `exception_to_status` and take one if there's a match
3131
2. If your exception is a `Symfony\Component\HttpKernel\Exception\HttpExceptionInterface` we get its status.

core/extensions.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,12 @@ use App\Entity\Offer;
6565
use Doctrine\ORM\QueryBuilder;
6666
use Symfony\Bundle\SecurityBundle\Security;
6767

68-
final class CurrentUserExtension implements QueryCollectionExtensionInterface, QueryItemExtensionInterface
68+
final readonly class CurrentUserExtension implements QueryCollectionExtensionInterface, QueryItemExtensionInterface
6969
{
7070

71-
public function __construct(private readonly Security $security)
71+
public function __construct(
72+
private Security $security,
73+
)
7274
{
7375
}
7476

core/file-upload.md

Lines changed: 60 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -169,43 +169,53 @@ A [normalizer](serialization.md#normalization) could be used to set the `content
169169

170170
```php
171171
<?php
172-
// api/src/Serializer/MediaObjectNormalizer.php
173172
174173
namespace App\Serializer;
175174
176175
use App\Entity\MediaObject;
177-
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
178-
use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;
179176
use Vich\UploaderBundle\Storage\StorageInterface;
177+
use Symfony\Component\DependencyInjection\Attribute\Autowire;
178+
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
180179
181-
final class MediaObjectNormalizer implements NormalizerAwareInterface
180+
class MediaObjectNormalizer implements NormalizerInterface
182181
{
183-
use NormalizerAwareTrait;
184182
185-
private const ALREADY_CALLED = 'MEDIA_OBJECT_NORMALIZER_ALREADY_CALLED';
183+
private const ALREADY_CALLED = 'MEDIA_OBJECT_NORMALIZER_ALREADY_CALLED';
186184
187-
public function __construct(private StorageInterface $storage)
188-
{
189-
}
185+
public function __construct(
186+
#[Autowire(service: 'serializer.normalizer.object')]
187+
private readonly NormalizerInterface $normalizer,
188+
private readonly StorageInterface $storage
189+
) {
190+
}
190191
191-
public function normalize($object, ?string $format = null, array $context = []): array|string|int|float|bool|\ArrayObject|null
192-
{
193-
$context[self::ALREADY_CALLED] = true;
192+
public function normalize($object, ?string $format = null, array $context = []): array|string|int|float|bool|\ArrayObject|null
193+
{
194+
$context[self::ALREADY_CALLED] = true;
194195
195-
$object->contentUrl = $this->storage->resolveUri($object, 'file');
196+
$object->contentUrl = $this->storage->resolveUri($object, 'file');
196197
197-
return $this->normalizer->normalize($object, $format, $context);
198-
}
198+
return $this->normalizer->normalize($object, $format, $context);
199+
}
199200
200-
public function supportsNormalization($data, ?string $format = null, array $context = []): bool
201-
{
202-
if (isset($context[self::ALREADY_CALLED])) {
203-
return false;
204-
}
201+
public function supportsNormalization($data, ?string $format = null, array $context = []): bool
202+
{
205203
206-
return $data instanceof MediaObject;
204+
if (isset($context[self::ALREADY_CALLED])) {
205+
return false;
207206
}
207+
208+
return $data instanceof MediaObject;
209+
}
210+
211+
public function getSupportedTypes(?string $format): array
212+
{
213+
return [
214+
MediaObject::class => true,
215+
];
216+
}
208217
}
218+
209219
```
210220

211221
### Making a Request to the `/media_objects` Endpoint
@@ -234,8 +244,8 @@ You will need to modify your `Caddyfile` to allow the above `contentUrl` to be a
234244
@pwa expression `(
235245
header({'Accept': '*text/html*'})
236246
&& !path(
237-
- '/docs*', '/graphql*', '/bundles*', '/media*', '/contexts*', '/_profiler*', '/_wdt*',
238-
+ '/media*', '/docs*', '/graphql*', '/bundles*', '/media*', '/contexts*', '/_profiler*', '/_wdt*',
247+
- '/docs*', '/graphql*', '/bundles*', '/contexts*', '/_profiler*', '/_wdt*',
248+
+ '/media*', '/docs*', '/graphql*', '/bundles*', '/contexts*', '/_profiler*', '/_wdt*',
239249
'*.json*', '*.html', '*.csv', '*.yml', '*.yaml', '*.xml'
240250
)
241251
)
@@ -313,25 +323,26 @@ class MediaObjectTest extends ApiTestCase
313323

314324
public function testCreateAMediaObject(): void
315325
{
316-
$file = new UploadedFile('fixtures/files/image.png', 'image.png');
326+
// The file "image.jpg" is the folder fixtures which is in the project dir
327+
$file = new UploadedFile(__DIR__ . '/../fixtures/image.jpg', 'image.jpg');
317328
$client = self::createClient();
318329

319-
$client->request('POST', '/media_objects', [
320-
'headers' => ['Content-Type' => 'multipart/form-data'],
321-
'extra' => [
322-
// If you have additional fields in your MediaObject entity, use the parameters.
323-
'parameters' => [
324-
'title' => 'My file uploaded',
325-
],
326-
'files' => [
327-
'file' => $file,
328-
],
329-
]
330+
$client->request('POST', 'http://localhost:8888/api/media_objects', [
331+
'headers' => ['Content-Type' => 'multipart/form-data'],
332+
'extra' => [
333+
// If you have additional fields in your MediaObject entity, use the parameters.
334+
'parameters' => [
335+
// 'title' => 'title'
336+
],
337+
'files' => [
338+
'file' => $file,
339+
],
340+
]
330341
]);
331342
$this->assertResponseIsSuccessful();
332343
$this->assertMatchesResourceItemJsonSchema(MediaObject::class);
333344
$this->assertJsonContains([
334-
'title' => 'My file uploaded',
345+
// 'title' => 'My file uploaded',
335346
]);
336347
}
337348
}
@@ -451,19 +462,28 @@ We also need to make sure the field containing the uploaded file is not denormal
451462

452463
namespace App\Serializer;
453464

454-
use Symfony\Component\HttpFoundation\File\UploadedFile;
465+
use Symfony\Component\HttpFoundation\File\File;
455466
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
456467

457468
final class UploadedFileDenormalizer implements DenormalizerInterface
458469
{
459-
public function denormalize($data, string $type, string $format = null, array $context = []): UploadedFile
470+
public function denormalize($data, string $type, string $format = null, array $context = []): File
460471
{
461472
return $data;
462473
}
463474

464-
public function supportsDenormalization($data, $type, $format = null): bool
475+
public function supportsDenormalization($data, $type, $format = null, array $context = []): bool
476+
{
477+
return $data instanceof File;
478+
}
479+
480+
public function getSupportedTypes(?string $format): array
465481
{
466-
return $data instanceof UploadedFile;
482+
return [
483+
'object' => null,
484+
'*' => false,
485+
File::class => true,
486+
];
467487
}
468488
}
469489
```

0 commit comments

Comments
 (0)