13

I am faced with a problem that gives me this error:

A circular reference has been detected when serializing the object of class "App\Entity\User" (configured limit: 1)

I have an Enterprise entity that has mission orders, vehicles, and users.

An orders entity that has a relationship with a User, Company, and Vehicle.

And a User entity that has a relationship with orders and company.

So I have this: Entreprise.php

class Entreprise
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;


    /**
     * @ORM\OneToMany(targetEntity="App\Entity\User", mappedBy="entreprise", orphanRemoval=true)
     */
    private $users;

    /**
     * @ORM\OneToMany(targetEntity="App\Entity\Vehicule", mappedBy="entreprise", orphanRemoval=true)
     */
    private $vehicules;

    /**
     * @ORM\OneToMany(targetEntity="App\Entity\OrdreMission", mappedBy="entreprise", orphanRemoval=true)
     */
    private $ordreMissions;

OrdreMission.php:

class OrdreMission
{

    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * Agent qui réalisera la mission
     * @ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="ordreMissions")
     * @ORM\JoinColumn(nullable=false)
     */
    private $user;


    /**
     * Immatriculation de la voiture de service
     * @ORM\ManyToOne(targetEntity="App\Entity\Vehicule")
     */
    private $vehicule;



    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\Entreprise", inversedBy="ordreMissions")
     * @ORM\JoinColumn(nullable=false)
     */
    private $entreprise;

Vehicule.php:

class Vehicule
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * Marque du véhicule
     * @ORM\Column(type="string", length=255)
     */
    private $marque;

    /**
     * Modèle du véhicule
     * @ORM\Column(type="string", length=255)
     */
    private $modele;

    /**
     * Immatriculation du véhicule
     * @ORM\Column(type="string", length=255)
     * @MaxDepth(2)
     */
    private $immatriculation;

    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\Entreprise", inversedBy="vehicules")
     * @ORM\JoinColumn(nullable=false)
     * @MaxDepth(2)
     */
    private $entreprise;

User.php:

class User implements UserInterface, Serializable
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * Adresse email de l'utilisateur
     * @ORM\Column(type="string", length=180, unique=true)
     * @Assert\NotBlank()
     * @Assert\Email(message="Veuillez renseigner un email valide")
     */
    private $email;

    /**
     * Rôles de l'utilisateur
     * @ORM\Column(type="json")
     */
    private $roles = [];


    /**
     * Ordres de mission de l'utilisateur
     * @ORM\OneToMany(targetEntity="App\Entity\OrdreMission", mappedBy="user")
     */
    private $ordreMissions;


    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\Entreprise", inversedBy="users")
     * @ORM\JoinColumn(nullable=false)
     */
    private $entreprise;

/**
 * String representation of object
 * @link http://php.net/manual/en/serializable.serialize.php
 * @return string the string representation of the object or null
 */
public function serialize()
{
    return serialize([
        $this->id,
        $this->email,
        $this->password,
    ]);
}

/**
 * Constructs the object
 * @link http://php.net/manual/en/serializable.unserialize.php
 * @param string $serialized <p>
 * The string representation of the object.
 * </p>
 * @return void
 */
public function unserialize($serialized)
{
    list (
        $this->id,
        $this->email,
        $this->password,
        ) = unserialize($serialized);
}

When I want to add a new vehicle, I get the error:

A circular reference has been detected when serializing the object of class "App\Entity\User" (configured limit: 1)

I saw on the Internet that I had to do something with a "maxdepth", but I do not understand what I have to do and where exactly

This is the function controller that I use to add a Vehicle Object and send it:

   /**
     * Pour créer un nouveau véhicule
     * 
     * @Route("/chef-service/ordres-mission/new/new-vehicule", name="vehicule_create")
     * @IsGranted({"ROLE_CHEF_SERVICE"})
     * @Method({"POST"})
     * @return Response
     */
    public function createVehicule(Request $request, EntityManagerInterface $manager)
    {
        $vehicule = new Vehicule();
        $vehicule->setEntreprise($this->adminService->getEntreprise());

        $form = $this->createForm(VehiculeType::class, $vehicule, [
            'action' => $this->generateUrl($request->get('_route'))
        ]);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {

            $encoders = array(new XmlEncoder(), new JsonEncoder());
            $normalizers = array(new ObjectNormalizer());
            $serializer = new Serializer($normalizers, $encoders);
            $manager->persist($vehicule);
            $manager->flush();

            $result = $serializer->normalize(
                [
                    'code' => 200,
                    'message' => 'OK',
                    'vehicule' => $vehicule,
                ],
                null,
                [AbstractObjectNormalizer::ENABLE_MAX_DEPTH => true]
            );
            
            $jsonContent = $serializer->serialize(
                $result,
                'json'
            );
            return new Response($jsonContent);

        }

        return $this->render('ordre_mission/partials/newVehicule.html.twig', [
            'formVehicule' => $form->createView(),
        ]);
    }
Dharman
  • 30,962
  • 25
  • 85
  • 135
eronn
  • 1,690
  • 3
  • 21
  • 53
  • Can you show us the bit of code where you use the serializer on your user entity ? – Dylan KAS Dec 10 '19 at 13:54
  • Yes, look in the User entity at the end, After the attributes, I have the two serialization functions – eronn Dec 10 '19 at 14:00
  • You should read about serialization depth : https://symfony.com/doc/current/components/serializer.html#handling-serialization-depth – Dylan KAS Dec 10 '19 at 14:01
  • Yes I already read this part, I understood that I had to use the @Maxdepth, but the problem is that I do not know where I should put it, in which class? – eronn Dec 10 '19 at 14:18
  • Your problem of depth concerns ordreMissions and entreprise, try to increase the depthmax to both attributes so that serializing an User will be able to go to ordreMissions and entreprise childrens – Dylan KAS Dec 10 '19 at 14:22
  • Mmh okay, done, but in my serialize function, I've to add ```[AbstractObjectNormalizer::ENABLE_MAX_DEPTH => true]``` no ? – eronn Dec 10 '19 at 14:46
  • Your using the php serialize functions not the Symfony Serializer component so the Symfony documentation is not going to help you. I can't help you with the php ones as I haven't really used them much but it's pretty straight forward to deal with the circular reference in the Symfony Serializer. – James Dec 10 '19 at 23:30
  • Okay very well I saw that JMS Serializer was more efficient than the Symfony serializer. So I installed it, and I looked at the doc, but I do not see what I need to change in my User entity to use serialization of JMS rather than PHP – eronn Dec 11 '19 at 10:49

9 Answers9

14

For me, this error happens when I use API platform (maybe not related to this case but might help someone else) I had to follow this comment here:

It's because the Entity is not marked with @ApiResource, so it's not handled by the API Platform (it is by the Symfony one), and as there is a circular reference, an error is thrown. If this Entity where an API resource, the circular reference would be handled automatically. If you don't want to make it a resource, you need to register a circular reference handler by yourself: https://symfony.com/doc/current/components/serializer.html#handling-circular-references

numediaweb
  • 16,362
  • 12
  • 74
  • 110
11

Try to avoid circular reference by using Serialization Groups (work for both Symfony Serializer and jms Serializer). Example when your serialize "User" don't serialize "users" from other entity.

User

class User 
{

/**
 * @Groups("user")
 * @ORM\Id()
 * @ORM\GeneratedValue()
 * @ORM\Column(type="integer")
 */
private $id;

/**
 * @Groups("user")
 * @ORM\ManyToOne(targetEntity="App\Entity\Entreprise", inversedBy="users")
 * @ORM\JoinColumn(nullable=false)
 */
private $entreprise;
}

Entreprise

class Entreprise
{
/**
 * @Groups("entreprise")
 * @ORM\Id()
 * @ORM\GeneratedValue()
 * @ORM\Column(type="integer")
 */
private $id;


/**
 * @Groups("user_detail")
 * @ORM\OneToMany(targetEntity="App\Entity\User", mappedBy="entreprise", orphanRemoval=true)
 */
private $users;

And then

$json = $serializer->serialize(
    $user,
    'json', ['groups' => ['user','entreprise' /* if you add "user_detail" here you get circular reference */]
);

However you have two more option either use Handling Circular References or use Handling Serialization Depth

Omar Ghorbel
  • 680
  • 5
  • 12
  • Thank you for your reply ! So why would I use the ```@Groups``` concept instead of ```@MaxDepth```? Would not it be simpler? And by the way, unfortunately it still does not work, I have the same mistake. In fact in my json, it's not a ```$user``` that I serialize and return, it's a ```Vehicle object```. But I did not really understand how you chose the groups, so I find it difficult to adapt for the current case :(. ( I will update my first post with my code ) – eronn Dec 12 '19 at 10:33
  • I prefer use Group because they give more flexibility. Each time group serialize object as you need in you context, but with MaxDepth you have one representation to one object in all your app. However you can use Maxdepth in "Vehicule" at "immatriculation" and "entreprise" – Omar Ghorbel Dec 12 '19 at 11:31
  • Okay I see ! So I opted for the @MaxDepth (2) solution. I tried to put it in place as on the doc ', but it does not change anything. I don't know if I did well. I edited my 1st post with the function I created. She is wrong ? – eronn Dec 12 '19 at 11:54
  • try @MaxDepth (1) – Omar Ghorbel Dec 12 '19 at 11:59
  • The limit is not taken into account in fact. When I'm at MaxDepth (2) on registration and company in the ```Vehicule``` entity, I always have the error with ```(configured limit: 1)``` on ```Entity \ User```. It means that something was done wrong where I did not put the maxDepth in the right places – eronn Dec 12 '19 at 12:12
  • all that boiler plate for just skipping ONE property? that is.... not good. I understand less and less how the serializer component got hyped so much on reddit. – Toskan Aug 09 '22 at 22:17
  • @Toskan it's reusable solution for complex projects – Omar Ghorbel Aug 11 '22 at 01:03
  • personally, my subjective opinion is that this is bad. You swamp your entities with groups to just exclude one thing. Imagine excluding different things in different scenarios, in no time you have so many groups you wont understand what does what. As well, listen to what this guy says: https://www.youtube.com/watch?v=XxIhzgGv214 – Toskan Aug 11 '22 at 09:37
8

In my case I've fixed injecting the serializer service instead of creating a new Serializer instance in the controller method.

use Symfony\Component\Serializer\SerializerInterface;

//...
public function createOrder(Request $request, SerializerInterface $serializer)
{
    //...
    $json = $serializer->serialize($order, 'json', ['groups' => ['normal']]);
    //...
}
Tomas
  • 866
  • 16
  • 21
7

If child entity has a parent and you do not want to get it as another "child" in serialization.. then you can try with Ignore use Symfony\Component\Serializer\Annotation\Ignore;

$user->messages(): // User has messages with relation oneToMany

then in Message add Ignore to $user:

class Message
// ...
/** @Ignore() */
$user;
Jakub O
  • 71
  • 1
  • 1
2

for th error “Reference Circulaire”

you have to tag the annotation @Groups("post:read") in front of every attribut you want to show (in your entity ) and then dont forget the use statement "use Symfony\Component\Serializer\Annotation\Groups;" (in your controller)

2

The CIRCULAR_REFERENCE error, it's an error to prevent infinite loops when the serializer is calling the object serializer. This commonly happens with the Entities with Many to one and Many to many relations. To solve this problem we need to handle the custom context for the serializer by focusing on the object id.

so in your Controllers, it can be done as the next example:

return $this->json($data,Response::HTTP_OK,[],
[ObjectNormalizer::CIRCULAR_REFERENCE_HANDLER=>function ($obj){return $obj->getId();}]);

and for using the serializer

$encoder = new JsonEncoder();
$defaultContext = [
AbstractNormalizer::CIRCULAR_REFERENCE_HANDLER => function ($object, $format, $context) {
    return $object->getName();
},];
$normalizer = new ObjectNormalizer(null, null, null, null, null, null, $defaultContext);
$serializer = new Serializer([$normalizer], [$encoder]);

For more info please check the symfony docs https://symfony.com/doc/current/components/serializer.html#handling-circular-references

Mina Amir
  • 21
  • 1
0

I took advantage of the ControllerTrait's method "json" :

return $this->json($result, Response::HTTP_OK, [], ['groups' => 'user','entreprise']);

It worked for me.

krachleur
  • 356
  • 3
  • 14
0

I discovered that you can pass circular reference limit to serialize() context, seems like that work since in error page I can see [...] (configured limit: 10). For example:

$yourNumber = 2;
$result = $serializer->normalize(
     [
        'code' => 200,
        'message' => 'OK',
        'vehicule' => $vehicule,
      ],
 null,
      [
         AbstractObjectNormalizer::ENABLE_MAX_DEPTH => true,
         AbstractNormalizer::CIRCULAR_REFERENCE_LIMIT => $yourNumber
        ]
);

Hope it helps you.

Sydney_dev
  • 1,448
  • 2
  • 17
  • 24
Szymon D.
  • 15
  • 6
-1

Maybe it's a Collection Type issue inside your Entity class.

It appears when you create a relation of the type ManyToOne, OneToMany or ManyToMany. To resolve this problem, you can try this code ;) :

Exemple :

<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use App\Repository\ClientRepository;
use App\Entity\Client;
use Symfony\Component\HttpFoundation\Request;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;


class ClientController extends AbstractController
{
    /**
     * @Route("/api/client", methods="GET")
     */
    public function index(ClientRepository $repository, SerializerInterface $serializer): Response
    { 
        //For avoiding Collection issues of ManyToOne || OneToMany || ManyToMany
        //relationship between 2 entities
        return $this->json(
            json_decode(
                $serializer->serialize(
                    $repository->findAll(),
                    'json',
                    [AbstractNormalizer::IGNORED_ATTRIBUTES => ['commandes']]
                ),
                JSON_OBJECT_AS_ARRAY
            )
        );
    }
    ...
}