Symfony 3.2 A circular reference has been detected (configured limit: 1)
Asked Answered
M

4

22

I have this two entities in my project

class PoliceGroupe
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="code", type="string", length=50)
     */
    private $code;

    /**
     * @ORM\ManyToMany(targetEntity="PointVente", inversedBy="policegroupe")
     * @ORM\JoinTable(name="police_groupe_point_vente",
     *      joinColumns={@ORM\JoinColumn(name="police_groupe_id", referencedColumnName="id")},
     *      inverseJoinColumns={@ORM\JoinColumn(name="point_vente_id", referencedColumnName="id")}
     *      )
     */
    private $pointVente;
    /**
     * Constructor
     */
    public function __construct($produit)
    {
       $this->pointVente = new \Doctrine\Common\Collections\ArrayCollection();
    }
}

And here is my other entity

class PointVente
{
    /**
     * @var string
     *
     * @ORM\Column(name="abb", type="string", length=50)
     */
    private $abb;

    /**
     * @var string
     *
     * @ORM\Column(name="libelle", type="string", length=255)
     */
    private $libelle;

    /**
     *
     * @ORM\ManyToMany(targetEntity="PoliceGroupe", mappedBy="pointVente")
     */
    private $policegroupe;
    }

and i'm trying to run this code in my controller

$encoders = array(new XmlEncoder(), new JsonEncoder());
$normalizers = array(new ObjectNormalizer());
$serializer = new Serializer($normalizers, $encoders);
$em = $this->getDoctrine()->getManager();
$data = $request->get('data');
$policegroupe=$em->getRepository('StatBundle:PoliceGroupe')->findOneBy(array('id' => $data));
$pointventes = $policegroupe->getPointVente();
$jsonContent = $serializer->serialize($pointventes, 'json');
return new JsonResponse( array('pointventes'=>$jsonContent) );

But I get this exception

Symfony\Component\Serializer\Exception\CircularReferenceException: A circular reference has been detected (configured limit: 1).
    at n/a
        in C:\wamp\www\Sys\vendor\symfony\symfony\src\Symfony\Component\Serializer\Normalizer\AbstractNormalizer.php line 194

I mapped my entities according to the doctrine annotations. Am I missing something?

Monson answered 31/5, 2017 at 13:45 Comment(0)
T
44

Symfony 3.2

Use the setCircularReferenceLimit method. For example:

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

The reason is that the circular referencing in your entities causes some problems when you try to serialize them. The effect of the method is to define the maximum depth of the serialization hierarchy.

Edit: Added circular reference handler (A circular reference has been detected (configured limit: 1) Serializer SYMFONY)

EDIT : Update (Symfony 4.2)

To be tried with Symfony 3.2, but the circular_reference_limit is not the problem here (and the defaults to 1 is OK, else your entity will be retrieved 2 times), the problem is the way the entity is handled by circular_reference_handler. Telling that id is the entity identifier solves the problem. See Symfony Docs at the bottom of this paragraph.

Since setCircularReferenceHandler is deprecated in favour of the following keys of the context circular_reference_handler, we can write:

// Tip : Inject SerializerInterface $serializer in the controller method
// and avoid these 3 lines of instanciation/configuration
$encoders = [new JsonEncoder()]; // If no need for XmlEncoder
$normalizers = [new ObjectNormalizer()];
$serializer = new Serializer($normalizers, $encoders);

// Serialize your object in Json
$jsonObject = $serializer->serialize($objectToSerialize, 'json', [
    'circular_reference_handler' => function ($object) {
        return $object->getId();
    }
]);

// For instance, return a Response with encoded Json
return new Response($jsonObject, 200, ['Content-Type' => 'application/json']);
Tarbox answered 31/5, 2017 at 13:52 Comment(11)
i add it but i get Method 'setCircularReferenceLimit' not found in class \Symfony\Component\Serializer\SerializerMonson
Sorry! It is a method of the normalizers! Let me fix this.Tarbox
@Monson try nowTarbox
thanks a lot for your quick answer but now i get A circular reference has been detected (configured limit: 2)Monson
I added this and now it's working $normalizer->setCircularReferenceLimit(2); $normalizer->setCircularReferenceHandler(function ($object) { return $object->getId(); });Monson
How can one get the normalizer to affect the serializer if we get the serializer service? (i.e. $this->get('serializer');)Boarish
Assuming that you're using the same symfony major version of the original question, I think that you need to change the default configuration of the serializer, as described here (symfony.com/doc/3.2/…). I don't think that is possible to dynamically change the normalizer after the serializer has been create.Tarbox
setCircularReferenceLimit and setCircularReferenceHandler are now deprecated. What other alternatives do we have?Argilliferous
@VictorAnuebunwa Updated, let me know if this works for you.Tarbox
Hello, I'm using Symfony 4.2 and when I tried to do what you did I got the following error : Warning: Cannot use a scalar value as an array. Any idea how to fix this ?Beaune
Hi @Beaune I can't help you due to the lack of context. If you have another question, please ask it by clicking the Ask Question button.Tarbox
D
7

The best way is to use the useCircularReferenceLimit method. As it has already been clearly explained in this post.

But we have another option. As an option, there's a way to ignore attributes from the origin object. We can ignore it if we definitely don't need it in a serialized object. The advantage of this solution is that the serialized object is smaller and easier to read, and the disadvantage is that we will no longer refer to the ignored attribute.

Symfony 2.3 - 4.1

To remove those attributes use the setIgnoredAttributes() method on the normalizer definition:

use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;

$normalizer = new ObjectNormalizer();
$normalizer->setIgnoredAttributes(array('age'));
$encoder = new JsonEncoder();

$serializer = new Serializer(array($normalizer), array($encoder));
$serializer->serialize($person, 'json'); // Output: {"name":"foo","sportsperson":false}

The setIgnoredAttributes() method was introduced in Symfony 2.3.

Prior to Symfony 2.7, attributes were only ignored while serializing. Since Symfony 2.7, they are ignored when deserializing too.

Symfony 4.2 - 5.0

The setIgnoredAttributes() method that was used as an alternative to the ignored_attributes option was deprecated in Symfony 4.2.

To remove those attributes provide an array via the ignored_attributes key in the context parameter of the desired serializer method:

use Acme\Person;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;

$person = new Person();
$person->setName('foo');
$person->setAge(99);

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

$serializer = new Serializer([$normalizer], [$encoder]);
$serializer->serialize($person, 'json', ['ignored_attributes' => ['age']]); // Output: {"name":"foo"}

In my Symfony 3.4 projects I use a mix of these two methods setIgnoredAttributes() and setCircularReferenceLimit() and it works fine.

Source: https://symfony.com/doc/3.4/components/serializer.html

Dissonancy answered 21/9, 2019 at 8:32 Comment(0)
M
1

You need to use context:

$normalizer->normalize($article, 'It doesn`t matter', [
    AbstractNormalizer::CIRCULAR_REFERENCE_HANDLER => function($object) {
        return $object->getId();
    }
]);

Big example:

namespace App\Command;

use App\Entity\Oltp\Article;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

use function json_encode;

class SqliteFirstCommand extends Command
{
    protected static $defaultName = 'app:first';
    private EntityManagerInterface $entityManager;
    private NormalizerInterface $normalizer;

    public function __construct(
        EntityManagerInterface $entityManager,
        NormalizerInterface $normalizer
    )
    {
        parent::__construct(self::$defaultName);
        $this->entityManager = $entityManager;
        $this->normalizer = $normalizer;
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $articles = $this->entityManager->getRepository(Article::class)->findAll();
        foreach ($articles as $article) {
            $array = $this->normalizer->normalize($article, 'It doesn`t matter', [
                AbstractNormalizer::CIRCULAR_REFERENCE_HANDLER => function($object) {
                    return $object->getId();
                }
            ]);
            $output->writeln(json_encode($array));
        }

        return 0;
    }
}
Marco answered 10/10, 2021 at 12:22 Comment(1)
That solved the issue for me in Symfony 6.x. Don't forget to clear the cache on each serialization call, else Symfony will pull the cached results of failure as well, meaning no changes / no working code will be seen - lol.Megan
U
-2

Fixed same issue by

use JMS\Serializer\SerializerBuilder;
...

$products = $em->getRepository('AppBundle:Product')->findAll();
$serializer = SerializerBuilder::create()->build();
$jsonObject = $serializer->serialize($products, 'json');

Read here

Unwarranted answered 13/7, 2017 at 13:5 Comment(1)
Which doesn't solve the given issue. You are just replacing the serializer component/service with a third party solution which in this case doesn't even provide a normalization of circular references.Bagehot

© 2022 - 2024 — McMap. All rights reserved.