首页 > 解决方案 > 如何在多个级别上更新实体及其关系?

问题描述

我正在使用 Symfony 3 和 Doctrine 制作一个 Web 应用程序,它允许绘制树结构并将其持久化。

一个节点包含名为 的OneToMany关系children中的子节点,树的根是唯一不是任何其他节点的子节点的节点。

这是实体:

/**
 * @ORM\Entity(repositoryClass="App\Repository\NodeRepository")
 */
class Node
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    public $id;

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

    /**
     * @ORM\OneToMany(targetEntity="App\Entity\Node", mappedBy="parent")
     */
    public $children;

    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\Node", inversedBy="children")
     * @ORM\JoinColumn(name="id_parent", referencedColumnName="id")
     */
    public $parent;

    public function __construct()
    {
        $this->children = 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|Node[]
     */
    public function getChildren(): Collection
    {
        return $this->children;
    }

    public function addChild(Node $child): self
    {
        if (!$this->children->contains($child)) {
            $this->children[] = $child;
            $child->setParent($this);
        }

        return $this;
    }

    public function removeChild(Node $child): self
    {
        if ($this->children->contains($child)) {
            $this->children->removeElement($child);
            // set the owning side to null (unless already changed)
            if ($child->getParent() === $this) {
                $child->setParent(null);
            }
        }

        return $this;
    }

    public function getParent(): ?self
    {
        return $this->parent;
    }

    public function setParent(?self $parent): self
    {
        $this->parent = $parent;

        return $this;
    }
}

创建一棵树我没有特别的问题。但是在更新它时,我不确定最好的方法是什么。主要示例是删除一个节点(但该问题也适用于移动节点),我将进一步说明我的问题。

我有一个updateNodeAction控制器,它将整个新树作为参数(整个结构都设置了学说 ID)。我想要merge具有已经持久化的传入树,以便在执行flush.

在我的梦中,我想象类似的东西(参数$node是设置了 id 的根节点):

public function updateNodeAction(Node $node, EntityManagerInterface $entityManager) {
    $entityManager->merge($node);
    $entityManager->flush();
    return new Response('OK');
}

Doctrine 处理对数据库中节点本身及其所有后代(添加、删除、移动)的所有修改。例如,如果以下根的 id 为 1 的树已经保存在数据库中

初始状态

然后我用以下一个调用我的更新控制器(当然尊重ID)

修改状态

Doctrine 将能够“看到”新结构中缺少节点 5,然后$entityManager->remove($node5)自行执行。

这不是一个梦想吗?有某种方法可以让 Doctrine 表现得像这样吗?或者我是否必须递归地向下树并进行逐节点比较才能通过entityManager自己进行修改?

标签: symfonydoctrine

解决方案


我得出的结论是教义没有那么神奇;)

所以我不得不:

  • merge声明沿关系传播的 Doctrine操作children(带有cascade={"merge"}注释)
  • merge根节点
  • $em->remove在合并之前存在于树上的节点上调用操作,并且在传入树上不存在
  • 最后,打电话$em->flush

推荐阅读