【问题标题】:Symfony 3 Doctrine 2: Circular reference on relationsSymfony 3 Doctrine 2:关于关系的循环引用
【发布时间】:2017-02-26 15:35:54
【问题描述】:

我正在尝试使用 Doctrine 2 在 Symfony 3 中使用 4 个实体,但是当我想序列化 Account 实体时遇到循环引用异常:

检测到循环引用(配置限制:1)。

我在实体中选择了双向关系,架构是这样的:

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

这里是实体(为了清楚起见,做了一些清洁):

src\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;
        }
    }

src\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;
        }
    }

src\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;
        }
    }

src\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;
        }
}

我猜它在 Operation 实体上,其中 AccountSheet 再次被引用。双向 on 操作并不是真正需要的。

我该如何重新安排呢?

谢谢!

【问题讨论】:

  • The bi-directional on operation is not really needed. 所以不要使用它:) 你可以使用单向
  • @AnomalySmith 你的构造函数是什么样的?
  • @MaxLipsky 由于文档指定关系一对多必须是双向的,我想我别无选择? docs.doctrine-project.org/projects/doctrine-orm/en/latest/….
  • @AlphonseD。我已更新以添加实体构造函数(继承的抽象在其构造函数中没有任何代码逻辑)
  • @AnomalySmith 我只是想确定一下 :) 如果你想保持这种双向关系,我想你可以使用 attribute groupsignoring attributes

标签: php doctrine symfony circular-reference


【解决方案1】:

来自官方文档:

循环引用在处理实体关系时很常见

为避免无限循环,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. 使用组属性(我的首选解决方案): 此方法类似于设置忽略的属性,因为您将通过在其上添加组注释来选择要序列化的属性,其余的将在规范化过程中被忽略以进行递归。

Using Serialization Groups Annotations

Attributes Groups

在您使用 Account 实体的情况下,例如在帐户方面执行此操作:

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

那就不要在 AccountSheet 实体的 $account 字段上加上这个组注解,以摆脱循环引用的问题。

最后你序列化你的帐户:

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

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

【讨论】:

  • 记得使用group的时候,给相关Entity加上Group属性,否则json中的relation就是空的。
【解决方案2】:
$jsonContent = $serializer->serialize($yourObject, 'json', [
    'circular_reference_handler' => function ($object) {
        return $object->getId();
    }
]);

上面的代码适用于我修复循环引用异常。 (Symfony >=4.2)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-04
    • 1970-01-01
    • 1970-01-01
    • 2013-03-12
    • 2023-03-09
    相关资源
    最近更新 更多