Symfony/Doctrine从数据库中提取时使用多个一元关系的反向端进行无限递归 上下文

Symfony/Doctrine从数据库中提取时使用多个一元关系的反向端进行无限递归 上下文,symfony,doctrine-orm,Symfony,Doctrine Orm,在一个简单的Symfony项目中,我创建了两个实体,Product和Category,它们通过@manytone和@OneToMany与条令注释的关系相关联。一个类别可以有多个产品,一个产品与一个类别相关。我已经在类别表中手动插入了数据 当我使用Categoryentityrepository获取数据并使用var\u dump(…)显示数据时,会发生无限递归。当我返回带有这些数据的JSON响应时,它只是空的。它应该准确地检索我手动插入的数据 您知道如何避免此错误而不删除类别实体中的反向边关系吗

在一个简单的Symfony项目中,我创建了两个实体,
Product
Category
,它们通过
@manytone
@OneToMany
与条令注释的关系相关联。一个类别可以有多个产品,一个产品与一个类别相关。我已经在
类别
表中手动插入了数据

当我使用
Category
entityrepository获取数据并使用
var\u dump(…)
显示数据时,会发生无限递归。当我返回带有这些数据的JSON响应时,它只是空的。它应该准确地检索我手动插入的数据

您知道如何避免此错误而不删除
类别
实体中的反向边关系吗

我试过的
  • 在关系的一侧、另一侧和两侧添加条令注释
  • 使用条令在数据库中插入
    Category
    对象,以查看数据库连接是否正常工作。是的
  • 正在删除关系的。它奏效了,但这不是我想要的
代码片段 控制器

dummy/src/Controller/DefaultController.php

...

$entityManager = $this->getDoctrine()->getManager();
$repository = $entityManager->getRepository(Category::class);

// ===== PROBLEM HERE =====
//var_dump($repository->findOneByName('house'));
//return $this->json($repository->findOneByName('house'));

...

实体

dummy/src/Entity/Category.php

<?php

namespace App\Entity;

use App\Repository\CategoryRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass=CategoryRepository::class)
 */
class Category
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(name="id", type="integer")
     */
    private $id;

    /**
     * @ORM\Column(name="name", type="string", length=255)
     */
    private $name;

    /**
     * @ORM\OneToMany(targetEntity=Product::class, mappedBy="category", fetch="LAZY")
     */
    private $products;

    public function __construct()
    {
        $this->products = new ArrayCollection();
    }

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getName(): ?string
    {
        return $this->name;
    }

    public function setName(string $name): self
    {
        $this->name = $name;

        return $this;
    }

    /**
     * @return Collection|Product[]
     */
    public function getProducts(): Collection
    {
        return $this->products;
    }

    public function addProduct(Product $product): self
    {
        if (!$this->products->contains($product)) {
            $this->products[] = $product;
            $product->setCategory($this);
        }

        return $this;
    }

    public function removeProduct(Product $product): self
    {
        if ($this->products->contains($product)) {
            $this->products->removeElement($product);
            // set the owning side to null (unless already changed)
            if ($product->getCategory() === $this) {
                $product->setCategory(null);
            }
        }

        return $this;
    }
}

dummy/src/Entity/Category.php

我假设您使用
var\u dump
进行调试。出于调试目的,请使用
dump
dd
,它来自
symfony/debug
,默认情况下应已在
dev
上启用。
dump
dd
都应该及时中止无限递归。(很多symfony/doctor对象/服务都有循环引用,或者只有很多被引用的对象。)
dump
将给定的php变量添加到探查器(探查器栏中的目标标记符号)或输出中
dd
添加给定的var,如
dump
,但也会结束该过程(因此dump和die)。-在生产环境中,永远不要使用dump/dd/var_dump,而是正确地序列化数据

其次,
$this->json
本质上是将
json\u encode
打包到
JsonResponse
对象中的快捷方式(或者改用symfony/serializer)<另一方面,code>json_encode
序列化给定对象的公共属性,除非该对象实现(见下文)。由于几乎所有实体的所有属性都是私有的,因此结果通常是空对象序列化

有许多选项可供选择,但本质上需要解决无限递归的问题。imho标准选项包括:

  • 使用可以(导致无限递归/循环)的数组,从而将对象转换为安全数组。然而,结果可能仍然不符合你的喜好
  • 在实体上实现,并小心避免递归地添加子对象
  • 您自己从对象构建一个安全数组,以传递到
    $this->json
    (“手动方法”)
在此上下文中,安全数组是一个仅包含字符串、数字和(嵌套)字符串和数字数组的数组,这本质上意味着丢失所有实际对象

可能还有其他选择,但我觉得这些是最方便的。我通常更喜欢
JsonSerializable
选项,但这是口味的问题。这方面的一个例子是:

class Category implements \JsonSerializable { // <-- new implements!
   // ... your entity stuff
 
   public function jsonSerialize() {
       return [
           'id' => $this->id,
           'name' => $this->name,
           'products' => $this->products->map(function(Product $product) {
               return [
                   'id' => $product->getId(),
                   'name' => $product->getName(),
                   // purposefully excluding category here!
               ];
           })->toArray(),
       ];
   }
}
类类别实现\JsonSerializable{/$this->id,
“名称”=>$this->name,
“产品”=>$this->products->map(功能(产品$Product){
返回[
'id'=>$product->getId(),
“名称”=>$product->getName(),
//故意排除这里的类别!
];
})->toArray(),
];
}
}
添加此代码后,您的代码应该可以正常工作。对于开发人员,您应该始终使用前面提到的
dump
,所有
$this->json
都可以正常工作。这就是为什么我通常更喜欢这个选项。但是,需要注意的是:通过这种方式,类别只能有一个json序列化方案。对于任何其他方式,您必须使用其他选项,然后。。。无论如何,这几乎总是正确的

class Category implements \JsonSerializable { // <-- new implements!
   // ... your entity stuff
 
   public function jsonSerialize() {
       return [
           'id' => $this->id,
           'name' => $this->name,
           'products' => $this->products->map(function(Product $product) {
               return [
                   'id' => $product->getId(),
                   'name' => $product->getName(),
                   // purposefully excluding category here!
               ];
           })->toArray(),
       ];
   }
}