Symfony 3 Doctrine 2: Циркулярная ссылка на отношения

Я пытаюсь заставить работать 4 сущности в Symfony 3 с Doctrine 2, но я застрял в исключении циклической ссылки, когда я хочу сериализовать сущность Account, например:

Обнаружена круговая ссылка (настроенный предел: 1).

Я выбрал двунаправленные отношения в моих сущностях, и схема выглядит так:

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

Вот сущности (с некоторыми чистками для ясности):

ЦСИ \ AppBundle \ Entity \ 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;
}
}

ЦСИ \ AppBundle \ Entity \ 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;
}
}

ЦСИ \ AppBundle \ Entity \ 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;
}
}

ЦСИ \ AppBundle \ Entity \ 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;
}
}

Я предполагаю, что это на сущности Операции, где AccountSheet снова ссылается. Двунаправленная работа не нужна.

Как я мог изменить это?

Спасибо!

2

Решение

Из официальной документации:

Циркулярные ссылки распространены при работе с объектными отношениями

Чтобы избежать бесконечных циклов, GetSetMethodNormalizer выдает исключение CircularReferenceException при обнаружении такого случая:

$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

Итак, с этого момента у вас есть 3 решения, чтобы избавиться от этой проблемы:

  1. Установите обработчик циклических ссылок:

Вместо того, чтобы генерировать исключение, круговые ссылки также могут обрабатываться пользовательскими вызовами. Это особенно полезно при сериализации объектов, имеющих уникальные идентификаторы:

$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. Установите игнорируемые атрибуты (не мое предпочтительное решение):

в твоем случае :

$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. Используйте атрибуты группы (мое предпочтительное решение):
    Этот метод аналогичен настройке игнорируемых атрибутов, поскольку вы выбираете, какой атрибут вы хотите сериализовать, добавляя к нему аннотацию группы, а остальные будут игнорироваться для рекурсивности в процессе нормализации.

Использование аннотаций групп сериализации

Атрибуты групп

Например, в вашем случае с учетной записью выполните следующие действия:

<?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;
}
}

Затем не помещайте эту групповую аннотацию в поле $ account в сущности AccountSheet, чтобы избавиться от проблемы циклических ссылок.

Наконец вы сериализуете свою учетную запись:

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

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

Другие решения

Проблема заключается в том, что AccountSheet содержит операции, а категории — операции.

Вы можете иметь операции только в категории ИЛИ AccountSheet, но не в обоих случаях.

У категории есть операции, у которых есть листы счетов, у которых есть операции, у которых есть листы счетов, у которых есть операции, …

0