Search code examples
restsymfonyserializationfosrestbundlecircular-reference

Symfony serializer - set circular reference global


Is there any way to set the circular reference limit in the serializer component of Symfony (not JMSSerializer) with any config or something like that?

I have a REST Application with FOSRestBundle and some Entities that contain other entities which should be serialized too. But I'm running into circular reference errors.

I know how to set it like this:

$encoder    = new JsonEncoder();
$normalizer = new ObjectNormalizer();

$normalizer->setCircularReferenceHandler(function ($object) {
     return $object->getName();
});

But this has to be done in more than one controller (overhead for me). I want to set it globally in the config (.yml) e.g. like this:

framework: 
    serializer:
        enabled: true
        circular_limit: 5

Found no serializer API reference for this so I wonder is it possible or not?


Solution

  • The only way I've found is to create your own object normalizer to add the circular reference handler.

    A minimal working one can be:

    <?php
    
    namespace AppBundle\Serializer\Normalizer;
    
    use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
    use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
    use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
    use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
    use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
    
    class AppObjectNormalizer extends ObjectNormalizer
    {
        public function __construct(ClassMetadataFactoryInterface $classMetadataFactory = null, NameConverterInterface $nameConverter = null, PropertyAccessorInterface $propertyAccessor = null, PropertyTypeExtractorInterface $propertyTypeExtractor = null)
        {
            parent::__construct($classMetadataFactory, $nameConverter, $propertyAccessor, $propertyTypeExtractor);
    
            $this->setCircularReferenceHandler(function ($object) {
                return $object->getName();
            });
        }
    }
    

    Then declare as a service with a slithly higher priority than the default one (which is -1000):

    <service
        id="app.serializer.normalizer.object"
        class="AppBundle\Serializer\Normalizer\AppObjectNormalizer"
        public="false"
        parent="serializer.normalizer.object">
    
        <tag name="serializer.normalizer" priority="-500" />
    </service>
    

    This normalizer will be used by default everywhere in your project.