Symfony 3主义2:关于关系的循环参考

时间:2017-02-26 15:35:55

标签: php doctrine symfony circular-reference

我试图使用Doctrine 2在Symfony 3中运行4个实体,但是当我想序列化一个Account实体时,我仍然坚持循环引用异常:

  

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

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

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

以下是实体(为清晰起见,有一些清理):

SRC \的appbundle \实体\ 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 \实体\ 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 \实体\ 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 \实体\ 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。实际上并不需要双向操作。

我怎么能重新安排这个?

谢谢!

2 个答案:

答案 0 :(得分:7)

来自官方文件:

  

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

     

为避免无限循环,遇到这种情况时,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. 设置循环引用处理程序:
  2.   

    循环引用也可以由自定义callables处理,而不是抛出异常。在序列化具有唯一标识符的实体时,这尤其有用:

    $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. 设置忽略的属性(不是我首选的解决方案):
    2. 在你的情况下:

      $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. 使用组属性(我的首选解决方案): 此方法类似于设置忽略的属性,因为您将通过在其上添加组注释来选择要序列化的属性,其余的将在标准化过程中被忽略以用于递归。
      2. Using Serialization Groups Annotations

        Attributes Groups

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

        <?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')) ));
        

答案 1 :(得分:2)

$jsonContent = $serializer->serialize($yourObject, 'json', [
    'circular_reference_handler' => function ($object) {
        return $object->getId();
    }
]);

以上代码对我有用,可以修复循环引用异常。 (Symfony> = 4.2)