Back to Nested class

Method moveByReference

public bool
moveByReference
(mixed $referenceId, mixed $position = 'after', mixed $pk = null, mixed $recursiveUpdate = true)
Method to move a node and its children to a new location in the tree.
Parameters
  • int $referenceId The primary key of the node to reference new location by.
  • string $position Location type string. ['before', 'after', 'first-child', 'last-child']
  • int $pk The primary key of the node to move.
  • bool $recursiveUpdate Flag indicate that method recursiveUpdatePublishedColumn should be call.
Returns
  • bool True on success.
Since
  • 1.7.0
-
  • \RuntimeException on database error.
Class: Nested
Project: Joomla

Method moveByReference - Source code

/**
 * Method to move a node and its children to a new location in the tree.
 *
 * @param   integer  $referenceId      The primary key of the node to reference new location by.
 * @param   string   $position         Location type string. ['before', 'after', 'first-child', 'last-child']
 * @param   integer  $pk               The primary key of the node to move.
 * @param   boolean  $recursiveUpdate  Flag indicate that method recursiveUpdatePublishedColumn should be call.
 *
 * @return  boolean  True on success.
 *
 * @since   1.7.0
 * @throws  \RuntimeException on database error.
 */
public function moveByReference($referenceId, $position = 'after', $pk = null, $recursiveUpdate = true)
{
    if ($this->_debug) {
        echo "\nMoving ReferenceId:{$referenceId}, Position:{$position}, PK:{$pk}";
    }
    $k = $this->_tbl_key;
    $pk = \is_null($pk) ? $this->{$k} : $pk;
    // Get the node by id.
    if (!($node = $this->_getNode($pk))) {
        // Error message set in getNode method.
        return false;
    }
    // Get the ids of child nodes.
    $query = $this->_db->getQuery(true)->select($k)->from($this->_tbl)->where('lft BETWEEN ' . (int) $node->lft . ' AND ' . (int) $node->rgt);
    $children = $this->_db->setQuery($query)->loadColumn();
    if ($this->_debug) {
        $this->_logtable(false);
    }
    // Cannot move the node to be a child of itself.
    if (\in_array($referenceId, $children)) {
        $this->setError(new \UnexpectedValueException(sprintf('%1$s::moveByReference() is trying to make record ID %2$d a child of itself.', \get_class($this), $pk)));
        return false;
    }
    // Lock the table for writing.
    if (!$this->_lock()) {
        return false;
    }
    /*
     * Move the sub-tree out of the nested sets by negating its left and right values.
     */
    $query->clear()->update($this->_tbl)->set('lft = lft * (-1), rgt = rgt * (-1)')->where('lft BETWEEN ' . (int) $node->lft . ' AND ' . (int) $node->rgt);
    $this->_db->setQuery($query);
    $this->_runQuery($query, 'JLIB_DATABASE_ERROR_MOVE_FAILED');
    /*
     * Close the hole in the tree that was opened by removing the sub-tree from the nested sets.
     */
    // Compress the left values.
    $query->clear()->update($this->_tbl)->set('lft = lft - ' . (int) $node->width)->where('lft > ' . (int) $node->rgt);
    $this->_db->setQuery($query);
    $this->_runQuery($query, 'JLIB_DATABASE_ERROR_MOVE_FAILED');
    // Compress the right values.
    $query->clear()->update($this->_tbl)->set('rgt = rgt - ' . (int) $node->width)->where('rgt > ' . (int) $node->rgt);
    $this->_db->setQuery($query);
    $this->_runQuery($query, 'JLIB_DATABASE_ERROR_MOVE_FAILED');
    // We are moving the tree relative to a reference node.
    if ($referenceId) {
        // Get the reference node by primary key.
        if (!($reference = $this->_getNode($referenceId))) {
            // Error message set in getNode method.
            $this->_unlock();
            return false;
        }
        // Get the reposition data for shifting the tree and re-inserting the node.
        if (!($repositionData = $this->_getTreeRepositionData($reference, $node->width, $position))) {
            // Error message set in getNode method.
            $this->_unlock();
            return false;
        }
    } else {
        // Get the last root node as the reference node.
        $query->clear()->select($this->_tbl_key . ', parent_id, level, lft, rgt')->from($this->_tbl)->where('parent_id = 0')->order('lft DESC');
        $query->setLimit(1);
        $this->_db->setQuery($query);
        $reference = $this->_db->loadObject();
        if ($this->_debug) {
            $this->_logtable(false);
        }
        // Get the reposition data for re-inserting the node after the found root.
        if (!($repositionData = $this->_getTreeRepositionData($reference, $node->width, 'last-child'))) {
            // Error message set in getNode method.
            $this->_unlock();
            return false;
        }
    }
    /*
     * Create space in the nested sets at the new location for the moved sub-tree.
     */
    // Shift left values.
    $query->clear()->update($this->_tbl)->set('lft = lft + ' . (int) $node->width)->where($repositionData->left_where);
    $this->_db->setQuery($query);
    $this->_runQuery($query, 'JLIB_DATABASE_ERROR_MOVE_FAILED');
    // Shift right values.
    $query->clear()->update($this->_tbl)->set('rgt = rgt + ' . (int) $node->width)->where($repositionData->right_where);
    $this->_db->setQuery($query);
    $this->_runQuery($query, 'JLIB_DATABASE_ERROR_MOVE_FAILED');
    /*
     * Calculate the offset between where the node used to be in the tree and
     * where it needs to be in the tree for left ids (also works for right ids).
     */
    $offset = $repositionData->new_lft - $node->lft;
    $levelOffset = $repositionData->new_level - $node->level;
    // Move the nodes back into position in the tree using the calculated offsets.
    $query->clear()->update($this->_tbl)->set('rgt = ' . (int) $offset . ' - rgt')->set('lft = ' . (int) $offset . ' - lft')->set('level = level + ' . (int) $levelOffset)->where('lft < 0');
    $this->_db->setQuery($query);
    $this->_runQuery($query, 'JLIB_DATABASE_ERROR_MOVE_FAILED');
    // Set the correct parent id for the moved node if required.
    if ($node->parent_id != $repositionData->new_parent_id) {
        $query = $this->_db->getQuery(true)->update($this->_tbl);
        // Update the title and alias fields if they exist for the table.
        $fields = $this->getFields();
        if ($this->hasField('title') && $this->title !== null) {
            $query->set('title = ' . $this->_db->quote($this->title));
        }
        if (\array_key_exists('alias', $fields) && $this->alias !== null) {
            $query->set('alias = ' . $this->_db->quote($this->alias));
        }
        $query->set('parent_id = ' . (int) $repositionData->new_parent_id)->where($this->_tbl_key . ' = ' . (int) $node->{$k});
        $this->_db->setQuery($query);
        $this->_runQuery($query, 'JLIB_DATABASE_ERROR_MOVE_FAILED');
    }
    // Unlock the table for writing.
    $this->_unlock();
    if ($this->hasField('published') && $recursiveUpdate) {
        $this->recursiveUpdatePublishedColumn($node->{$k});
    }
    // Set the object values.
    $this->parent_id = $repositionData->new_parent_id;
    $this->level = $repositionData->new_level;
    $this->lft = $repositionData->new_lft;
    $this->rgt = $repositionData->new_rgt;
    return true;
}