0

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

Jack Skeletron
  • 1,351
  • 13
  • 37
  • 1
    shouldn't you be using the `moveUp()` method? I worked on that package once and remember using that https://github.com/Atlantic18/DoctrineExtensions/pull/1877 – delboy1978uk Dec 13 '19 at 11:05
  • @delboy1978uk the fact is that the array that comes from the javascript is the result of a drag and drop of the categories... Also a subcategory can be moved from a parent to another o become a root node itself. – Jack Skeletron Dec 13 '19 at 11:23
  • do you move one at a time in each AJAX call? Or do you do a whole bunch of stuff and send the final result in one AJAX call? I would take the first approach – delboy1978uk Dec 13 '19 at 11:40
  • @delboy1978uk right now is one by one, it means that you can move an entire branch just moving one element. It uses [jQuery Nestable2 library](https://ramonsmit.github.io/Nestable2/) – Jack Skeletron Dec 13 '19 at 12:57

0 Answers0