Skip to content

Commit

Permalink
fix(serializer): dynamic groups should not be cached (#5207)
Browse files Browse the repository at this point in the history
fixes #5202
  • Loading branch information
soyuka authored Nov 23, 2022
1 parent 7d1a4fe commit bd0b05a
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 7 deletions.
11 changes: 11 additions & 0 deletions features/serializer/dynamic_groups.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@!mongodb
Feature: Dynamic serialization context
In order to customize the Resource representation dynamically
As a developer
I should be able to add and remove groups

@createSchema
Scenario:
When I add "Content-Type" header equal to "application/ld+json"
And I send a "GET" request to "/relation_group_impact_on_collections/1"
And the JSON node "related.title" should be equal to "foo"
13 changes: 6 additions & 7 deletions src/Serializer/AbstractItemNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -507,18 +507,17 @@ protected function denormalizeRelation(string $attributeName, ApiProperty $prope
*/
protected function getFactoryOptions(array $context): array
{
$operationCacheKey = ($context['resource_class'] ?? '').($context['operation_name'] ?? '').($context['api_normalize'] ?? '');
if ($operationCacheKey && isset($this->localFactoryOptionsCache[$operationCacheKey])) {
return $this->localFactoryOptionsCache[$operationCacheKey];
}

$options = [];

if (isset($context[self::GROUPS])) {
/* @see https://github.com/symfony/symfony/blob/v4.2.6/src/Symfony/Component/PropertyInfo/Extractor/SerializerExtractor.php */
$options['serializer_groups'] = (array) $context[self::GROUPS];
}

$operationCacheKey = ($context['resource_class'] ?? '').($context['operation_name'] ?? '').($context['api_normalize'] ?? '');
if ($operationCacheKey && isset($this->localFactoryOptionsCache[$operationCacheKey])) {
return $options + $this->localFactoryOptionsCache[$operationCacheKey];
}

// This is a hot spot
if (isset($context['resource_class'])) {
// Note that the groups need to be read on the root operation
Expand All @@ -536,7 +535,7 @@ protected function getFactoryOptions(array $context): array
}
}

return $this->localFactoryOptionsCache[$operationCacheKey] = $options;
return $options + $this->localFactoryOptionsCache[$operationCacheKey] = $options;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
operations: [
new GetCollection(),
new Get(normalizationContext: ['groups' => 'related']),
// This adds a "related" group in the "AddGroupNormalizer"
new Get(uriTemplate: '/custom_normalizer_relation_group_impact_on_collection'),
]
)]
class RelationGroupImpactOnCollection
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace ApiPlatform\Tests\Fixtures\TestBundle\Serializer\Normalizer;

use ApiPlatform\Metadata\Get;
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\RelationGroupImpactOnCollection;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

final class AddGroupNormalizer implements NormalizerAwareInterface, NormalizerInterface
{
use NormalizerAwareTrait;

private const ALREADY_CALLED = 'RELATED_GROUP_IMPACT_ON_COLLECTION_NORMALIZER_ALREADY_CALLED';

public function normalize($object, $format = null, array $context = []): array|string|int|float|bool|\ArrayObject
{
$context[self::ALREADY_CALLED] = true;
if (!($operation = $context['operation'] ?? null)) {
return $this->normalizer->normalize($object, $format, $context);
}

if ($operation instanceof Get && '/custom_normalizer_relation_group_impact_on_collection' === $operation->getUriTemplate()) {
$context['groups'] = ['related'];
}

return $this->normalizer->normalize($object, $format, $context);
}

public function supportsNormalization($data, $format = null, array $context = []): bool
{
// Make sure we're not called twice
if (isset($context[self::ALREADY_CALLED])) {
return false;
}

return $data instanceof RelationGroupImpactOnCollection;
}
}
4 changes: 4 additions & 0 deletions tests/Fixtures/app/config/config_common.yml
Original file line number Diff line number Diff line change
Expand Up @@ -415,3 +415,7 @@ services:
tags:
- { name: 'api_platform.state_processor' }

ApiPlatform\Tests\Fixtures\TestBundle\Serializer\Normalizer\AddGroupNormalizer:
tags:
- { name: 'serializer.normalizer' }

0 comments on commit bd0b05a

Please sign in to comment.