Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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' }