2017-02-26 3 views
1

J'essaye de travailler 4 entités dans Symfony 3 avec Doctrine 2 mais je suis coincé sur une exception de référence circulaire quand je veux sérialiser une entité Account par exemple:Symfony 3 Doctrine 2: Référence circulaire sur les relations

Une référence circulaire a été détectée (limite configurée: 1).

J'ai choisi les relations bi-directionnelles dans mes entités et le schéma est comme ceci:

- Account [1] ---- [0..*] AccountSheet 
- AccountSheet [1] ---- [0..*] Operation 
- Operation [0..*] ---- [1] Category 

Voici les entités (avec quelques Nettoyages pour plus de clarté):

src \ AppBundle \ Entité \ Account.php

<?php 
    namespace AppBundle\Entity; 

    use Doctrine\ORM\Mapping as ORM; 
    use Doctrine\Common\Collections\ArrayCollection; 
    use AppBundle\Entity\AbstractGenericEntity; 

    /** 
    * @ORM\Entity() 
    * @ORM\Table(name="accounts", 
    *  uniqueConstraints={@ORM\UniqueConstraint(name="accounts_name_unique",columns={"name"})}) 
    */ 
    class Account extends AbstractGenericEntity{ 
     /** 
     * @ORM\OneToMany(targetEntity="AccountSheet", mappedBy="account") 
     * @var AccountSheet[] 
     */ 
     protected $accountSheets; 

     public function __construct($name = null, $description = null){ 
      $this->accountSheets = new ArrayCollection(); 
      $this->name = $name; 
      $this->description = $description; 
     } 
    } 

src \ AppBundle \ En tité \ AccountSheet.php

<?php 
    namespace AppBundle\Entity; 

    use Doctrine\ORM\Mapping as ORM; 
    use Doctrine\Common\Collections\ArrayCollection; 
    use AppBundle\Entity\AbstractGenericEntity; 

    /** 
    * @ORM\Entity() 
    * @ORM\Table(name="accounts_sheets", 
    *  uniqueConstraints={@ORM\UniqueConstraint(name="accountsheet_account_unique", columns={"name", "account_id"})}) 
    * @ORM\HasLifecycleCallbacks 
    */ 
    class AccountSheet extends AbstractGenericEntity{ 

     /** 
     * @ORM\ManyToOne(targetEntity="AppBundle\Entity\Account", inversedBy="accountSheets") 
     * @var Account 
     */ 
     protected $account; 

     /** 
     * @ORM\OneToMany(targetEntity="Operation", mappedBy="accountSheet") 
     * @var Operation[] 
     */ 
     protected $operations; 

     public function __construct($name = null){ 
      $this->operations = new ArrayCollection(); 
      $this->name = $name; 
     } 
    } 

src \ AppBundle \ Entité \ Operation.php

<?php 
    namespace AppBundle\Entity; 

    use Doctrine\ORM\Mapping as ORM; 
    use AppBundle\Entity\AbstractGenericEntity; 

    /** 
    * @ORM\Entity() 
    * @ORM\Table(name="operations") 
    */ 
    class Operation extends AbstractGenericEntity{ 
     /** 
     * @ORM\ManyToOne(targetEntity="AppBundle\Entity\AccountSheet", inversedBy="operations") 
     * @ORM\JoinColumn(nullable=false) 
     * @var AccountSheet 
     */ 
     protected $accountSheet; 

     /** 
     * @ORM\ManyToOne(targetEntity="AppBundle\Entity\Category", inversedBy="operations") 
     * @var Category 
     */ 
     protected $category; 

     public function __construct($type = null, $label = null, $montant = null, $comment = null){ 
      $this->label = $label; 
      $this->type = $type; 
      $this->comment = $comment; 
      $this->montant = $montant; 
     } 
    } 

src \ AppBundle \ Entité \ category.php

<?php 
    namespace AppBundle\Entity; 

    use Doctrine\ORM\Mapping as ORM; 
    use Doctrine\Common\Collections\ArrayCollection; 
    use AppBundle\Entity\AbstractGenericEntity; 

    /** 
    * @ORM\Entity() 
    * @ORM\Table(name="categories") 
    */ 
    class Category extends AbstractGenericEntity{ 

     /** 
     * @ORM\Column(type="string") 
     */ 
     protected $label; 

     /** 
     * @ORM\Column(type="string") 
     */ 
     protected $description; 

     /** 
     * @ORM\OneToMany(targetEntity="Operation", mappedBy="category") 
     * @var Operation[] 
     */ 
     protected $operations; 

     public function __construct($name = null){ 
      $this->operations = new ArrayCollection(); 
      $this->name = $name; 
     } 
} 

I suppose que c'est sur l'entité Operation, où AccountSheet est référencé à nouveau. Le fonctionnement bidirectionnel n'est pas vraiment nécessaire.

Comment est-ce que je peux réorganiser ceci?

Merci!

+1

'Le bi-directionnel sur le fonctionnement n'est pas vraiment needed.' Donc, ne pas l'utiliser :) Vous pouvez utiliser unidirectionnels –

+0

@AnomalySmith Que vos constructeurs regarder comme? –

+0

@MaxLipsky Comme le doc spécifie une relation One To Many doit être bidirectionnelle, je suppose que je n'ai pas le choix? http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html#one-to-many-bidirectional. – AnomalySmith

Répondre

0

Le problème vient de AccountSheet ayant des opérations et des catégories ayant des opérations.

Vous ne pouvez effectuer des opérations que dans la catégorie OU dans la feuille de compte, mais pas dans les deux.

Une catégorie a des opérations qui ont des feuilles de comptes qui ont des opérations qui ont des feuilles de calcul qui ont des opérations, ...

+0

J'ai finalement supprimé la déclaration '$ operations' dans la catégorie et modifié la déclaration' $ category' dans Operation pour qu'elle soit unidirectionnelle: '* @ORM \ ManyToOne (targetEntity =" Category ")'. J'ai tenté de mettre à jour le schéma, de vider le cache, mais j'ai toujours cette exception. Est-ce que je manque quelque chose? – AnomalySmith

2

De la documentation officielle:

Les références circulaires sont communes lorsqu'ils traitent des relations d'entités

Pour éviter des boucles infinies, GetSetMethodNormalizer jette un CircularReferenceException lorsqu'un tel cas est rencontré:

$member = new Member(); 
$member->setName('Kévin'); 

$org = new Organization(); 
$org->setName('Les-Tilleuls.coop'); 
$org->setMembers(array($member)); 

$member->setOrganization($org); 

echo $serializer->serialize($org, 'json'); // Throws a CircularReferenceException 

Donc, à partir de ce point, vous avez 3 solutions pour vous débarrasser de ce problème:

  1. Définir un gestionnaire de référence circulaire:

Au lieu de lancer une exception, les références circulaires peuvent également être traitées par appelables personnalisées. Ceci est particulièrement utile lors de la sérialisation entités ayant des identifiants uniques:

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

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

$serializer = new Serializer(array($normalizer), array($encoder)); 
var_dump($serializer->serialize($org, 'json')); 
// {"name":"Les-Tilleuls.coop","members":[{"name":"K\u00e9vin", organization: "Les-Tilleuls.coop"}]} 
  1. Définir les attributs ignorés (pas ma solution préférée):

dans votre cas:

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

normalizer->setIgnoredAttributes(array("account", "accountSheet", "category", "operation")); 

$serializer = new Serializer(array($normalizer), array($encoder)); 
var_dump($serializer->serialize($org, 'json')); 
  1. Utilisez des attributs groupe (ma solution préférée): Cette rencontre hod est similaire à la définition d'attributs ignorés car vous choisirez l'attribut que vous voulez sérialiser en y ajoutant l'annotation de groupe et le reste sera ignoré pour la récursivité pendant le processus de normalisation.

Using Serialization Groups Annotations

Attributes Groups

Dans votre cas avec l'entité compte par exemple faire sur le côté du compte:

<?php 
namespace AppBundle\Entity; 

use Doctrine\ORM\Mapping as ORM; 
use Doctrine\Common\Collections\ArrayCollection; 
use AppBundle\Entity\AbstractGenericEntity; 
use Symfony\Component\Serializer\Annotation\Groups; 

/** 
* @ORM\Entity() 
* @ORM\Table(name="accounts", 
*  uniqueConstraints={@ORM\UniqueConstraint(name="accounts_name_unique",columns={"name"})}) 
*/ 
class Account extends AbstractGenericEntity{ 
    /** 
    * @ORM\OneToMany(targetEntity="AccountSheet", mappedBy="account") 
    * @var AccountSheet[] 
    * @Groups({"account"}) 
    */ 
    protected $accountSheets; 

    public function __construct($name = null, $description = null){ 
     $this->accountSheets = new ArrayCollection(); 
     $this->name = $name; 
     $this->description = $description; 
    } 
} 

Alors ne mettez pas cette annotation de groupe sur le compte de $ champ dans l'entité AccountSheet pour se débarrasser du problème de référence circulaire.

Enfin, vous sérialiser votre compte:

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

$serializer = new Serializer(array($normalizer), array($encoder)); 
var_dump($serializer->serialize($account, 'json', array('groups' => array('account'))));