Skip to content

Commit 2bc1754

Browse files
committed
Refactor the Serializer context building process
1 parent 9d67c78 commit 2bc1754

File tree

17 files changed

+342
-146
lines changed

17 files changed

+342
-146
lines changed

features/content_negotiation.feature

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,51 @@ Feature: Content Negotiation support
1212
<name>XML!</name>
1313
</root>
1414
"""
15-
Then the header "Content-Type" should be equal to "application/xml"
15+
Then the response status code should be 201
16+
And the header "Content-Type" should be equal to "application/xml"
1617
And the response should be equal to
1718
"""
1819
<?xml version="1.0"?>
1920
<response><id>1</id><name>XML!</name><alias/><description/><dummyDate/><dummyPrice/><jsonData/><relatedDummy/><dummyBoolean/><dummy/><relatedDummies/><nameConverted/></response>
2021
"""
2122

23+
Scenario: Retrieve a collection in XML
24+
When I add "Accept" header equal to "text/xml"
25+
And I send a "GET" request to "/dummies"
26+
Then the response status code should be 200
27+
And the header "Content-Type" should be equal to "text/xml; charset=UTF-8"
28+
And the response should be equal to
29+
"""
30+
<?xml version="1.0"?>
31+
<response><item key="0"><id>1</id><name>XML!</name><alias/><description/><dummyDate/><dummyPrice/><jsonData/><relatedDummy/><dummyBoolean/><dummy/><relatedDummies/><nameConverted/></item></response>
32+
"""
33+
34+
Scenario: Retrieve a collection in JSON
35+
When I add "Accept" header equal to "application/json"
36+
And I send a "GET" request to "/dummies"
37+
Then the response status code should be 200
38+
And the header "Content-Type" should be equal to "application/json"
39+
And the response should be in JSON
40+
And the JSON should be equal to:
41+
"""
42+
[
43+
{
44+
"id": 1,
45+
"name": "XML!",
46+
"alias": null,
47+
"description": null,
48+
"dummyDate": null,
49+
"dummyPrice": null,
50+
"jsonData": [],
51+
"relatedDummy": null,
52+
"dummyBoolean": null,
53+
"dummy": null,
54+
"relatedDummies": [],
55+
"nameConverted": null
56+
}
57+
]
58+
"""
59+
2260
@dropSchema
2361
Scenario: Requesting an unknown format should return JSON-LD
2462
When I add "Accept" header equal to "text/plain"

src/Api/RequestAttributesExtractor.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
* Extracts data used by the library form a Request instance.
1919
*
2020
* @author Kévin Dunglas <[email protected]>
21+
*
22+
* @internal
2123
*/
2224
final class RequestAttributesExtractor
2325
{
@@ -29,19 +31,17 @@ final class RequestAttributesExtractor
2931
*
3032
* @throws RuntimeException
3133
*
32-
* @return array
34+
* @return array [$resourceClass, $collectionOperation, $itemOperation, $format]
3335
*/
3436
public static function extractAttributes(Request $request)
3537
{
3638
$resourceClass = $request->attributes->get('_resource_class');
37-
3839
if (!$resourceClass) {
3940
throw new RuntimeException('The request attribute "_resource_class" must be defined.');
4041
}
4142

4243
$collectionOperation = $request->attributes->get('_collection_operation_name');
4344
$itemOperation = $request->attributes->get('_item_operation_name');
44-
4545
if (!$itemOperation && !$collectionOperation) {
4646
throw new RuntimeException('One of the request attribute "_item_operation_name" or "_collection_operation_name" must be defined.');
4747
}

src/Bridge/Symfony/Bundle/Resources/config/api.xml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@
4343

4444
<service id="api_platform.negotiator" class="Negotiation\Negotiator" public="false" />
4545

46+
<!-- Serializer -->
47+
48+
<service id="api_platform.serializer.context_builder" class="ApiPlatform\Core\Serializer\SerializerContextBuilder" public="false">
49+
<argument type="service" id="api_platform.metadata.resource.metadata_factory" />
50+
</service>
51+
4652
<!-- Resource path generators -->
4753

4854
<service id="api_platform.routing.resource_path_generator.underscore" class="ApiPlatform\Core\Routing\UnderscoreResourcePathGenerator" public="false" />
@@ -60,14 +66,14 @@
6066

6167
<service id="api_platform.listener.view.serializer" class="ApiPlatform\Core\EventListener\SerializerViewListener">
6268
<argument type="service" id="api_platform.serializer" />
63-
<argument type="service" id="api_platform.metadata.resource.metadata_factory" />
69+
<argument type="service" id="api_platform.serializer.context_builder" />
6470

6571
<tag name="kernel.event_listener" event="kernel.view" method="onKernelView" priority="10" />
6672
</service>
6773

6874
<service id="api_platform.listener.view.deserializer" class="ApiPlatform\Core\EventListener\DeserializerViewListener">
6975
<argument type="service" id="api_platform.serializer" />
70-
<argument type="service" id="api_platform.metadata.resource.metadata_factory" />
76+
<argument type="service" id="api_platform.serializer.context_builder" />
7177

7278
<tag name="kernel.event_listener" event="kernel.view" method="onKernelView" priority="30" />
7379
</service>

src/Bridge/Symfony/Bundle/Resources/config/hydra.xml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@
4444
<!-- Serializer -->
4545

4646
<service id="api_platform.hydra.normalizer.collection" class="ApiPlatform\Core\Hydra\Serializer\CollectionNormalizer" public="false">
47-
<argument type="service" id="api_platform.metadata.resource.metadata_factory" />
4847
<argument type="service" id="api_platform.jsonld.context_builder" />
4948
<argument type="service" id="api_platform.resource_class_resolver" />
5049
<argument type="service" id="api_platform.iri_converter" />

src/EventListener/DeserializerViewListener.php

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
use ApiPlatform\Core\Api\RequestAttributesExtractor;
1515
use ApiPlatform\Core\Exception\RuntimeException;
16-
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
16+
use ApiPlatform\Core\Serializer\SerializerContextBuilderInterface;
1717
use Symfony\Component\HttpFoundation\Request;
1818
use Symfony\Component\HttpFoundation\Response;
1919
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
@@ -27,46 +27,41 @@
2727
final class DeserializerViewListener
2828
{
2929
private $serializer;
30-
private $resourceMetadataFactory;
30+
private $serializerContextBuilder;
3131

32-
public function __construct(SerializerInterface $serializer, ResourceMetadataFactoryInterface $resourceMetadataFactory)
32+
public function __construct(SerializerInterface $serializer, SerializerContextBuilderInterface $serializerContextBuilder)
3333
{
3434
$this->serializer = $serializer;
35-
$this->resourceMetadataFactory = $resourceMetadataFactory;
35+
$this->serializerContextBuilder = $serializerContextBuilder;
3636
}
3737

38+
/**
39+
* Deserializes the data sent in the requested format.
40+
*
41+
* @param GetResponseForControllerResultEvent $event
42+
*/
3843
public function onKernelView(GetResponseForControllerResultEvent $event)
3944
{
40-
$data = $event->getControllerResult();
45+
$controllerResult = $event->getControllerResult();
4146
$request = $event->getRequest();
4247

43-
if ($data instanceof Response || !in_array($request->getMethod(), [Request::METHOD_POST, Request::METHOD_PUT], true)) {
48+
if ($controllerResult instanceof Response || !in_array($request->getMethod(), [Request::METHOD_POST, Request::METHOD_PUT], true)) {
4449
return;
4550
}
4651

4752
try {
48-
list($resourceClass, $collectionOperationName, $itemOperationName, $format) = RequestAttributesExtractor::extractAttributes($request);
53+
$extractedAttributes = RequestAttributesExtractor::extractAttributes($request);
4954
} catch (RuntimeException $e) {
5055
return;
5156
}
5257

53-
$resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
54-
55-
if ($collectionOperationName) {
56-
$context = $resourceMetadata->getCollectionOperationAttribute($collectionOperationName, 'denormalization_context', [], true);
57-
$context['collection_operation_name'] = $collectionOperationName;
58-
} else {
59-
$context = $resourceMetadata->getItemOperationAttribute($itemOperationName, 'denormalization_context', [], true);
60-
$context['item_operation_name'] = $itemOperationName;
61-
}
62-
63-
$context['resource_class'] = $resourceClass;
64-
$context['request_uri'] = $request->getRequestUri();
65-
66-
if (null !== $data) {
67-
$context['object_to_populate'] = $data;
58+
$context = $this->serializerContextBuilder->createFromRequest($request, false, $extractedAttributes);
59+
if (null !== $controllerResult) {
60+
$context['object_to_populate'] = $controllerResult;
6861
}
6962

70-
$event->setControllerResult($this->serializer->deserialize($request->getContent(), $resourceClass, $format, $context));
63+
$event->setControllerResult(
64+
$this->serializer->deserialize($request->getContent(), $extractedAttributes[0], $extractedAttributes[3], $context)
65+
);
7166
}
7267
}

src/EventListener/SerializerViewListener.php

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
namespace ApiPlatform\Core\EventListener;
1313

1414
use ApiPlatform\Core\Api\RequestAttributesExtractor;
15-
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
15+
use ApiPlatform\Core\Exception\RuntimeException;
16+
use ApiPlatform\Core\Serializer\SerializerContextBuilderInterface;
1617
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
1718
use Symfony\Component\Serializer\SerializerInterface;
1819

@@ -21,49 +22,38 @@
2122
*
2223
* @author Kévin Dunglas <[email protected]>
2324
*/
24-
class SerializerViewListener
25+
final class SerializerViewListener
2526
{
2627
private $serializer;
27-
private $resourceMetadataFactory;
28+
private $serializerContextBuilder;
2829

29-
public function __construct(SerializerInterface $serializer, ResourceMetadataFactoryInterface $resourceMetadataFactory)
30+
public function __construct(SerializerInterface $serializer, SerializerContextBuilderInterface $serializerContextBuilder)
3031
{
3132
$this->serializer = $serializer;
32-
$this->resourceMetadataFactory = $resourceMetadataFactory;
33+
$this->serializerContextBuilder = $serializerContextBuilder;
3334
}
3435

3536
/**
3637
* Serializes the data to the requested format.
38+
*
39+
* @param GetResponseForControllerResultEvent $event
3740
*/
3841
public function onKernelView(GetResponseForControllerResultEvent $event)
3942
{
4043
$controllerResult = $event->getControllerResult();
4144
$request = $event->getRequest();
42-
$format = $request->attributes->get('_api_format');
4345

44-
if ($controllerResult instanceof Response || !$format) {
46+
if ($controllerResult instanceof Response) {
4547
return;
4648
}
4749

4850
try {
49-
list($resourceClass, $collectionOperationName, $itemOperationName, $format) = RequestAttributesExtractor::extractAttributes($request);
51+
$extractedAttributes = RequestAttributesExtractor::extractAttributes($request);
5052
} catch (RuntimeException $e) {
5153
return;
5254
}
5355

54-
$resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
55-
56-
if ($collectionOperationName) {
57-
$context = $resourceMetadata->getCollectionOperationAttribute($collectionOperationName, 'normalization_context', [], true);
58-
$context['collection_operation_name'] = $collectionOperationName;
59-
} else {
60-
$context = $resourceMetadata->getItemOperationAttribute($itemOperationName, 'normalization_context', [], true);
61-
$context['item_operation_name'] = $itemOperationName;
62-
}
63-
64-
$context['resource_class'] = $resourceClass;
65-
$context['request_uri'] = $request->getRequestUri();
66-
67-
$event->setControllerResult($this->serializer->serialize($controllerResult, $format, $context));
56+
$context = $this->serializerContextBuilder->createFromRequest($request, true, $extractedAttributes);
57+
$event->setControllerResult($this->serializer->serialize($controllerResult, $extractedAttributes[3], $context));
6858
}
6959
}

src/Hydra/Serializer/CollectionFiltersNormalizer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public function normalize($object, $format = null, array $context = [])
6464
return $data;
6565
}
6666

67-
$resourceClass = $this->getResourceClass($this->resourceClassResolver, $object, $context);
67+
$resourceClass = $this->resourceClassResolver->getResourceClass($object, $context['resource_class'] ?? null, true);
6868
$resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
6969

7070
$operationName = $context['collection_operation_name'] ?? null;

src/Hydra/Serializer/CollectionNormalizer.php

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
use ApiPlatform\Core\Exception\RuntimeException;
1818
use ApiPlatform\Core\JsonLd\ContextBuilderInterface;
1919
use ApiPlatform\Core\JsonLd\Serializer\ContextTrait;
20-
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
2120
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
2221
use Symfony\Component\Serializer\SerializerAwareInterface;
2322
use Symfony\Component\Serializer\SerializerAwareTrait;
@@ -35,14 +34,12 @@ final class CollectionNormalizer implements NormalizerInterface, SerializerAware
3534

3635
const FORMAT = 'jsonld';
3736

38-
private $resourceMetadataFactory;
3937
private $contextBuilder;
4038
private $resourceClassResolver;
4139
private $iriConverter;
4240

43-
public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFactory, ContextBuilderInterface $contextBuilder, ResourceClassResolverInterface $resourceClassResolver, IriConverterInterface $iriConverter)
41+
public function __construct(ContextBuilderInterface $contextBuilder, ResourceClassResolverInterface $resourceClassResolver, IriConverterInterface $iriConverter)
4442
{
45-
$this->resourceMetadataFactory = $resourceMetadataFactory;
4643
$this->contextBuilder = $contextBuilder;
4744
$this->resourceClassResolver = $resourceClassResolver;
4845
$this->iriConverter = $iriConverter;
@@ -78,10 +75,9 @@ public function normalize($object, $format = null, array $context = [])
7875
return $data;
7976
}
8077

81-
$resourceClass = $this->getResourceClass($this->resourceClassResolver, $object, $context);
82-
$resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
78+
$resourceClass = $this->resourceClassResolver->getResourceClass($object, $context['resource_class'] ?? null, true);
8379
$data = $this->addJsonLdContext($this->contextBuilder, $resourceClass, $context);
84-
$context = $this->createContext($resourceClass, $resourceMetadata, $context, true);
80+
$context = $this->createContext($resourceClass, $context);
8581

8682
$data['@id'] = $this->iriConverter->getIriFromResourceClass($resourceClass);
8783
$data['@type'] = 'hydra:Collection';

0 commit comments

Comments
 (0)