Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[Serializer] Fix recursive custom normalizer #19532

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 8, 2024

Conversation

mtarld
Copy link
Contributor

@mtarld mtarld commented Feb 8, 2024

As mentioned in the following issue: symfony/symfony#53708, the example showing how to create a custom normalizer leads to an infinite recursion.

I could have been fixed like that:

class TopicNormalizer implements NormalizerInterface, NormalizerAwareInterface
{
    use NormalizerAwareTrait;

+    private const ALREADY_CALLED = self::class.'_already_called';
+
    public function __construct(
        private UrlGeneratorInterface $router,
    ) {
    }

    public function normalize($topic, string $format = null, array $context = []): array
    {
+       $context[self::ALREADY_CALLED] = true;
+
        $data = $this->normalizer->normalize($topic, $format, $context);

        // Here, add, edit, or delete some data:
        $data['href']['self'] = $this->router->generate('topic_show', [
            'id' => $topic->getId(),
        ], UrlGeneratorInterface::ABSOLUTE_URL);

        return $data;
    }

    public function supportsNormalization($data, string $format = null, array $context = []): bool
    {
+        if ($context[self::ALREADY_CALLED] ?? false) {
+            return false;
+        }
+
        return $data instanceof Topic;
    }

    public function getSupportedTypes(?string $format): array
    {
        return [
-             Topic::class => true,
+             Topic::class => false,
        ];
    }
}

But this will prevent the normalizer to be cacheable (because it depends on the context).

Instead, I dropped the use of NormalizerAwareInterface and NormalizerAwareTrait and used an explicit constructor injection instead.

WDYT?

@javiereguiluz
Copy link
Member

Yes, the solution proposed in this PR looks more elegant than the one you showed in the comment above. Thanks Mathias!

@javiereguiluz javiereguiluz merged commit 9286310 into symfony:7.0 Feb 8, 2024
@mtarld mtarld deleted the fix/custom-normalizer branch February 8, 2024 17:46
@stof
Copy link
Member

stof commented Feb 13, 2024

autowiring only the object normalizer is a bad idea to me, because it assumes that no other normalizer than the ObjectNormalizer should be responsible for the delegated call.

Note that depending on the context for the support here is not that bad: getSupportedTypes already allows caching that anything that is not a Topic won't be supported (even if the support for things that are Topic cannot be cached)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants