Symfony/Doctrine从具有ManyToOne关系的反转侧的数据库中获取时的无限递归



上下文

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

当我使用Category实体存储库获取数据并使用var_dump(...)显示数据时,会发生无限递归。当我返回带有这些数据的JSON响应时,它只是空的。它应该准确地检索我手动插入的数据。

你知道如何在不删除Category实体中的相反侧关系的情况下避免这个错误吗?

我尝试过的

  • 添加条令注释fetch=";LAZY";在一方、另一方和双方的关系中
  • 使用Doctrine在数据库中插入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 AppEntity;
use AppRepositoryCategoryRepository;
use DoctrineCommonCollectionsArrayCollection;
use DoctrineCommonCollectionsCollection;
use DoctrineORMMapping as ORM;
/**
* @ORMEntity(repositoryClass=CategoryRepository::class)
*/
class Category
{
/**
* @ORMId()
* @ORMGeneratedValue()
* @ORMColumn(name="id", type="integer")
*/
private $id;
/**
* @ORMColumn(name="name", type="string", length=255)
*/
private $name;
/**
* @ORMOneToMany(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/Product.php
<?php
namespace AppEntity;
use AppRepositoryProductRepository;
use DoctrineCommonCollectionsArrayCollection;
use DoctrineCommonCollectionsCollection;
use DoctrineORMMapping as ORM;
/**
* @ORMEntity(repositoryClass=ProductRepository::class)
*/
class Product
{
/**
* @ORMId()
* @ORMGeneratedValue()
* @ORMColumn(name="id", type="integer")
*/
private $id;
/**
* @ORMColumn(name="name", type="string", length=255)
*/
private $name;
/**
* @ORMManyToOne(targetEntity=Category::class, inversedBy="products", fetch="LAZY")
* @ORMJoinColumn(name="category_id", referencedColumnName="id")
*/
private $category;
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;
}
public function getCategory(): ?Category
{
return $this->category;
}
public function setCategory(?Category $category): self
{
$this->category = $category;
return $this;
}
}

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

其次,$this->json本质上是将json_encode打包到JsonResponse对象中的快捷方式(或者使用符号/串行器(。另一方面,json_encode序列化给定的对象的公共属性,除非对象实现JsonSerializable(见下文(。由于几乎所有实体的所有属性都是私有的,因此结果通常是空对象序列化。

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

  • 使用symfony序列化程序,该序列化程序可以处理循环引用(导致无限递归/循环(,从而将对象变成安全的数组。然而,结果可能仍然不符合你的喜好
  • 在实体上实现JsonSerializable,并小心避免递归添加子对象
  • 从对象中自己构建安全数组,以传递到$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(),
];
}
}

添加这些之后,您的代码应该可以正常工作了。对于dev,您应该始终使用前面提到的dump,并且所有$this->json都可以正常工作。这就是为什么我通常更喜欢这个选项。但是,需要注意的是:通过这种方式,您只能为类别提供一个json序列化方案。对于任何其他方式,您必须使用其他选项,然后。。。无论如何,这几乎总是正确的。

相关内容

  • 没有找到相关文章

最新更新