Как обновить сущность и ее отношения на нескольких уровнях? - PullRequest
0 голосов
/ 19 сентября 2018

Я создаю веб-приложение с Symfony 3 и Doctrine, которое позволяет рисовать древовидную структуру и сохранять ее.

Узел содержит подузлы в 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, который принимает все новое дерево какпараметр (с идентификаторами доктрины, установленными по всей структуре).Я хочу merge входящее дерево с уже сохраненным, чтобы удаленные узлы удалялись при выполнении flush.

Во сне я представляю что-то вроде (параметр $node является корнемузел с установленным идентификатором):

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

, где Doctrine занимается выполнением всех модификаций самого узла и всех его потомков (добавления, удаления, перемещения) в базе данных.Например, если следующее дерево с идентификатором root, равным 1, уже сохранено в базе данных

initial state

, а затем я вызываю мой контроллер обновлений со следующим (соблюдая идентификаторы конечно)

modified state

Доктрина сможет "увидеть", что узел 5 отсутствует в новой структуре, а затем выполнить $entityManager->remove($node5) сам по себе.

Разве это не сон, и есть ли какой-то способ заставить Учение вести себя так?Или мне нужно рекурсивно спуститься по дереву и выполнить сравнение узлов за узлами, чтобы внести изменения через entityManager самостоятельно?

1 Ответ

0 голосов
/ 24 сентября 2018

Я пришел к выводу, что Доктрина не была такой волшебной;)

Поэтому мне пришлось:

  • объявить операцию Доктрина merge для распространения вдоль childrenотношение (с аннотацией cascade={"merge"})
  • merge корневой узел
  • вызывает операцию $em->remove на узлах, которые существовали в дереве до слияния и которые отсутствуютна входящем дереве
  • наконец, позвоните $em->flush
...