3

I have to serialize an object and I get the ever so common "circular reference error"

I have used the old Symfony method :

$normalizer = new ObjectNormalizer();
// Add Circular reference handler
$normalizer->setCircularReferenceHandler(function ($object) {
    return $object->getId();
});
$normalizers = array($normalizer);
$encoders = [new JsonEncoder()];
$serializer = new Serializer($normalizers, $encoders);

This work but as of Symfony 4.2 I get the exception you see in the title of this question :

use the "circular_reference_handler" key of the context instead Symfony 4.2

I cannot find any reference to this in the Symfony documentation concerning the Serializer.

https://symfony.com/doc/current/components/serializer.html#handling-circular-references

Does any one know how to use this "key of context" instead of the old method?

yivi
  • 42,438
  • 18
  • 116
  • 138
Dennis de Best
  • 1,078
  • 13
  • 30

5 Answers5

7

Unfortunately it is a bit hidden away in the docs, but you can create a class instead of using an anonymous function and then configure the serializer to use this service by default.

It is part of the configuration reference: https://symfony.com/doc/current/reference/configuration/framework.html#circular-reference-handler

# config/packages/serializer.yaml

serializer:
    circular_reference_handler: 'App\Serializer\MyCircularReferenceHandler'

There is no interface specified. Instead the class needs to be invokable. So in your case it could look like this:

class MyCircularReferenceHandler
{
    public function __invoke($object)
    {
        return $object->id;
    }
}
arkuuu
  • 619
  • 7
  • 23
dbrumann
  • 16,803
  • 2
  • 42
  • 58
  • Thanks for the reply. However how am I supposed to then use this method ? I just forgo the whole $normalizer->setCircularReferenceHandler all together ? – Dennis de Best Feb 12 '19 at 08:36
  • 2
    @DennisdeBest yes you can remove `setCircularReferenceHandler`. the config sets the handler which will be automatically called if a circular reference is detected. – ᴄʀᴏᴢᴇᴛ Feb 12 '19 at 08:43
  • Ok I was a bit confused as to where to put the circular_reference_handler key turns out it's in framework.yaml under framework:serializer:circular_reference_handler This function is now called correctly. This leaves me with a max nesting error but that's a whole other subject. Thanks for your help – Dennis de Best Feb 12 '19 at 09:08
2

An addendum to @dbrumann's answer, which very nearly worked for me. In my Symfony 4.3 application, the serializer section of the YAML requires a parent framework container. So:

# config/packages/serializer.yaml

framework:
    serializer:
        circular_reference_handler: 'App\Serializer\MyCircularReferenceHandler'

And then in the reference handler:

class MyCircularReferenceHandler {
    public function __invoke($object) {
        return $object->id;
    }
}

To check that the config values are being loaded correctly, you can utilise the Symfony console:

$ php bin/console debug:config framework

If the config values are loaded correctly, you should see them under the appropriate section in the output.

Greg J Preece
  • 111
  • 1
  • 6
1

you can see a reference to this config key here : https://symfony.com/doc/current/reference/configuration/framework.html#circular-reference-handler the doc has not been updated to show usages of this config key

this config key has to be used in one of your config files (framework.yml for instance) to set the service used to handle circular references

serializer:
    circular_reference_handler: App\Service\YourCircularReferenceHandler
ᴄʀᴏᴢᴇᴛ
  • 2,939
  • 26
  • 44
0

Alternatively, you can provide a dynamic handler in context property :

public function normalize($user, $format = null, array $context = [])
{
    // Handle circular references
    // DEPRECATED in sf 4.2 : 
    // $this->normalizer->setCircularReferenceHandler(function ($object) {return $object->getId();});

    // Good way in sf > 4.2
    $context['circular_reference_handler'] = function ($object) {return $object->getId();};
    // Return data
    $data = $this->normalizer->normalize($user, $format, $context);
}
0

In messenger configuration, You can call an object method like this:

class CircularReferenceHandler
{
    public function handle($object, string $format, array $context)
    {
        return $object->getId();
    }
}
# config/package/messenger.yaml
framework:
  messenger:
    serializer:
      default_serializer: messenger.transport.symfony_serializer
      symfony_serializer:
        format: json
        context: {circular_reference_handler: 'App\application\Helpers\CircularReferenceHandler::handle'}
lionelto
  • 81
  • 5