Moves node inside one parent inside one root

@param null $oldPosition
@param null $position
@param array $siblings
@return array|bool
@throws \yii\db\Exception
348715
protected function reorder($oldPosition = null, $position = null, $siblings = [])
    {
        if (null === $oldPosition || null === $position || true === empty($siblings)) {
            return ['error' => Yii::t('jstw', 'Invalid data provided!')];
        }
        $nodeId = $siblings[$position];
        $class = $this->className;
        if ($oldPosition > $position) {
            //change next
            $nodeOperator = '-';
            $siblingsOperator = '+';
            $workWith = array_slice($siblings, $position, $oldPosition - $position + 1);
        } else if ($oldPosition < $position) {
            //change previous
            $nodeOperator = '+';
            $siblingsOperator = '-';
            $workWith = array_slice($siblings, $oldPosition, $position - $oldPosition + 1);
        } else {
            return true;
        }
        if (true === empty($workWith)) {
            return ['error' => Yii::t('jstw', 'Invalid data provided!')];
        }
        $lr = $workWithLr = $this->getLr($workWith);
        if (true === empty($lr)) {
            return ['error' => Yii::t('jstw', 'Invalid data provided!')];
        }
        unset($workWithLr[$nodeId]);
        $lft = array_column($workWithLr, $this->leftAttribute);
        $lft = min($lft);
        $rgt = array_column($workWithLr, $this->rightAttribute);
        $rgt = max($rgt);
        $nodeCondition = [
            'and',
            ['>=', $this->leftAttribute, $lft],
            ['<=', $this->rightAttribute, $rgt]
        ];
        $this->applyRootCondition($nodeCondition);
        $nodeDelta = $this->getCount($nodeCondition);
        $nodeDelta *= 2;
        $siblingsCondition = [
            'and',
            ['>=', $this->leftAttribute, $lr[$nodeId][$this->leftAttribute]],
            ['<=', $this->rightAttribute, $lr[$nodeId][$this->rightAttribute]]
        ];
        $this->applyRootCondition($siblingsCondition);
        $nodeChildren = $this->getChildIds($siblingsCondition);
        $siblingsDelta = count($nodeChildren) * 2;
        $db = Yii::$app->getDb();
        $transaction = $db->beginTransaction();
        try {
            //updating necessary node siblings
            $db->createCommand()->update(
                $class::tableName(),
                [
                    $this->leftAttribute => new Expression($this->leftAttribute . sprintf('%s%d', $siblingsOperator, $siblingsDelta)),
                    $this->rightAttribute => new Expression($this->rightAttribute . sprintf('%s%d', $siblingsOperator, $siblingsDelta)),
                ],
                $nodeCondition
            )->execute();
            //updating node
            $db->createCommand()->update(
                $class::tableName(),
                [
                    $this->leftAttribute => new Expression($this->leftAttribute . sprintf('%s%d', $nodeOperator, $nodeDelta)),
                    $this->rightAttribute => new Expression($this->rightAttribute . sprintf('%s%d', $nodeOperator, $nodeDelta)),
                ],
                ['id' => $nodeChildren]
            )->execute();
            $transaction->commit();
        } catch (\Exception $e) {
            $transaction->rollBack();
            return ['error' => $e->getMessage()];
        }
        return true;
    }
588165
public function insert_node(DOMElement $node, DOMElement $new_node, $pos)
    {
        if ($pos === "after" || !$node->firstChild) {
            $node->appendChild($new_node);
        } else {
            $node->insertBefore($new_node, $node->firstChild);
        }

        $this->_build_tree_r($new_node);

        $frame_id = $new_node->getAttribute("frame_id");
        $frame = $this->get_frame($frame_id);

        $parent_id = $node->getAttribute("frame_id");
        $parent = $this->get_frame($parent_id);

        if ($parent) {
            if ($pos === "before") {
                $parent->prepend_child($frame, false);
            } else {
                $parent->append_child($frame, false);
            }
        }

        return $frame_id;
    }
859646
public function removeChild($position = null, $forceDelete = false)
    {
        if ($this->exists) {
            $action = ($forceDelete === true ? 'forceDelete' : 'delete');

            $this->children($position)->$action();
        }

        return $this;
    }
960289
public function moveNode( $sourceNode, $position, $relatedNode )
    {
        $sourceNode = (int) $sourceNode;
        $sourceRoot = $this->getMapper()
                           ->findRootOf( $sourceNode );

        if ( empty( $sourceRoot ) ||
             ! $sourceRoot->isEditable() )
        {
            return array(
                'sourceNode'    => null,
                'position'      => $position,
                'relatedNode'   => $relatedNode,
                'success'       => false,
            );
        }

        $relatedNode = (int) $relatedNode;
        $relatedRoot = $this->getMapper()
                            ->findRootOf( $relatedNode );

        if ( empty( $relatedRoot ) ||
             ! $relatedRoot->isEditable() )
        {
            return array(
                'sourceNode'    => $sourceNode,
                'position'      => $position,
                'relatedNode'   => null,
                'success'       => false,
            );
        }

        $success = $this->getMapper()
                        ->moveNode( $sourceNode,
                                    strtolower( $position ),
                                    $relatedNode );

        return array(
            'sourceNode'    => $sourceNode,
            'position'      => $position,
            'relatedNode'   => $relatedNode,
            'success'       => (bool) $success,
        );
    }
951822
def prev_position(self, pos):
        """"""
        candidate = None
        if pos is not None:
            prevsib = self.prev_sibling_position(pos)  # is None if first
            if prevsib is not None:
                candidate = self.last_decendant(prevsib)
            else:
                parent = self.parent_position(pos)
                if parent is not None:
                    candidate = parent
        return candidate
260968
public function siblings()
    {
        if (!$this->nodes) {
            throw new \InvalidArgumentException('The current node list is empty.');
        }

        return $this->createSubCrawler($this->sibling($this->getNode(0)->parentNode->firstChild));
    }
281651
public function getNodeAt($position) {
        if (0 <= $position && $position <= count($this->nodes) - 1) {
            return $this->nodes[$position];
        }
        return null;
    }
647926
function move( $newParentNodeID, $nodeID = 0 )
    {
        if ( $nodeID == 0 )
        {
            $node = $this;
            $nodeID = $node->attribute( 'node_id' );
        }
        else
        {
            $node = eZContentObjectTreeNode::fetch( $nodeID );
        }

        $oldPath = $node->attribute( 'path_string' );
        $oldParentNodeID = $node->attribute( 'parent_node_id' );
        $newParentNodeID =(int) $newParentNodeID;
        if ( $oldParentNodeID != $newParentNodeID )
        {
            $node->updateAndStoreModified();
            // Who moves which content should be logged.
            $object = $node->object();
            eZAudit::writeAudit( 'content-move', array( 'Node ID' => $node->attribute( 'node_id' ),
                                                        'Old parent node ID' => $oldParentNodeID, 'New parent node ID' => $newParentNodeID,
                                                        'Object ID' => $object->attribute( 'id' ), 'Content Name' => $object->attribute( 'name' ),
                                                        'Comment' => 'Moved the node to the given node: eZContentObjectTreeNode::move()' ) );

            $newParentNode = eZContentObjectTreeNode::fetch( $newParentNodeID );
            $newParentPath = $newParentNode->attribute( 'path_string' );
            $newParentDepth = $newParentNode->attribute( 'depth' );
            $newPath = $newParentPath . $nodeID;
            $oldDepth = $node->attribute( 'depth' );

            $oldPathLength = strlen( $oldPath );
            $moveQuery = "UPDATE
                                 ezcontentobject_tree
                          SET
                                 parent_node_id = $newParentNodeID
                          WHERE
                                 node_id = $nodeID";
            $db = eZDB::instance();
            $subStringString = $db->subString( 'path_string', $oldPathLength );
            $newPathString   = $db->concatString( array( "'$newPath'", $subStringString ) );
            $moveQuery1 = "UPDATE
                                 ezcontentobject_tree
                           SET
                                 path_identification_string = " .
                                 $db->concatString(
                                     array(
                                         "'" . $db->escapeString( $newParentNode->PathIdentificationString ) . "'",
                                         $db->subString(
                                             "path_identification_string",
                                             mb_strlen( $node->PathIdentificationString ) + 1
                                         )
                                     )
                                 ) . ",
                                 path_string = $newPathString,
                                 depth = depth + $newParentDepth - $oldDepth + 1
                           WHERE
                                 path_string LIKE '$oldPath%'";
            $db->begin();
            $db->query( $moveQuery );
            $db->query( $moveQuery1 );

            /// role system clean up
            // Clean up policies and limitations

            $expireRoleCache = false;

            $limitationsToFix = eZPolicyLimitation::findByType( 'SubTree', $node->attribute( 'path_string' ), false );
            if ( count( $limitationsToFix ) > 0 )
            {
                $limitationIDString = $db->generateSQLINStatement( $limitationsToFix, 'limitation_id' );
                $subStringString =  $db->subString( 'value', $oldPathLength );
                $newValue = $db->concatString( array( "'$newPath'", $subStringString ) );

                $query = "UPDATE
                                ezpolicy_limitation_value
                          SET
                                value = $newValue
                          WHERE
                                value LIKE '$oldPath%' AND $limitationIDString";

                $db->query( $query );

                $expireRoleCache = true;
            }

            // clean up limitations on role assignment level
            $countRows = $db->arrayQuery( "SELECT COUNT(*) AS row_count FROM ezuser_role WHERE limit_identifier='Subtree' AND limit_value LIKE '$oldPath%'" );
            $assignmentsToFixCount = $countRows[0]['row_count'];

            if ( $assignmentsToFixCount > 0 )
            {
                $subStringString =  $db->subString( 'limit_value', $oldPathLength );
                $newValue = $db->concatString( array( "'$newPath'", $subStringString ) );

                $db->query( "UPDATE
                                ezuser_role
                             SET
                                limit_value = $newValue
                             WHERE
                                limit_identifier='Subtree' AND limit_value LIKE '$oldPath%'"
                          );

               $expireRoleCache = true;
           }

            if ( $expireRoleCache )
            {
                eZRole::expireCache();
            }

            // Update "is_invisible" node attribute.
            $newNode = eZContentObjectTreeNode::fetch( $nodeID );
            eZContentObjectTreeNode::updateNodeVisibility( $newNode, $newParentNode );
            $db->commit();
        }
    }
1058360
public function setPosition($position = self::POSITION_RIGHT) {
		if (!in_array($position,self::$_positionXLref)) {
			return false;
		}

		$this->_position = $position;
		return true;
	}
622812
private static function _getByPosition($position, $conditions=null)
    {
        self::_checkLoaded();

        $builder          = self::_makeBuilder();
        $builder->fields  = self::extractColumns();
        $builder->order   = $position=="first" ? self::getOrder() : self::getReversedOrder();
        $builder->where   = self::_extractWhereConditions($conditions);
        $vals             = self::extractWhereValues($conditions);

        $cls  = get_called_class();
        $stmt = self::executePrepared($builder, $vals);
        $data = $stmt->fetch(\PDO::FETCH_ASSOC);

        if (!$data) {
            return null;
        }
        return new $cls($data);
    }
68318
public function moveToParent($movedNode, $newParent, $position=null)
    {

        $this->callBeforeListeners('move', [$movedNode, $newParent, $position]);

        $this->model()->placeAt($movedNode, $newParent, $position);

        if (!$position) {
            $this->model()->makeChildOf($movedNode, $newParent);
        } else {
            $this->model()->placeAt($movedNode, $newParent, $position);
        }

        $this->callAfterListeners('move', [$movedNode, $newParent, $position]);

        return $this;
    }
267161
public function move(array $data, $position = 0, $insertType = self::INSERT_NODE)
    {
        // get key of node we are moving.
        $id = $data[$this->getPrimaryKey()];

        $this->getHydrator()->addDepth();

        // node and it's children
        $select = $this->getDecendentsByParentId($id, false);
        $nodes = $this->fetchResult($select);

        // get the width of the node.
        $width = $nodes->current()->width();

        // take out the left and right values of list from database.
        $where = new Where();
        $where->between(self::COLUMN_LEFT, $nodes->current()->getLft(), $nodes->current()->getRgt());
        $result = $this->update([
            'lft' => null,
            'rgt' => null,
        ], $where);

        // update left and right values.
        $this->updateTree($nodes->current()->getRgt(), '-', $width);

        // get the new parent and it's children
        // and make room for moved node
        if ($position != 0) {
            /* @var $parent \UthandoCommon\Model\NestedSet */
            $parent = $this->getById($position);
            $lft_rgt = ($insertType === self::INSERT_NODE) ? $parent->getRgt() : $parent->getLft();
        } else {
            $lft_rgt = 0;
        }

        // update left and right values
        $this->updateTree($lft_rgt, '+', $width);

        // insert moved category.
        $reduce = $nodes->current()->getRgt() - $width;
        $getter = 'get' . ucfirst($this->getPrimaryKey());

        /* @var $row \UthandoCommon\Model\NestedSet */
        foreach ($nodes as $row) {
            $lft = ($row->getLft() - $reduce);
            $rgt = ($row->getRgt() - $reduce);

            if ($position != 0) {
                $rgt += $lft_rgt;
                $lft += $lft_rgt;
            }

            $result = $this->update([
                'rgt' => $rgt,
                'lft' => $lft,
            ], [$this->getPrimaryKey() => $row->$getter()]);
        }

        return $result;
    }
1058300
protected function children($position = null, $order = 'asc')
    {
        $query = $this->queryByParentId();

        if (!is_null($position)) {
            if (is_array($position)) {
                $query->buildWherePosition($this->getPositionColumn(), $position);
            } else {
                if ($position === static::QUERY_LAST) {
                    $query->orderBy($this->getPositionColumn(), 'desc');
                } else {
                    $query->where($this->getPositionColumn(), '=', $position);
                }
            }
        }

        if ($position !== static::QUERY_LAST) {
            $query->orderBy($this->getPositionColumn(), $order);
        }

        return $query;
    }
1204113
protected function reorderSiblings($parentIdChanged = false)
    {
        list($range, $action) = $this->setupReordering($parentIdChanged);

        $positionColumn = $this->getPositionColumn();

        // As the method called twice (before moving and after moving),
        // first we gather "old" siblings by the old parent id value of the model.
        if ($parentIdChanged === true) {
            $query = $this->siblings(false, $this->old_parent_id);
        } else {
            $query = $this->siblings();
        }

        if ($action) {
            $query->buildWherePosition($positionColumn, $range)
                ->where($this->getKeyName(), '<>', $this->getKey())
                ->$action($positionColumn);
        }
    }
93923
public function setAxisPosition($AxisID, $Position = AXIS_POSITION_LEFT)
    {
        if (isset($this->Data["Axis"][$AxisID])) {
            $this->Data["Axis"][$AxisID]["Position"] = $Position;
        }
    }
548539
public function addChild(EntityInterface $child, $position = null, $returnChild = false)
    {
        if ($this->exists) {
            if (is_null($position)) {
                $position = $this->getNextAfterLastPosition($this->getKey());
            }

            $child->moveTo($position, $this);
        }

        return ($returnChild === true ? $child : $this);
    }
696946
public function setSiblingOf(Tree $sibling)
    {
        $this->handleNewNodes();
        if ($this->validateSetSiblingOf($sibling)) {
            if ($this->fireModelEvent('updatingParent') === false) {
                return $this;
            }
            $oldDescendants                         = $this->getOldDescendants();
            $this->{$this->getTreeColumn('path')}   = $sibling->removeLastNodeFromPath() . $this->getKey() . '/';
            $this->{$this->getTreeColumn('parent')} = $sibling->{$this->getTreeColumn('parent')};
            $this->{$this->getTreeColumn('level')}  = $sibling->{$this->getTreeColumn('level')};
            $this->save();
            $this->fireModelEvent('updatedParent', false);
            $this->updateDescendants($this, $oldDescendants);
        }
        return $this;
    }