Symfony 3.0.4 Circular reference detected during s

2019-05-07 20:41发布

问题:

I'm using FOSRestBundle in a Symfony project. When it I try to handle a view, it fails during the serialization of my data with the Symfony serializer as well as with the JMSSerializer.

This is the method rendering the response:

DefaultController.php

$em = $this->getDoctrine()->getManager('magellan');
$qb = $em->createQueryBuilder();

$query = $qb->select('h')
        ->from('DataBundle:Holding', 'h')
        ->where($qb->expr()->eq('h.id', ':holding_id'))
        ->setParameter('holding_id', $holding_id)
        ->getQuery();

$results = $query->getResult();

$view = $this->view($results, 200);

// Everything's ok up to this point

return $this->handleview($view);

And these are my entities:

Holding.php

class Holding
{

    ...

    /**
     * @ORM\OneToMany(targetEntity="Subsidiary", mappedBy="holding")
     */
    private $subsidiaries;
}

Subsidiary.php

class Subsidiary
{

    ...

    /**
     * @ORM\ManyToOne(targetEntity="Holding", inversedBy="subsidiaries")
     * @ORM\JoinColumn(name="id_holding", referencedColumnName="id_holding")
     */
    private $holding;

    /**
     * @ORM\OneToMany(targetEntity="Brand", mappedBy="subsidiary")
     */
    private $brands;
}

Brand.php

class Brand
{

    ...

    /**
     * @ORM\ManyToOne(targetEntity="Subsidiary", inversedBy="brands")
     * @ORM\JoinColumn(name="id_subsidiary", referencedColumnName="id_subsidiary")
     */
    private $subsidiary;

    /**
     * @ORM\OneToMany(targetEntity="Product", mappedBy="brand")
     */
    private $products;
}

Product.php

class Product
{

    ...

    /**
     * @ORM\ManyToOne(targetEntity="Brand", inversedBy="products")
     * @ORM\JoinColumn(name="id_brand", referencedColumnName="id_brand")
     */
    private $brand;

    /**
     * @ORM\ManyToOne(targetEntity="Sector", inversedBy="products")
     * @ORM\JoinColumn(name="id_sector", referencedColumnName="id_sector")
     */
    private $sector;

    /**
     * @ORM\OneToMany(targetEntity="Commercial", mappedBy="product")
     */
    private $commercials;
}

Commercial.php

class Commercial
{

    ...

    /**
     * @ORM\ManyToOne(targetEntity="Product", inversedBy="commercials")
     * @ORM\JoinColumn(name="id_product", referencedColumnName="id_product")
     */
    private $product;

    /**
     * @ORM\OneToMany(targetEntity="CommercialReport", mappedBy="commercial")
     */
    private $reports;

CommercialReport.php

class CommercialReport
{

    ...

    /**
     * @ORM\ManyToOne(targetEntity="Commercial", inversedBy="reports")
     * @ORM\JoinColumn(name="id_commercial", referencedColumnName="id_commercial")
     */
    private $commercial;
}

Sector.php

class Sector
{

    ...

    /**
     * @ORM\OneToMany(targetEntity="Product", mappedBy="sector")
     */
    private $products;
}

When using the default symfony serializer, I get the following error:

"message":"A circular reference has been detected (configured limit: 1).","class":"Symfony\Component\Serializer\Exception\CircularReferenceException"

And when using the JMSSerializer, when I go to the corresponding page of the controller, the page just never finishes loading. At the same time in the dev.log file new Doctrine.debug entries with requests to my DB are added every second.

回答1:

    $normalizers->setCircularReferenceHandler(function ($object) {
        return $object->getId();
    });

Just add it after you make the instance if your objectNormalizer() it worl perfectly for me



回答2:

If you use FosRestBundle, you can use the GROUPS for the serializer. There is an annotation given by FosRestBundle : @FOS\RestBundle\Controller\Annotations\View(serializerGroups={"user"})

Your group can exclude the circular property.

Another idea you can do this. In your app/config/services.yml

circular_reference_handler:
    public: false
    class: callback
    factory: [AppBundle\Serializer\CircularHandlerFactory, getId]
serializer.normalizer.object:
    class: Symfony\Component\Serializer\Normalizer\ObjectNormalizer
    arguments: ["@serializer.mapping.class_metadata_factory", null, "@serializer.property_accessor"]
    public: false
    tags: [serializer.normalizer]
    calls:
        - method: setCircularReferenceLimit
          arguments: [1]
        - method: setCircularReferenceHandler
          arguments: ["@circular_reference_handler"]

The factory can be like this:

namespace AppBundle\Serializer;

class CircularHandlerFactory
{
    /**
     * @return \Closure
     */
    public static function getId()
    {
        return function ($object) {
            return $object->getId();
        };
    }
}