Refreshing Nested Set Tree Indexes in Symfony

Tree

Once I used Nestedset behavior for Doctrine, Symfony in a project, and faced issue with broken indexes. (You can read how Nested Set works here). This happened because low-level MySQL query executed during migration, and because of this Doctrine events responsible for refreshing indexes was not executed. To fix that a Symfony Command was written, which refreshes indexes. This is just example. Better way is to move this code to the service and add one more migration which will refresh indexes. Here is just simple example how this issue can be solved.

Argument of the command is an entity to refresh.

<?php

namespace Vendor\Bundle\SomeBundle\Command;

use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class NestedSetRefreshCommand extends ContainerAwareCommand
{
    /**
     * {@inheritdoc}
     */
    protected function configure()
    {
        $this
            ->setName('vendor:nested-set:refresh')
            ->addArgument('entity', InputArgument::REQUIRED)
        ;
    }

    /**
     * @param InputInterface $input
     * @param OutputInterface $output
     * @throws \Exception
     * @return int
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $container = $this->getContainer();
        /** @var EntityManagerInterface $em */
        $em = $container->get('doctrine.orm.entity_manager');
        $repository = $em->getRepository($input->getArgument('entity'));

        $refreshLeftAndRight = function($root, $left) use ($repository, &$refreshLeftAndRight) {
            $right = $left + 1;
            $children = $repository->findBy([
                'parent' => $root,
            ]);

            foreach ($children as $entity) {
                // recursive execution of this function for each
                // child of this node
                // $right is the current right value, which is
                // incremented by the refreshLeftAndRight function
                $right = $refreshLeftAndRight($entity, $right);
            }

            // we've got the left value, and now that we've processed
            // the children of this node we also know the right value
            $this->updateValue($left, $root, 'left');
            $this->updateValue($right, $root, 'right');
            return $right + 1;
        };

        foreach ($repository->findBy(['parent' => null]) as $rootEntry) {
            $refreshLeftAndRight($rootEntry, 1);
        }

        $em->flush();

        foreach ($repository->findAll() as $entity) {
            $level = 0;
            $parent = $entity;

            /** @noinspection PhpUndefinedMethodInspection */
            while ($parent = $parent->getParent()) {
                $level++;
            }

            $this->updateValue($level, $entity, 'level');
        }

        $em->flush();
    }

    /**
     * @param mixed $value
     * @param object $entity
     * @param string $property
     */
    private function updateValue($value, $entity, $property)
    {
        $reflection = new \ReflectionProperty(get_class($entity), $property);
        $reflection->setAccessible(true);
        $reflection->setValue($entity, $value);
    }
}

https://gist.github.com/harentius/d70af4a470cee0cec1bb36db966b0d9a