Nested удаление дерева, как пересчитывать lft, rgt?

Когда речь идет о удалении дерева с помощью вложенных множеств, очень важно пересчитать значения lft (левый индекс) и rgt (правый индекс) для остальных узлов в дереве.

Перед тем, как пересчитать эти значения, нужно определить, какие узлы будут удалены. В Symfony есть встроенная функция preRemove для обнаружения предстоящего удаления объекта. В этой функции вы можете определить всех родителей и детей удаляемого узла.

После определения удаляемых узлов вы должны обновить lft и rgt для всех остальных узлов в дереве. Это делается с помощью алгоритма, который включает следующие шаги:

1. Увеличьте все lft и rgt узлов, у которых их текущие значения больше, чем rgt удаляемого узла. Это нужно сделать, чтобы сделать место для удаления узлов.
2. Вычтите разницу между rgt и lft удаленного узла из всех rgt, которые находятся выше удаляемого узла. Таким образом, вы уменьшите значения rgt для всех родительских узлов дерева.
3. Вычтите разницу между rgt и lft (плюс единица) удаленного узла из всех lft, которые находятся выше удаляемого узла. Это уменьшает значение lft для всех узлов выше удаленного узла.
4. После этого вы можете удалить сами узлы из базы данных.

Вам также нужно учесть, что пересчет lft и rgt может вызывать изменения в базе данных, поэтому рекомендуется выполнять эту операцию в одной транзакции для обеспечения целостности данных.

Пример кода для выполнения этих шагов может выглядеть следующим образом:

/**
 * @ORMPreRemove()
 */
public function preRemove()
{
    $parent = $this->getParent(); // получить родительский узел
    $children = $this->getChildren(); // получить дочерние узлы

    $difference = $this->getRgt() - $this->getLft() + 1; // вычислить разницу между rgt и lft узла

    // Обновите lft и rgt для узлов выше удаленного узла
    // увеличьте lft и rgt для узлов, у которых их текущие значения больше, чем rgt удаляемого узла
    $qb = $this->createQueryBuilder('n')
        ->update()
        ->set('n.lft', 'n.lft + :difference')
        ->set('n.rgt', 'n.rgt + :difference')
        ->where('n.rgt > :rgt')
        ->setParameter('difference', $difference)
        ->setParameter('rgt', $this->getRgt())
        ->getQuery();
    $qb->execute();

    // уменьшить rgt для всех родительских узлов дерева
    if ($parent) {
        $qb = $this->createQueryBuilder('n')
            ->update()
            ->set('n.rgt', 'n.rgt - :difference')
            ->where('n.lft < :lft')
            ->andWhere('n.rgt > :rgt')
            ->andWhere('n.root = :root')
            ->setParameter('difference', $difference)
            ->setParameter('lft', $this->getLft())
            ->setParameter('rgt', $this->getRgt())
            ->setParameter('root', $this->getRoot())
            ->getQuery();
        $qb->execute();
    }

    // уменьшить lft для всех узлов выше удаленного узла
    if ($parent) {
        $qb = $this->createQueryBuilder('n')
            ->update()
            ->set('n.lft', 'n.lft - :difference')
            ->where('n.lft > :lft')
            ->andWhere('n.root = :root')
            ->setParameter('difference', $difference)
            ->setParameter('lft', $this->getRgt())
            ->setParameter('root', $this->getRoot())
            ->getQuery();
        $qb->execute();
    }

    // удалить сами узлы
    $qb = $this->createQueryBuilder('n')
        ->delete()
        ->where('n.lft >= :lft')
        ->andWhere('n.rgt <= :rgt')
        ->andWhere('n.root = :root')
        ->setParameter('lft', $this->getLft())
        ->setParameter('rgt', $this->getRgt())
        ->setParameter('root', $this->getRoot())
        ->getQuery();
    $qb->execute();
}

Это всего лишь пример, и вам может потребоваться настроить его для ваших конкретных требований и моделей данных в Symfony. Однако он предоставляет общую идею о том, как пересчитывать значения lft и rgt при удалении узлов в дереве, используя вложенные множества.