Я пытаюсь заставить работать 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 снова ссылается. Двунаправленная работа не нужна.
Как я мог изменить это?
Спасибо!
Из официальной документации:
Циркулярные ссылки распространены при работе с объектными отношениями
Чтобы избежать бесконечных циклов, 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 решения, чтобы избавиться от этой проблемы:
Вместо того, чтобы генерировать исключение, круговые ссылки также могут обрабатываться пользовательскими вызовами. Это особенно полезно при сериализации объектов, имеющих уникальные идентификаторы:
$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"}]}
в твоем случае :
$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'));
Использование аннотаций групп сериализации
Например, в вашем случае с учетной записью выполните следующие действия:
<?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')) ));
Проблема заключается в том, что AccountSheet содержит операции, а категории — операции.
Вы можете иметь операции только в категории ИЛИ AccountSheet, но не в обоих случаях.
У категории есть операции, у которых есть листы счетов, у которых есть операции, у которых есть листы счетов, у которых есть операции, …