propelorm/Propel2

src/Propel/Generator/Behavior/NestedSet/NestedSetBehaviorQueryBuilderModifier.php
F

Complex method in addUpdateLoadedNodes

    protected function addUpdateLoadedNodes(&$script)
    {
        $queryClassName  = $this->queryClassName;
        $objectClassName = $this->objectClassName;
        $tableMapClassName = $this->tableMapClassName;

Long or complex methods can make code harder to understand. In most circumstances, methods are best as a small chunk of code (the "how") with a clear, understandable name (the "what"). Long methods can also lead to duplication, as it's harder to reuse logic that is tightly coupled to surrounding code.

Refactorings

Read More

File is too long

<?php

/**
 * This file is part of the Propel package.
 * For the full copyright and license information, please view the LICENSE

Method addUpdateLoadedNodes is too long

    protected function addUpdateLoadedNodes(&$script)
    {
        $queryClassName  = $this->queryClassName;
        $objectClassName = $this->objectClassName;
        $tableMapClassName = $this->tableMapClassName;

Method addFixLevels is too long

    protected function addFixLevels(&$script)
    {
        $objectClassName   = $this->objectClassName;
        $queryClassName    = $this->queryClassName;
        $tableMapClassName = $this->tableMapClassName;

Method addShiftRLValues is too long

    protected function addShiftRLValues(&$script)
    {
        $objectClassName   = $this->objectClassName;
        $useScope          = $this->behavior->useScope();
        $tableMapClassName = $this->builder->getTableMapClass();

The variable name, n, is too short

        $n = 0;

Identical code found in 3 other locations

<?php

/**
 * This file is part of the Propel package.
 * For the full copyright and license information, please view the LICENSE
src/Propel/Generator/Behavior/NestedSet/NestedSetBehaviorQueryBuilderModifier.php on lines 167..188
src/Propel/Generator/Behavior/NestedSet/NestedSetBehaviorQueryBuilderModifier.php on lines 240..260
src/Propel/Generator/Behavior/NestedSet/NestedSetBehaviorQueryBuilderModifier.php on lines 265..286

Duplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:

Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.

When you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).

Refactorings

Further Reading

Identical code found in 3 other locations

<?php

/**
 * This file is part of the Propel package.
 * For the full copyright and license information, please view the LICENSE
src/Propel/Generator/Behavior/NestedSet/NestedSetBehaviorQueryBuilderModifier.php on lines 142..162
src/Propel/Generator/Behavior/NestedSet/NestedSetBehaviorQueryBuilderModifier.php on lines 167..188
src/Propel/Generator/Behavior/NestedSet/NestedSetBehaviorQueryBuilderModifier.php on lines 240..260

Duplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:

Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.

When you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).

Refactorings

Further Reading

Identical code found in 3 other locations

<?php

/**
 * This file is part of the Propel package.
 * For the full copyright and license information, please view the LICENSE
src/Propel/Generator/Behavior/NestedSet/NestedSetBehaviorQueryBuilderModifier.php on lines 142..162
src/Propel/Generator/Behavior/NestedSet/NestedSetBehaviorQueryBuilderModifier.php on lines 167..188
src/Propel/Generator/Behavior/NestedSet/NestedSetBehaviorQueryBuilderModifier.php on lines 265..286

Duplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:

Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.

When you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).

Refactorings

Further Reading

Identical code found in 3 other locations

<?php

/**
 * This file is part of the Propel package.
 * For the full copyright and license information, please view the LICENSE
src/Propel/Generator/Behavior/NestedSet/NestedSetBehaviorQueryBuilderModifier.php on lines 142..162
src/Propel/Generator/Behavior/NestedSet/NestedSetBehaviorQueryBuilderModifier.php on lines 240..260
src/Propel/Generator/Behavior/NestedSet/NestedSetBehaviorQueryBuilderModifier.php on lines 265..286

Duplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:

Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.

When you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).

Refactorings

Further Reading

Identical code found in 1 other location

<?php

/**
 * This file is part of the Propel package.
 * For the full copyright and license information, please view the LICENSE
src/Propel/Generator/Behavior/NestedSet/NestedSetBehaviorQueryBuilderModifier.php on lines 446..475

Duplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:

Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.

When you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).

Refactorings

Further Reading

Identical code found in 1 other location

<?php

/**
 * This file is part of the Propel package.
 * For the full copyright and license information, please view the LICENSE
src/Propel/Generator/Behavior/NestedSet/NestedSetBehaviorQueryBuilderModifier.php on lines 480..512

Duplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:

Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.

When you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).

Refactorings

Further Reading

There are no issues that match your filters.

<?php

/**
 * This file is part of the Propel package.
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @license MIT License
 */

namespace Propel\Generator\Behavior\NestedSet;

use Propel\Generator\Builder\Om\QueryBuilder;
use Propel\Generator\Model\Table;

/**
 * Behavior to adds nested set tree structure columns and abilities
 *
 * @author Fran├žois Zaninotto
 */
class NestedSetBehaviorQueryBuilderModifier
{
    /** @var NestedSetBehavior */
    protected $behavior;

    /** @var Table */
    protected $table;

    /** @var QueryBuilder */
    protected $builder;

    protected $objectClassName;

    protected $queryClassName;

    protected $tableMapClassName;

    public function __construct(NestedSetBehavior $behavior)
    {
        $this->behavior = $behavior;
        $this->table = $behavior->getTable();
    }

    protected function getParameter($key)
    {
        return $this->behavior->getParameter($key);
    }

    protected function getColumn($name)
    {
        return $this->behavior->getColumnForParameter($name);
    }

    protected function setBuilder(QueryBuilder $builder)
    {
        $this->builder           = $builder;
        $this->objectClassName   = $builder->getObjectClassName();
        $this->queryClassName    = $builder->getQueryClassName();
        $this->tableMapClassName = $builder->getTableMapClassName();
    }

    public function queryMethods(QueryBuilder $builder)
    {
        $this->setBuilder($builder);
        $script = '';

        // select filters
        if ($this->behavior->useScope()) {
            $this->addTreeRoots($script);
            $this->addInTree($script);
        }

        $this->addDescendantsOf($script);
        $this->addBranchOf($script);
        $this->addChildrenOf($script);
        $this->addSiblingsOf($script);
        $this->addAncestorsOf($script);
        $this->addRootsOf($script);
        // select orders
        $this->addOrderByBranch($script);
        $this->addOrderByLevel($script);
        // select termination methods
        $this->addFindRoot($script);
        if ($this->behavior->useScope()) {
            $this->addFindRoots($script);
        }
        $this->addFindTree($script);

        if ($this->behavior->useScope()) {
            $this->addRetrieveRoots($script);
        }

        $this->addRetrieveRoot($script);
        $this->addRetrieveTree($script);
        $this->addIsValid($script);
        $this->addDeleteTree($script);
        $this->addShiftRLValues($script);
        $this->addShiftLevel($script);
        $this->addUpdateLoadedNodes($script);
        $this->addMakeRoomForLeaf($script);
        $this->addFixLevels($script);

        if ($this->behavior->useScope()) {
            $this->addSetNegativeScope($script);
        }

        return $script;
    }

    protected function addTreeRoots(&$script)
    {
        $script .= "
/**
 * Filter the query to restrict the result to root objects
 *
 * @return    \$this|{$this->queryClassName} The current query, for fluid interface
 */
public function treeRoots()
{
    return \$this->addUsingAlias({$this->objectClassName}::LEFT_COL, 1, Criteria::EQUAL);
}
";
    }

    protected function addInTree(&$script)
    {
        $script .= "
/**
 * Returns the objects in a certain tree, from the tree scope
 *
 * @param     int \$scope        Scope to determine which objects node to return
 *
 * @return    \$this|{$this->queryClassName} The current query, for fluid interface
 */
public function inTree(\$scope = null)
{
    return \$this->addUsingAlias({$this->objectClassName}::SCOPE_COL, \$scope, Criteria::EQUAL);
}
";
    }

    protected function addDescendantsOf(&$script)
    {
        $objectName = '$' . $this->table->getCamelCaseName();
        $script .= "
/**
 * Filter the query to restrict the result to descendants of an object
 *
 * @param     {$this->objectClassName} $objectName The object to use for descendant search
 *
 * @return    \$this|{$this->queryClassName} The current query, for fluid interface
 */
public function descendantsOf($this->objectClassName $objectName)
{
    return \$this";
        if ($this->behavior->useScope()) {
            $script .= "
        ->inTree({$objectName}->getScopeValue())";
        }
        $script .= "
        ->addUsingAlias({$this->objectClassName}::LEFT_COL, {$objectName}->getLeftValue(), Criteria::GREATER_THAN)
        ->addUsingAlias({$this->objectClassName}::LEFT_COL, {$objectName}->getRightValue(), Criteria::LESS_THAN);
}
";
    }

    protected function addBranchOf(&$script)
    {
        $objectName = '$' . $this->table->getCamelCaseName();
        $script .= "
/**
 * Filter the query to restrict the result to the branch of an object.
 * Same as descendantsOf(), except that it includes the object passed as parameter in the result
 *
 * @param     {$this->objectClassName} $objectName The object to use for branch search
 *
 * @return    \$this|{$this->queryClassName} The current query, for fluid interface
 */
public function branchOf($this->objectClassName $objectName)
{
    return \$this";
        if ($this->behavior->useScope()) {
            $script .= "
        ->inTree({$objectName}->getScopeValue())";
        }
        $script .= "
        ->addUsingAlias({$this->objectClassName}::LEFT_COL, {$objectName}->getLeftValue(), Criteria::GREATER_EQUAL)
        ->addUsingAlias({$this->objectClassName}::LEFT_COL, {$objectName}->getRightValue(), Criteria::LESS_EQUAL);
}
";
    }

    protected function addChildrenOf(&$script)
    {
        $objectName = '$' . $this->table->getCamelCaseName();
        $script .= "
/**
 * Filter the query to restrict the result to children of an object
 *
 * @param     {$this->objectClassName} $objectName The object to use for child search
 *
 * @return    \$this|{$this->queryClassName} The current query, for fluid interface
 */
public function childrenOf($this->objectClassName $objectName)
{
    return \$this
        ->descendantsOf($objectName)
        ->addUsingAlias({$this->objectClassName}::LEVEL_COL, {$objectName}->getLevel() + 1, Criteria::EQUAL);
}
";
    }

    protected function addSiblingsOf(&$script)
    {
        $objectName = '$' . $this->table->getCamelCaseName();
        $script .= "
/**
 * Filter the query to restrict the result to siblings of an object.
 * The result does not include the object passed as parameter.
 *
 * @param     {$this->objectClassName} $objectName The object to use for sibling search
 * @param      ConnectionInterface \$con Connection to use.
 *
 * @return    \$this|{$this->queryClassName} The current query, for fluid interface
 */
public function siblingsOf($this->objectClassName $objectName, ConnectionInterface \$con = null)
{
    if ({$objectName}->isRoot()) {
        return \$this->
            add({$this->objectClassName}::LEVEL_COL, '1<>1', Criteria::CUSTOM);
    } else {
        return \$this
            ->childrenOf({$objectName}->getParent(\$con))
            ->prune($objectName);
    }
}
";
    }

    protected function addAncestorsOf(&$script)
    {
        $objectName = '$' . $this->table->getCamelCaseName();
        $script .= "
/**
 * Filter the query to restrict the result to ancestors of an object
 *
 * @param     {$this->objectClassName} $objectName The object to use for ancestors search
 *
 * @return    \$this|{$this->queryClassName} The current query, for fluid interface
 */
public function ancestorsOf($this->objectClassName $objectName)
{
    return \$this";
        if ($this->behavior->useScope()) {
            $script .= "
        ->inTree({$objectName}->getScopeValue())";
        }
        $script .= "
        ->addUsingAlias({$this->objectClassName}::LEFT_COL, {$objectName}->getLeftValue(), Criteria::LESS_THAN)
        ->addUsingAlias({$this->objectClassName}::RIGHT_COL, {$objectName}->getRightValue(), Criteria::GREATER_THAN);
}
";
    }

    protected function addRootsOf(&$script)
    {
        $objectName = '$' . $this->table->getCamelCaseName();
        $script .= "
/**
 * Filter the query to restrict the result to roots of an object.
 * Same as ancestorsOf(), except that it includes the object passed as parameter in the result
 *
 * @param     {$this->objectClassName} $objectName The object to use for roots search
 *
 * @return    \$this|{$this->queryClassName} The current query, for fluid interface
 */
public function rootsOf($this->objectClassName $objectName)
{
    return \$this";
        if ($this->behavior->useScope()) {
            $script .= "
        ->inTree({$objectName}->getScopeValue())";
        }
        $script .= "
        ->addUsingAlias({$this->objectClassName}::LEFT_COL, {$objectName}->getLeftValue(), Criteria::LESS_EQUAL)
        ->addUsingAlias({$this->objectClassName}::RIGHT_COL, {$objectName}->getRightValue(), Criteria::GREATER_EQUAL);
}
";
    }

    protected function addOrderByBranch(&$script)
    {
        $script .= "
/**
 * Order the result by branch, i.e. natural tree order
 *
 * @param     bool \$reverse if true, reverses the order
 *
 * @return    \$this|{$this->queryClassName} The current query, for fluid interface
 */
public function orderByBranch(\$reverse = false)
{
    if (\$reverse) {
        return \$this
            ->addDescendingOrderByColumn({$this->objectClassName}::LEFT_COL);
    } else {
        return \$this
            ->addAscendingOrderByColumn({$this->objectClassName}::LEFT_COL);
    }
}
";
    }

    protected function addOrderByLevel(&$script)
    {
        $script .= "
/**
 * Order the result by level, the closer to the root first
 *
 * @param     bool \$reverse if true, reverses the order
 *
 * @return    \$this|{$this->queryClassName} The current query, for fluid interface
 */
public function orderByLevel(\$reverse = false)
{
    if (\$reverse) {
        return \$this
            ->addDescendingOrderByColumn({$this->objectClassName}::LEVEL_COL)
            ->addDescendingOrderByColumn({$this->objectClassName}::LEFT_COL);
    } else {
        return \$this
            ->addAscendingOrderByColumn({$this->objectClassName}::LEVEL_COL)
            ->addAscendingOrderByColumn({$this->objectClassName}::LEFT_COL);
    }
}
";
    }

    protected function addFindRoot(&$script)
    {
        $useScope = $this->behavior->useScope();
        $script .= "
/**
 * Returns " . ($useScope ? 'a' : 'the') ." root node for the tree
 *";
        if ($useScope) {
            $script .= "
 * @param      int \$scope        Scope to determine which root node to return";
        }

        $script .= "
 * @param      ConnectionInterface \$con    Connection to use.
 *
 * @return     {$this->objectClassName} The tree root object
 */
public function findRoot(" . ($useScope ? "\$scope = null, " : "") . "ConnectionInterface \$con = null)
{
    return \$this
        ->addUsingAlias({$this->objectClassName}::LEFT_COL, 1, Criteria::EQUAL)";
        if ($useScope) {
            $script .= "
        ->inTree(\$scope)";
        }
        $script .= "
        ->findOne(\$con);
}
";
    }

    protected function addFindRoots(&$script)
    {
        $script .= "
/**
 * Returns the root objects for all trees.
 *
 * @param      ConnectionInterface \$con    Connection to use.
 *
 * @return    {$this->objectClassName}[]|ObjectCollection|mixed the list of results, formatted by the current formatter
 */
public function findRoots(ConnectionInterface \$con = null)
{
    return \$this
        ->treeRoots()
        ->find(\$con);
}
";
    }

    protected function addFindTree(&$script)
    {
        $useScope = $this->behavior->useScope();
        $script .= "
/**
 * Returns " . ($useScope ? 'a' : 'the') ." tree of objects
 *";
        if ($useScope) {
            $script .= "
 * @param      int \$scope        Scope to determine which tree node to return";
        }

        $script .= "
 * @param      ConnectionInterface \$con    Connection to use.
 *
 * @return     {$this->objectClassName}[]|ObjectCollection|mixed the list of results, formatted by the current formatter
 */
public function findTree(" . ($useScope ? "\$scope = null, " : "") . "ConnectionInterface \$con = null)
{
    return \$this";
        if ($useScope) {
            $script .= "
        ->inTree(\$scope)";
        }
        $script .= "
        ->orderByBranch()
        ->find(\$con);
}
";
    }

    protected function addRetrieveRoots(&$script)
    {
        $queryClassName     = $this->queryClassName;
        $objectClassName   = $this->objectClassName;
        $tableMapClassName = $this->builder->getTableMapClass();

        $script .= "
/**
 * Returns the root nodes for the tree
 *
 * @param      Criteria \$criteria    Optional Criteria to filter the query
 * @param      ConnectionInterface \$con    Connection to use.
 * @return     {$this->objectClassName}[]|ObjectCollection|mixed the list of results, formatted by the current formatter
 */
static public function retrieveRoots(Criteria \$criteria = null, ConnectionInterface \$con = null)
{
    if (null === \$criteria) {
        \$criteria = new Criteria($tableMapClassName::DATABASE_NAME);
    }
    \$criteria->add($objectClassName::LEFT_COL, 1, Criteria::EQUAL);

    return $queryClassName::create(null, \$criteria)->find(\$con);
}
";
    }

    protected function addRetrieveRoot(&$script)
    {
        $queryClassName    = $this->queryClassName;
        $objectClassName   = $this->objectClassName;
        $useScope          = $this->behavior->useScope();
        $tableMapClassName = $this->builder->getTableMapClass();

        $script .= "
/**
 * Returns the root node for a given scope
 *";
        if ($useScope) {
            $script .= "
 * @param      int \$scope        Scope to determine which root node to return";
        }
        $script .= "
 * @param      ConnectionInterface \$con    Connection to use.
 * @return     {$this->objectClassName}            Propel object for root node
 */
static public function retrieveRoot(" . ($useScope ? "\$scope = null, " : "") . "ConnectionInterface \$con = null)
{
    \$c = new Criteria($tableMapClassName::DATABASE_NAME);
    \$c->add($objectClassName::LEFT_COL, 1, Criteria::EQUAL);";
        if ($useScope) {
            $script .= "
    \$c->add($objectClassName::SCOPE_COL, \$scope, Criteria::EQUAL);";
        }
        $script .= "

    return $queryClassName::create(null, \$c)->findOne(\$con);
}
";
    }

    protected function addRetrieveTree(&$script)
    {
        $queryClassName     = $this->queryClassName;
        $objectClassName   = $this->objectClassName;
        $useScope          = $this->behavior->useScope();
        $tableMapClassName = $this->builder->getTableMapClass();

        $script .= "
/**
 * Returns the whole tree node for a given scope
 *";
        if ($useScope) {
            $script .= "
 * @param      int \$scope        Scope to determine which root node to return";
        }
        $script .= "
 * @param      Criteria \$criteria    Optional Criteria to filter the query
 * @param      ConnectionInterface \$con    Connection to use.
 * @return     {$this->objectClassName}[]|ObjectCollection|mixed the list of results, formatted by the current formatter
 */
static public function retrieveTree(" . ($useScope ? "\$scope = null, " : "") . "Criteria \$criteria = null, ConnectionInterface \$con = null)
{
    if (null === \$criteria) {
        \$criteria = new Criteria($tableMapClassName::DATABASE_NAME);
    }
    \$criteria->addAscendingOrderByColumn($objectClassName::LEFT_COL);";
        if ($useScope) {
            $script .= "
    \$criteria->add($objectClassName::SCOPE_COL, \$scope, Criteria::EQUAL);";
        }
        $script .= "

    return $queryClassName::create(null, \$criteria)->find(\$con);
}
";
    }

    protected function addIsValid(&$script)
    {
        $objectClassName = $this->objectClassName;

        $script .= "
/**
 * Tests if node is valid
 *
 * @param      $objectClassName \$node    Propel object for src node
 * @return     bool
 */
static public function isValid($objectClassName \$node = null)
{
    if (is_object(\$node) && \$node->getRightValue() > \$node->getLeftValue()) {
        return true;
    } else {
        return false;
    }
}
";
    }

    protected function addDeleteTree(&$script)
    {
        $objectClassName   = $this->objectClassName;
        $useScope          = $this->behavior->useScope();
        $tableMapClassName = $this->builder->getTableMapClass();

        $script .= "
/**
 * Delete an entire tree
 * ";
        if ($useScope) {
            $script .= "
 * @param      int \$scope        Scope to determine which tree to delete";
        }
        $script .= "
 * @param      ConnectionInterface \$con    Connection to use.
 *
 * @return     int  The number of deleted nodes
 */
static public function deleteTree(" . ($useScope ? "\$scope = null, " : "") . "ConnectionInterface \$con = null)
{";
        if ($useScope) {
            $script .= "
    \$c = new Criteria($tableMapClassName::DATABASE_NAME);
    \$c->add($objectClassName::SCOPE_COL, \$scope, Criteria::EQUAL);

    return $tableMapClassName::doDelete(\$c, \$con);";
        } else {
            $script .= "

    return $tableMapClassName::doDeleteAll(\$con);";
        }
        $script .= "
}
";
    }

    protected function addShiftRLValues(&$script)
    {
        $objectClassName   = $this->objectClassName;
        $useScope          = $this->behavior->useScope();
        $tableMapClassName = $this->builder->getTableMapClass();

        $this->builder->declareClass('Propel\\Runtime\Map\\TableMap');

        $script .= "
/**
 * Adds \$delta to all L and R values that are >= \$first and <= \$last.
 * '\$delta' can also be negative.
 *
 * @param int \$delta               Value to be shifted by, can be negative
 * @param int \$first               First node to be shifted
 * @param int \$last                Last node to be shifted (optional)";
        if ($useScope) {
            $script .= "
 * @param int \$scope               Scope to use for the shift";
        }
        $script .= "
 * @param ConnectionInterface \$con Connection to use.
 */
static public function shiftRLValues(\$delta, \$first, \$last = null" . ($useScope ? ", \$scope = null" : ""). ", ConnectionInterface \$con = null)
{
    if (\$con === null) {
        \$con = Propel::getServiceContainer()->getWriteConnection($tableMapClassName::DATABASE_NAME);
    }

    // Shift left column values
    \$whereCriteria = new Criteria($tableMapClassName::DATABASE_NAME);
    \$criterion = \$whereCriteria->getNewCriterion($objectClassName::LEFT_COL, \$first, Criteria::GREATER_EQUAL);
    if (null !== \$last) {
        \$criterion->addAnd(\$whereCriteria->getNewCriterion($objectClassName::LEFT_COL, \$last, Criteria::LESS_EQUAL));
    }
    \$whereCriteria->add(\$criterion);";
        if ($useScope) {
            $script .= "
    \$whereCriteria->add($objectClassName::SCOPE_COL, \$scope, Criteria::EQUAL);";
        }
        $script .= "

    \$valuesCriteria = new Criteria($tableMapClassName::DATABASE_NAME);
    \$valuesCriteria->add($objectClassName::LEFT_COL, array('raw' => $objectClassName::LEFT_COL . ' + ?', 'value' => \$delta), Criteria::CUSTOM_EQUAL);

    \$whereCriteria->doUpdate(\$valuesCriteria, \$con);

    // Shift right column values
    \$whereCriteria = new Criteria($tableMapClassName::DATABASE_NAME);
    \$criterion = \$whereCriteria->getNewCriterion($objectClassName::RIGHT_COL, \$first, Criteria::GREATER_EQUAL);
    if (null !== \$last) {
        \$criterion->addAnd(\$whereCriteria->getNewCriterion($objectClassName::RIGHT_COL, \$last, Criteria::LESS_EQUAL));
    }
    \$whereCriteria->add(\$criterion);";
        if ($useScope) {
            $script .= "
    \$whereCriteria->add($objectClassName::SCOPE_COL, \$scope, Criteria::EQUAL);";
        }
        $script .= "

    \$valuesCriteria = new Criteria($tableMapClassName::DATABASE_NAME);
    \$valuesCriteria->add($objectClassName::RIGHT_COL, array('raw' => $objectClassName::RIGHT_COL . ' + ?', 'value' => \$delta), Criteria::CUSTOM_EQUAL);

    \$whereCriteria->doUpdate(\$valuesCriteria, \$con);
}
";
    }

    protected function addShiftLevel(&$script)
    {
        $objectClassName   = $this->objectClassName;
        $useScope          = $this->behavior->useScope();
        $tableMapClassName = $this->builder->getTableMapClass();

        $this->builder->declareClass('Propel\\Runtime\Map\\TableMap');

        $script .= "
/**
 * Adds \$delta to level for nodes having left value >= \$first and right value <= \$last.
 * '\$delta' can also be negative.
 *
 * @param      int \$delta        Value to be shifted by, can be negative
 * @param      int \$first        First node to be shifted
 * @param      int \$last            Last node to be shifted";
        if ($useScope) {
            $script .= "
 * @param      int \$scope        Scope to use for the shift";
        }
        $script .= "
 * @param      ConnectionInterface \$con        Connection to use.
 */
static public function shiftLevel(\$delta, \$first, \$last" . ($useScope ? ", \$scope = null" : ""). ", ConnectionInterface \$con = null)
{
    if (\$con === null) {
        \$con = Propel::getServiceContainer()->getWriteConnection($tableMapClassName::DATABASE_NAME);
    }

    \$whereCriteria = new Criteria($tableMapClassName::DATABASE_NAME);
    \$whereCriteria->add($objectClassName::LEFT_COL, \$first, Criteria::GREATER_EQUAL);
    \$whereCriteria->add($objectClassName::RIGHT_COL, \$last, Criteria::LESS_EQUAL);";
        if ($useScope) {
            $script .= "
    \$whereCriteria->add($objectClassName::SCOPE_COL, \$scope, Criteria::EQUAL);";
        }
        $script .= "

    \$valuesCriteria = new Criteria($tableMapClassName::DATABASE_NAME);
    \$valuesCriteria->add($objectClassName::LEVEL_COL, array('raw' => $objectClassName::LEVEL_COL . ' + ?', 'value' => \$delta), Criteria::CUSTOM_EQUAL);

    \$whereCriteria->doUpdate(\$valuesCriteria, \$con);
}
";
    }

    protected function addUpdateLoadedNodes(&$script)
    {
        $queryClassName  = $this->queryClassName;
        $objectClassName = $this->objectClassName;
        $tableMapClassName = $this->tableMapClassName;

        $script .= "
/**
 * Reload all already loaded nodes to sync them with updated db
 *
 * @param      $objectClassName \$prune        Object to prune from the update
 * @param      ConnectionInterface \$con        Connection to use.
 */
static public function updateLoadedNodes(\$prune = null, ConnectionInterface \$con = null)
{
    if (Propel::isInstancePoolingEnabled()) {
        \$keys = array();
        /** @var \$obj $objectClassName */
        foreach ($tableMapClassName::\$instances as \$obj) {
            if (!\$prune || !\$prune->equals(\$obj)) {
                \$keys[] = \$obj->getPrimaryKey();
            }
        }

        if (!empty(\$keys)) {
            // We don't need to alter the object instance pool; we're just modifying these ones
            // already in the pool.
            \$criteria = new Criteria($tableMapClassName::DATABASE_NAME);";
        if (1 === count($this->table->getPrimaryKey())) {
            $pkey = $this->table->getPrimaryKey();
            $col = array_shift($pkey);
            $script .= "
            \$criteria->add(".$this->builder->getColumnConstant($col).", \$keys, Criteria::IN);";
        } else {
            $fields = [];
            foreach ($this->table->getPrimaryKey() as $k => $col) {
                $fields[] = $this->builder->getColumnConstant($col);
            };
            $script .= "

            // Loop on each instances in pool
            foreach (\$keys as \$values) {
              // Create initial Criterion
                \$cton = \$criteria->getNewCriterion(" . $fields[0] . ", \$values[0]);";
            unset($fields[0]);
            foreach ($fields as $k => $col) {
                $script .= "

                // Create next criterion
                \$nextcton = \$criteria->getNewCriterion(" . $col . ", \$values[$k]);
                // And merge it with the first
                \$cton->addAnd(\$nextcton);";
            }
            $script .= "

                // Add final Criterion to Criteria
                \$criteria->addOr(\$cton);
            }";
        }

        $script .= "
            \$dataFetcher = $queryClassName::create(null, \$criteria)->setFormatter(ModelCriteria::FORMAT_STATEMENT)->find(\$con);
            while (\$row = \$dataFetcher->fetch()) {
                \$key = $tableMapClassName::getPrimaryKeyHashFromRow(\$row, 0);
                /** @var \$object $objectClassName */
                if (null !== (\$object = $tableMapClassName::getInstanceFromPool(\$key))) {";
        $n = 0;
        foreach ($this->table->getColumns() as $col) {
            if ($col->isLazyLoad()) {
                continue;
            }
            if ($col->getPhpName() == $this->getColumnPhpName('left_column')) {
                $script .= "
                    \$object->setLeftValue(\$row[$n]);";
            } elseif ($col->getPhpName() == $this->getColumnPhpName('right_column')) {
                $script .= "
                    \$object->setRightValue(\$row[$n]);";
            } elseif ($this->getParameter('use_scope') == 'true' && $col->getPhpName() == $this->getColumnPhpName('scope_column')) {
                $script .= "
                    \$object->setScopeValue(\$row[$n]);";
            } elseif ($col->getPhpName() == $this->getColumnPhpName('level_column')) {
                $script .= "
                    \$object->setLevel(\$row[$n]);
                    \$object->clearNestedSetChildren();";
            }
            $n++;
        }
        $script .= "
                }
            }
            \$dataFetcher->close();
        }
    }
}
";
    }

    protected function addMakeRoomForLeaf(&$script)
    {
        $queryClassName = $this->queryClassName;
        $useScope       = $this->behavior->useScope();

        $script .= "
/**
 * Update the tree to allow insertion of a leaf at the specified position
 *
 * @param      int \$left    left column value";
        if ($useScope) {
            $script .= "
 * @param      integer \$scope    scope column value";
        }
        $script .= "
 * @param      mixed \$prune    Object to prune from the shift
 * @param      ConnectionInterface \$con    Connection to use.
 */
static public function makeRoomForLeaf(\$left" . ($useScope ? ", \$scope" : ""). ", \$prune = null, ConnectionInterface \$con = null)
{
    // Update database nodes
    $queryClassName::shiftRLValues(2, \$left, null" . ($useScope ? ", \$scope" : "") . ", \$con);

    // Update all loaded nodes
    $queryClassName::updateLoadedNodes(\$prune, \$con);
}
";
    }

    protected function addFixLevels(&$script)
    {
        $objectClassName   = $this->objectClassName;
        $queryClassName    = $this->queryClassName;
        $tableMapClassName = $this->tableMapClassName;
        $useScope          = $this->behavior->useScope();

        $script .= "
/**
 * Update the tree to allow insertion of a leaf at the specified position
 *";
        if ($useScope) {
            $script .= "
 * @param      integer \$scope    scope column value";
        }
        $script .= "
 * @param      ConnectionInterface \$con    Connection to use.
 */
static public function fixLevels(" . ($useScope ? "\$scope, " : ""). "ConnectionInterface \$con = null)
{
    \$c = new Criteria();";
        if ($useScope) {
            $script .= "
    \$c->add($objectClassName::SCOPE_COL, \$scope, Criteria::EQUAL);";
        }
        $script .= "
    \$c->addAscendingOrderByColumn($objectClassName::LEFT_COL);
    \$dataFetcher = $queryClassName::create(null, \$c)->setFormatter(ModelCriteria::FORMAT_STATEMENT)->find(\$con);
    ";
        if (!$this->table->getChildrenColumn()) {
            $script .= "
    // set the class once to avoid overhead in the loop
    \$cls = $tableMapClassName::getOMClass(false);";
        }

        $script .= "
    \$level = null;
    // iterate over the statement
    while (\$row = \$dataFetcher->fetch()) {

        // hydrate object
        \$key = $tableMapClassName::getPrimaryKeyHashFromRow(\$row, 0);
        /** @var \$obj $objectClassName */
        if (null === (\$obj = $tableMapClassName::getInstanceFromPool(\$key))) {";
        if ($this->table->getChildrenColumn()) {
            $script .= "
            // class must be set each time from the record row
            \$cls = $tableMapClassName::getOMClass(\$row, 0);
            \$cls = substr('.'.\$cls, strrpos('.'.\$cls, '.') + 1);
            " . $this->builder->buildObjectInstanceCreationCode('$obj', '$cls') . "
            \$obj->hydrate(\$row);
            $tableMapClassName::addInstanceToPool(\$obj, \$key);";
        } else {
            $script .= "
            " . $this->builder->buildObjectInstanceCreationCode('$obj', '$cls') . "
            \$obj->hydrate(\$row);
            $tableMapClassName::addInstanceToPool(\$obj, \$key);";
        }
        $script .= "
        }

        // compute level
        // Algorithm shamelessly stolen from sfPropelActAsNestedSetBehaviorPlugin
        // Probably authored by Tristan Rivoallan
        if (\$level === null) {
            \$level = 0;
            \$i = 0;
            \$prev = array(\$obj->getRightValue());
        } else {
            while (\$obj->getRightValue() > \$prev[\$i]) {
                \$i--;
            }
            \$level = ++\$i;
            \$prev[\$i] = \$obj->getRightValue();
        }

        // update level in node if necessary
        if (\$obj->getLevel() !== \$level) {
            \$obj->setLevel(\$level);
            \$obj->save(\$con);
        }
    }
    \$dataFetcher->close();
}
";
    }

    protected function addSetNegativeScope(&$script)
    {
        $objectClassName   = $this->objectClassName;
        $tableMapClassName = $this->tableMapClassName;
        $script .= "
/**
 * Updates all scope values for items that has negative left (<=0) values.
 *
 * @param      mixed     \$scope
 * @param      ConnectionInterface \$con  Connection to use.
 */
public static function setNegativeScope(\$scope, ConnectionInterface \$con = null)
{
    //adjust scope value to \$scope
    \$whereCriteria = new Criteria($tableMapClassName::DATABASE_NAME);
    \$whereCriteria->add($objectClassName::LEFT_COL, 0, Criteria::LESS_EQUAL);

    \$valuesCriteria = new Criteria($tableMapClassName::DATABASE_NAME);
    \$valuesCriteria->add($objectClassName::SCOPE_COL, \$scope, Criteria::EQUAL);

    \$whereCriteria->doUpdate(\$valuesCriteria, \$con);
}
";
    }

    protected function getColumnPhpName($columnName)
    {
        return $this->behavior->getColumnForParameter($columnName)->getPhpName();
    }
}