Reordering nested set tree with Gedmo Tree

1.8k Views Asked by At

I'm managing categories with (Atlantic18/DoctrineExtensions) Gedmo Nested Set Tree strategy in Symofny4 application.

I need to reorder the existing tree passing an array of rearranged categories to my controller.

So the starting tree is

- (1) Senza Categoria (2)
- (1) Utenze (6)
-- (2) Gas (3)
-- (4) Energia (5)

Starting DB Table enter image description here

The array that defines the new order is something like this

[
  [
    "icona" => "fas fa-ban"
    "nome" => "Senza categoria"
    "id" => "1"
  ]
  [
    "icona" => "far fa-copy"
    "nome" => "Utenze"
    "id" => "2"
    "children" => [
      [
        "icona" => "fas fa-plug"
        "nome" => "Energia"
        "id" => "4"
      ]
      [
        "icona" => "fab fa-gripfire"
        "nome" => "Gas"
        "id" => "3"
      ]
    ]
  ]
]

So the expected tree is

- (1) Senza Categoria (2)
- (1) Utenze (6)
-- (2) Energia (3)
-- (4) Gas (5)

Categoria Entity

namespace App\Entity\Contabilita\Spese;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;

/**
 *
 * @Gedmo\Tree(type="nested")
 * @ORM\Table(name="categoria_spesa")
 * @ORM\Entity(repositoryClass="Gedmo\Tree\Entity\Repository\NestedTreeRepository")
 */
class Categoria
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

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

    /**
     * @ORM\Column(type="string", length=50, nullable=true)
     */
    private $icona;

    /**
     * @Gedmo\TreeLeft
     * @ORM\Column(name="lft", type="integer")
     */
    private $lft;

    /**
     * @Gedmo\TreeLevel
     * @ORM\Column(name="lvl", type="integer")
     */
    private $lvl;

    /**
     * @Gedmo\TreeRight
     * @ORM\Column(name="rgt", type="integer")
     */
    private $rgt;

    /**
     * @Gedmo\TreeRoot
     * @ORM\ManyToOne(targetEntity="Categoria")
     * @ORM\JoinColumn(name="tree_root", referencedColumnName="id", onDelete="CASCADE")
     */
    private $root;

    /**
     * @Gedmo\TreeParent
     * @ORM\ManyToOne(targetEntity="Categoria", inversedBy="sottocategorie")
     * @ORM\JoinColumn(name="parent_id", referencedColumnName="id", onDelete="CASCADE")
     */
    private $parent;

    /**
     * @ORM\OneToMany(targetEntity="Categoria", mappedBy="parent")
     * @ORM\OrderBy({"lft" = "ASC"})
     */
    private $sottocategorie;

    /**
     * @ORM\OneToMany(targetEntity="App\Entity\Contabilita\Spese\Spesa", mappedBy="categoria", fetch="EXTRA_LAZY")
     */
    private $spese;

    /**
     * Categoria constructor.
     */
    public function __construct()
    {
        $this->sottocategorie = new ArrayCollection();
        $this->spese = new ArrayCollection();
    }

    /**
     * @return int|null
     */
    public function getId(): ?int
    {
        return $this->id;
    }

    /**
     * @return string|null
     */
    public function getNome(): ?string
    {
        return $this->nome;
    }

    /**
     * @param string $nome
     *
     * @return $this
     */
    public function setNome(string $nome): self
    {
        $this->nome = $nome;

        return $this;
    }

    /**
     * @return string|null
     */
    public function getIcona(): ?string
    {
        return $this->icona;
    }

    /**
     * @param string|null $icona
     *
     * @return $this
     */
    public function setIcona(?string $icona): self
    {
        $this->icona = $icona;

        return $this;
    }

    /**
     * @return Collection|Spesa[]
     */
    public function getSpese(): Collection
    {
        return $this->spese;
    }

    /**
     * @param Spesa $spese
     *
     * @return $this
     */
    public function addSpese(Spesa $spese): self
    {
        if (!$this->spese->contains($spese)) {
            $this->spese[] = $spese;
            $spese->setCategoria($this);
        }

        return $this;
    }

    /**
     * @param Spesa $spese
     *
     * @return $this
     */
    public function removeSpese(Spesa $spese): self
    {
        if ($this->spese->contains($spese)) {
            $this->spese->removeElement($spese);
            // set the owning side to null (unless already changed)
            if ($spese->getCategoria() === $this) {
                $spese->setCategoria(null);
            }
        }

        return $this;
    }

    /**
     * @return Categoria
     */
    public function getRoot(): Categoria
    {
        return $this->root;
    }

    /**
     * @return Categoria|null
     */
    public function getParent(): ?Categoria
    {
        return $this->parent;
    }

    /**
     * @param mixed $parent
     *
     * @return Categoria
     */
    public function setParent($parent)
    {
        $this->parent = $parent;
        return $this;
    }
}

In my controller i have a recursive function trying to reach my goal, a JS calls the update function passing categorie as parameter

public function update(Request $request)
{
  if(!$request->isXmlHttpRequest()){
      throw $this->createAccessDeniedException('What are you triyng to do?');
  }

  $categorie = $request->get('categorie');

  $em = $this->getDoctrine()
      ->getManager();

  $repository = $em->getRepository(Categoria::class);

  // Starts the reorder
  $this->reorderCategorie(null, $categorie, $repository);
  $em->flush();

  return $this->json(['status' => 1, 'message' => 'Ok']);
}

private function reorderCategorie(?int $parentId, array $children, NestedTreeRepository $repository)
{

  // Has a parent category
  if(null !== $parentId){
    $parent = $repository->find($parentId);
  }
  // It's a root node
  else {
    $parent = null;
  }

  $previousSibling = null;

  foreach ($children as $c){
    // It's a new Categoria
    if(0 == $c['id']){
        $categoria = new Categoria();
    }
    // Retrieves the category from the DB
    else {
        $categoria = $repository->find($c['id']);
    }

    // Init category
    $categoria->setParent($parent);
    $categoria->setNome($c['nome']);
    $categoria->setIcona($c['icona']);

    dump('-------- START CATEGORIA '.$categoria->getNome());

    if(is_null($parent)) {
        dump('Categoria '.$categoria->getNome().' is ROOT');
        $repository->persistAsFirstChild($categoria);
    }
    else{
        dump('Categoria is child of '.$parent->getNome());

        if(is_null($previousSibling)){
            dump('Categoria is the first child of '.$parent->getNome());
            $repository->persistAsFirstChildOf($categoria, $parent);
        } else{
            dump('Categoria is sibling of '.$previousSibling->getNome());
            $repository->persistAsNextSiblingOf($categoria, $previousSibling);
        }
    }

    if(isset($c['children']) && !empty($c['children'])){
        dump('HAS '.count($c['children']).' CHILDREN');
        // Recursive
        $this->reorderCategorie($categoria->getId(), $c['children'], $repository);
    }

    dump('-------- END CATEGORIA '.$categoria->getNome());

    $previousSibling = $categoria;
}

But the resulting tree is

enter image description here

As you can see lft and rgt index are wrong... so is it possible, using Gedmo, what i'm trying to do? If yes... where am i wrong? Thanks

0

There are 0 best solutions below