Source for file nested.php
Documentation is available at nested.php
* @package Joomla.Platform
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
* Table class supporting modified pre-order tree traversal behavior.
* @package Joomla.Platform
* @link http://docs.joomla.org/JTableNested
* Object property holding the primary key of the parent node. Provides
* adjacency list data for nodes.
* Object property holding the depth level of the node in the tree.
* Object property holding the left value of the node for managing its
* placement in the nested sets tree.
* Object property holding the right value of the node for managing its
* placement in the nested sets tree.
* Object property holding the alias of this node used to constuct the
* full text path, forward-slash delimited.
* Object property to hold the location type to use when storing the row.
* Possible values are: ['before', 'after', 'first-child', 'last-child'].
* Object property to hold the primary key of the location reference node to
* use when storing the row. A combination of location type and reference
* node describes where to store the current node in the tree.
* An array to cache values in recursive processes.
* Sets the debug level on or off
* @param integer $level 0 = off, 1 = on
public function debug($level)
* Method to get an array of nodes from a given node to its root.
* @param integer $pk Primary key of the node for which to get the path.
* @param boolean $diagnostic Only select diagnostic data for the nested sets.
* @return mixed An array of node objects including the start node.
* @throws RuntimeException on database error
public function getPath($pk =
null, $diagnostic =
false)
$pk =
(is_null($pk)) ?
$this->$k :
$pk;
// Get the path from the node to the root.
$select =
($diagnostic) ?
'p.' .
$k .
', p.parent_id, p.level, p.lft, p.rgt' :
'p.*';
$query =
$this->_db->getQuery(true)
->from($this->_tbl .
' AS n, ' .
$this->_tbl .
' AS p')
->where('n.lft BETWEEN p.lft AND p.rgt')
->where('n.' .
$k .
' = ' . (int)
$pk)
$this->_db->setQuery($query);
return $this->_db->loadObjectList();
* Method to get a node and all its child nodes.
* @param integer $pk Primary key of the node for which to get the tree.
* @param boolean $diagnostic Only select diagnostic data for the nested sets.
* @return mixed Boolean false on failure or array of node objects on success.
* @throws RuntimeException on database error.
public function getTree($pk =
null, $diagnostic =
false)
$pk =
(is_null($pk)) ?
$this->$k :
$pk;
// Get the node and children as a tree.
$select =
($diagnostic) ?
'n.' .
$k .
', n.parent_id, n.level, n.lft, n.rgt' :
'n.*';
$query =
$this->_db->getQuery(true)
->from($this->_tbl .
' AS n, ' .
$this->_tbl .
' AS p')
->where('n.lft BETWEEN p.lft AND p.rgt')
->where('p.' .
$k .
' = ' . (int)
$pk)
return $this->_db->setQuery($query)->loadObjectList();
* Method to determine if a node is a leaf node in the tree (has no children).
* @param integer $pk Primary key of the node to check.
* @return boolean True if a leaf node, false if not or null if the node does not exist.
* @note Since 12.1 this method returns null if the node does not exist.
* @throws RuntimeException on database error.
public function isLeaf($pk =
null)
$pk =
(is_null($pk)) ?
$this->$k :
$pk;
// Get the node by primary key.
// Error message set in getNode method.
// The node is a leaf node.
return (($node->rgt -
$node->lft) ==
1);
* Method to set the location of a node in the tree object. This method does not
* save the new location to the database, but will set it in the object so
* that when the node is stored it will be stored in the new location.
* @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']
* @note Since 12.1 this method returns void and throws an InvalidArgumentException when an invalid position is passed.
* @throws InvalidArgumentException
public function setLocation($referenceId, $position =
'after')
// Make sure the location is valid.
if (($position !=
'before') &&
($position !=
'after') &&
($position !=
'first-child') &&
($position !=
'last-child'))
throw
new InvalidArgumentException(sprintf('%s::setLocation(%d, *%s*)', get_class($this), $referenceId, $position));
// Set the location properties.
* Method to move a row in the ordering sequence of a group of rows defined by an SQL WHERE clause.
* Negative numbers move the row up in the sequence and positive numbers move it down.
* @param integer $delta The direction and magnitude to move the row in the ordering sequence.
* @param string $where WHERE clause to use for limiting the selection of rows to compact the
* @return mixed Boolean true on success.
* @link http://docs.joomla.org/JTable/move
public function move($delta, $where =
'')
$query =
$this->_db->getQuery(true)
$query->where('rgt > ' .
$this->rgt)
$query->where('lft < ' .
$this->lft)
$this->_db->setQuery($query);
$referenceId =
$this->_db->loadResult();
* 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.
* @return boolean True on success.
* @link http://docs.joomla.org/JTableNested/moveByReference
* @throws RuntimeException on database error.
public function moveByReference($referenceId, $position =
'after', $pk =
null)
// @codeCoverageIgnoreStart
echo
"\nMoving ReferenceId:$referenceId, Position:$position, PK:$pk";
// @codeCoverageIgnoreEnd
$pk =
(is_null($pk)) ?
$this->$k :
$pk;
// Error message set in getNode method.
// Get the ids of child nodes.
$query =
$this->_db->getQuery(true)
->where('lft BETWEEN ' . (int)
$node->lft .
' AND ' . (int)
$node->rgt);
$children =
$this->_db->setQuery($query)->loadColumn();
// @codeCoverageIgnoreStart
// @codeCoverageIgnoreEnd
// Cannot move the node to be a child of itself.
$e =
new UnexpectedValueException(
sprintf('%s::moveByReference(%d, %s, %d) parenting to child.', get_class($this), $referenceId, $position, $pk)
// Lock the table for writing.
* Move the sub-tree out of the nested sets by negating its left and right values.
->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.
->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.
->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.
// Get the reference node by primary key.
if (!$reference =
$this->_getNode($referenceId))
// Error message set in getNode method.
// Get the reposition data for shifting the tree and re-inserting the node.
// Error message set in getNode method.
// We are moving the tree to be the last child of the root node
// Get the last root node as the reference node.
->select($this->_tbl_key .
', parent_id, level, lft, rgt')
$this->_db->setQuery($query, 0, 1);
$reference =
$this->_db->loadObject();
// @codeCoverageIgnoreStart
// @codeCoverageIgnoreEnd
// Get the reposition data for re-inserting the node after the found root.
// Error message set in getNode method.
* Create space in the nested sets at the new location for the moved sub-tree.
->set('lft = lft + ' . (int)
$node->width)
->where($repositionData->left_where);
$this->_db->setQuery($query);
$this->_runQuery($query, 'JLIB_DATABASE_ERROR_MOVE_FAILED');
->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.
->set('rgt = ' . (int)
$offset .
' - rgt')
->set('lft = ' . (int)
$offset .
' - lft')
->set('level = level + ' . (int)
$levelOffset)
$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 the title and alias fields if they exist for the table.
$query->set('title = ' .
$this->_db->quote($this->title));
$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.
// 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;
* Method to delete a node and, optionally, its child nodes from the table.
* @param integer $pk The primary key of the node to delete.
* @param boolean $children True to delete child nodes, false to move them up a level.
* @return boolean True on success.
public function delete($pk =
null, $children =
true)
$pk =
(is_null($pk)) ?
$this->$k :
$pk;
// Implement JObservableInterface: Pre-processing by observers
$this->_observers->update('onBeforeDelete', array($pk));
// Lock the table for writing.
// Error message set in lock method.
// If tracking assets, remove the asset first.
// Lock the table for writing.
// Error message set in lock method.
if ($asset->loadByName($name))
// Delete the node in assets table.
if (!$asset->delete(null, $children))
// Error message set in getNode method.
$query =
$this->_db->getQuery(true);
// Should we delete all children along with the node?
// Delete the node and all of its children.
->where('lft BETWEEN ' . (int)
$node->lft .
' AND ' . (int)
$node->rgt);
$this->_runQuery($query, 'JLIB_DATABASE_ERROR_DELETE_FAILED');
// Compress the left values.
->set('lft = lft - ' . (int)
$node->width)
->where('lft > ' . (int)
$node->rgt);
$this->_runQuery($query, 'JLIB_DATABASE_ERROR_DELETE_FAILED');
// Compress the right values.
->set('rgt = rgt - ' . (int)
$node->width)
->where('rgt > ' . (int)
$node->rgt);
$this->_runQuery($query, 'JLIB_DATABASE_ERROR_DELETE_FAILED');
// Leave the children and move them up a level.
->where('lft = ' . (int)
$node->lft);
$this->_runQuery($query, 'JLIB_DATABASE_ERROR_DELETE_FAILED');
// Shift all node's children up a level.
->set('level = level - 1')
->where('lft BETWEEN ' . (int)
$node->lft .
' AND ' . (int)
$node->rgt);
$this->_runQuery($query, 'JLIB_DATABASE_ERROR_DELETE_FAILED');
// Adjust all the parent values for direct children of the deleted node.
->set('parent_id = ' . (int)
$node->parent_id)
->where('parent_id = ' . (int)
$node->$k);
$this->_runQuery($query, 'JLIB_DATABASE_ERROR_DELETE_FAILED');
// Shift all of the left values that are right of the node.
->where('lft > ' . (int)
$node->rgt);
$this->_runQuery($query, 'JLIB_DATABASE_ERROR_DELETE_FAILED');
// Shift all of the right values that are right of the node.
->where('rgt > ' . (int)
$node->rgt);
$this->_runQuery($query, 'JLIB_DATABASE_ERROR_DELETE_FAILED');
// Unlock the table for writing.
// Implement JObservableInterface: Post-processing by observers
$this->_observers->update('onAfterDelete', array($pk));
* Checks that the object is valid and able to be stored.
* This method checks that the parent_id is non-zero and exists in the database.
* Note that the root node (parent_id = 0) cannot be manipulated with this class.
* @return boolean True if all checks pass.
* @throws RuntimeException on database error.
* @throws UnexpectedValueException
// Set up a mini exception handler.
// Check that the parent_id field is valid.
$query =
$this->_db->getQuery(true)
->select('COUNT(' .
$this->_tbl_key .
')')
if (!$this->_db->setQuery($query)->loadResult())
catch
(UnexpectedValueException $e)
// Validation error - record it and return false.
// @codeCoverageIgnoreStart
// Database error - rethrow.
// @codeCoverageIgnoreEnd
* Method to store a node in the database table.
* @param boolean $updateNulls True to update null values as well.
* @return boolean True on success.
* @link http://docs.joomla.org/JTableNested/store
public function store($updateNulls =
false)
// Implement JObservableInterface: Pre-processing by observers
$this->_observers->update('onBeforeStore', array($updateNulls, $k));
// @codeCoverageIgnoreStart
// @codeCoverageIgnoreEnd
* If the primary key is empty, then we assume we are inserting a new node into the
* tree. From this point we would need to determine where in the tree to insert it.
* We are inserting a node somewhere in the tree with a known reference
* node. We have to make room for the new node and set the left and right
* values before we insert the row.
// Lock the table for writing.
// Error message set in lock method.
// We are inserting a node relative to the last root node.
// Get the last root node as the reference node.
$query =
$this->_db->getQuery(true)
->select($this->_tbl_key .
', parent_id, level, lft, rgt')
$this->_db->setQuery($query, 0, 1);
$reference =
$this->_db->loadObject();
// @codeCoverageIgnoreStart
// @codeCoverageIgnoreEnd
// We have a real node set as a location reference.
// Get the reference node by primary key.
// Error message set in getNode method.
// Get the reposition data for shifting the tree and re-inserting the node.
// Error message set in getNode method.
// Create space in the tree at the new location for the new node in left ids.
$query =
$this->_db->getQuery(true)
->where($repositionData->left_where);
$this->_runQuery($query, 'JLIB_DATABASE_ERROR_STORE_FAILED');
// Create space in the tree at the new location for the new node in right ids.
->where($repositionData->right_where);
$this->_runQuery($query, 'JLIB_DATABASE_ERROR_STORE_FAILED');
// 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;
// Negative parent ids are invalid
$e =
new UnexpectedValueException(sprintf('%s::store() used a negative _location_id', get_class($this)));
* If we have a given primary key then we assume we are simply updating this
* node in the tree. We should assess whether or not we are moving the node
* or just updating its data fields.
// If the location has been set, move the node to its new location.
// Error message set in move method.
// Lock the table for writing.
// Error message set in lock method.
// Implement JObservableInterface: We do not want parent::store to update observers,
// since tables are locked and we are updating it from this level of store():
$oldCallObservers =
$this->_observers->doCallObservers(false);
$result =
parent::store($updateNulls);
// Implement JObservableInterface: Restore previous callable observers state:
$this->_observers->doCallObservers($oldCallObservers);
// @codeCoverageIgnoreStart
// @codeCoverageIgnoreEnd
// Unlock the table for writing.
// Implement JObservableInterface: Post-processing by observers
$this->_observers->update('onAfterStore', array(&$result));
* Method to set the publishing state for a node or list of nodes in the database
* table. The method respects rows checked out by other users and will attempt
* to checkin rows that it can after adjustments are made. The method will not
* allow you to set a publishing state higher than any ancestor node and will
* not allow you to set a publishing state on a node with a checked out child.
* @param mixed $pks An optional array of primary key values to update. If not
* set the instance property value is used.
* @param integer $state The publishing state. eg. [0 = unpublished, 1 = published]
* @param integer $userId The user id of the user performing the operation.
* @return boolean True on success.
* @link http://docs.joomla.org/JTableNested/publish
* @throws UnexpectedValueException
public function publish($pks =
null, $state =
1, $userId =
0)
$query =
$this->_db->getQuery(true);
// If $state > 1, then we allow state changes even if an ancestor has lower state
// (for example, can change a child state to Archived (2) if an ancestor is Published (1)
$compareState =
($state >
1) ?
1 :
$state;
// If there are no primary keys set check to see if the instance key is set.
// Nothing to set publishing state on, return false.
$e =
new UnexpectedValueException(sprintf('%s::publish(%s, %d, %d) empty.', get_class($this), $pks, $state, $userId));
// Determine if there is checkout support for the table.
// Iterate over the primary keys to execute the publish action if possible.
// Get the node by primary key.
// Error message set in getNode method.
// If the table has checkout support, verify no children are checked out.
// Ensure that children are not checked out.
->select('COUNT(' .
$k .
')')
->where('lft BETWEEN ' . (int)
$node->lft .
' AND ' . (int)
$node->rgt)
->where('(checked_out <> 0 AND checked_out <> ' . (int)
$userId .
')');
$this->_db->setQuery($query);
// Check for checked out children.
if ($this->_db->loadResult())
// TODO Convert to a conflict exception when available.
$e =
new RuntimeException(sprintf('%s::publish(%s, %d, %d) checked-out conflict.', get_class($this), $pks, $state, $userId));
// If any parent nodes have lower published state values, we cannot continue.
// Get any ancestor nodes that have a lower publishing state.
->from($this->_db->quoteName($this->_tbl) .
' AS n')
->where('n.lft < ' . (int)
$node->lft)
->where('n.rgt > ' . (int)
$node->rgt)
->where('n.parent_id > 0')
->where('n.published < ' . (int)
$compareState);
// Just fetch one row (one is one too many).
$this->_db->setQuery($query, 0, 1);
$rows =
$this->_db->loadColumn();
$e =
new UnexpectedValueException(
sprintf('%s::publish(%s, %d, %d) ancestors have lower state.', get_class($this), $pks, $state, $userId)
// Update and cascade the publishing state.
->update($this->_db->quoteName($this->_tbl))
->set('published = ' . (int)
$state)
->where('(lft > ' . (int)
$node->lft .
' AND rgt < ' . (int)
$node->rgt .
') OR ' .
$k .
' = ' . (int)
$pk);
$this->_db->setQuery($query)->execute();
// If checkout support exists for the object, check the row in.
// If the JTable instance value is in the list of primary keys that were set, set the instance.
$this->published =
$state;
* Method to move a node one position to the left in the same level.
* @param integer $pk Primary key of the node to move.
* @return boolean True on success.
* @throws RuntimeException on database error.
$pk =
(is_null($pk)) ?
$this->$k :
$pk;
// Lock the table for writing.
// Error message set in lock method.
// Get the node by primary key.
// Error message set in getNode method.
// Get the left sibling node.
$sibling =
$this->_getNode($node->lft -
1, 'right');
// Error message set in getNode method.
// Get the primary keys of child nodes.
$query =
$this->_db->getQuery(true)
->where('lft BETWEEN ' . (int)
$node->lft .
' AND ' . (int)
$node->rgt);
$children =
$this->_db->setQuery($query)->loadColumn();
// Shift left and right values for the node and its children.
->set('lft = lft - ' . (int)
$sibling->width)
->set('rgt = rgt - ' . (int)
$sibling->width)
->where('lft BETWEEN ' . (int)
$node->lft .
' AND ' . (int)
$node->rgt);
$this->_db->setQuery($query)->execute();
// Shift left and right values for the sibling and its children.
->set('lft = lft + ' . (int)
$node->width)
->set('rgt = rgt + ' . (int)
$node->width)
->where('lft BETWEEN ' . (int)
$sibling->lft .
' AND ' . (int)
$sibling->rgt)
$this->_db->setQuery($query)->execute();
catch
(RuntimeException $e)
// Unlock the table for writing.
* Method to move a node one position to the right in the same level.
* @param integer $pk Primary key of the node to move.
* @return boolean True on success.
* @throws RuntimeException on database error.
$pk =
(is_null($pk)) ?
$this->$k :
$pk;
// Lock the table for writing.
// Error message set in lock method.
// Get the node by primary key.
// Error message set in getNode method.
$query =
$this->_db->getQuery(true);
// Get the right sibling node.
$sibling =
$this->_getNode($node->rgt +
1, 'left');
// Error message set in getNode method.
$query->_unlock($this->_db);
// Get the primary keys of child nodes.
->where('lft BETWEEN ' . (int)
$node->lft .
' AND ' . (int)
$node->rgt);
$this->_db->setQuery($query);
$children =
$this->_db->loadColumn();
// Shift left and right values for the node and its children.
->set('lft = lft + ' . (int)
$sibling->width)
->set('rgt = rgt + ' . (int)
$sibling->width)
->where('lft BETWEEN ' . (int)
$node->lft .
' AND ' . (int)
$node->rgt);
$this->_db->setQuery($query)->execute();
// Shift left and right values for the sibling and its children.
->set('lft = lft - ' . (int)
$node->width)
->set('rgt = rgt - ' . (int)
$node->width)
->where('lft BETWEEN ' . (int)
$sibling->lft .
' AND ' . (int)
$sibling->rgt)
$this->_db->setQuery($query)->execute();
catch
(RuntimeException $e)
// Unlock the table for writing.
* Gets the ID of the root item in the tree
* @return mixed The primary id of the root row, or false if not found and the internal error is set.
// Test for a unique record with parent_id = 0
$query =
$this->_db->getQuery(true)
->where('parent_id = 0');
$result =
$this->_db->setQuery($query)->loadColumn();
// Test for a unique record with lft = 0
$result =
$this->_db->setQuery($query)->loadColumn();
// Test for a unique record alias = root
->where('alias = ' .
$this->_db->quote('root'));
$result =
$this->_db->setQuery($query)->loadColumn();
* Method to recursively rebuild the whole nested set tree.
* @param integer $parentId The root of the tree to rebuild.
* @param integer $leftId The left id to start with in building the tree.
* @param integer $level The level to assign to the current nodes.
* @param string $path The path to the current nodes.
* @return integer 1 + value of root rgt on success, false on failure
* @link http://docs.joomla.org/JTableNested/rebuild
* @throws RuntimeException on database error.
public function rebuild($parentId =
null, $leftId =
0, $level =
0, $path =
'')
// If no parent is provided, try to find it.
$query =
$this->_db->getQuery(true);
// Build the structure of the recursive query.
if (!isset
($this->_cache['rebuild.sql']))
->where('parent_id = %d');
// If the table has an ordering field, use that for ordering.
$query->order('parent_id, ordering, lft');
$query->order('parent_id, lft');
$this->_cache['rebuild.sql'] = (string)
$query;
// Make a shortcut to database object.
// Assemble the query to find all children of this node.
$children =
$this->_db->loadObjectList();
// The right value of this node is the left value + 1
// Execute this function recursively over all children
foreach ($children as $node)
* $rightId is the current right value, which is incremented on recursion return.
* Increment the level for the children.
* Add this item's alias to the path (but avoid a leading /)
$rightId =
$this->rebuild($node->{$this->_tbl_key}, $rightId, $level +
1, $path .
(empty($path) ?
'' :
'/') .
$node->alias);
// If there is an update failure, return false to break out of the recursion.
// We've got the left value, and now that we've processed
// the children of this node we also know the right value.
->set('lft = ' . (int)
$leftId)
->set('rgt = ' . (int)
$rightId)
->set('level = ' . (int)
$level)
->set('path = ' .
$this->_db->quote($path))
->where($this->_tbl_key .
' = ' . (int)
$parentId);
$this->_db->setQuery($query)->execute();
// Return the right value of this node + 1.
* Method to rebuild the node's path field from the alias values of the
* nodes from the current node to the root node of the tree.
* @param integer $pk Primary key of the node for which to get the path.
* @return boolean True on success.
* @link http://docs.joomla.org/JTableNested/rebuildPath
// If there is no alias or path field, just return true.
$pk =
(is_null($pk)) ?
$this->$k :
$pk;
// Get the aliases for the path from the node to the root node.
$query =
$this->_db->getQuery(true)
->from($this->_tbl .
' AS n, ' .
$this->_tbl .
' AS p')
->where('n.lft BETWEEN p.lft AND p.rgt')
->where('n.' .
$this->_tbl_key .
' = ' . (int)
$pk)
$this->_db->setQuery($query);
$segments =
$this->_db->loadColumn();
// Make sure to remove the root path if it exists in the list.
if ($segments[0] ==
'root')
// Update the path field for the node.
->set('path = ' .
$this->_db->quote($path))
->where($this->_tbl_key .
' = ' . (int)
$pk);
$this->_db->setQuery($query)->execute();
// Update the current record's path to the new one:
* Method to update order of table rows
* @param array $idArray id numbers of rows to be reordered.
* @param array $lft_array lft values of rows to be reordered.
* @return integer 1 + value of root rgt on success, false on failure.
* @throws Exception on database error.
public function saveorder($idArray =
null, $lft_array =
null)
$query =
$this->_db->getQuery(true);
for ($i =
0, $count =
count($idArray); $i <
$count; $i++
)
// Do an update to change the lft values in the table for each id
->where($this->_tbl_key .
' = ' . (int)
$idArray[$i])
->set('lft = ' . (int)
$lft_array[$i]);
$this->_db->setQuery($query)->execute();
// @codeCoverageIgnoreStart
// @codeCoverageIgnoreEnd
* Method to get nested set properties for a node in the tree.
* @param integer $id Value to look up the node by.
* @param string $key An optional key to look up the node by (parent | left | right).
* If omitted, the primary key of the table is used.
* @return mixed Boolean false on failure or node object on success.
* @throws RuntimeException on database error.
protected function _getNode($id, $key =
null)
// Determine which key to get the node base on.
$query =
$this->_db->getQuery(true)
->select($this->_tbl_key .
', parent_id, level, lft, rgt')
->where($k .
' = ' . (int)
$id);
$row =
$this->_db->setQuery($query, 0, 1)->loadObject();
// Check for no $row returned
$e =
new UnexpectedValueException(sprintf('%s::_getNode(%d, %s) failed.', get_class($this), $id, $key));
// Do some simple calculations.
$row->numChildren = (int)
($row->rgt -
$row->lft -
1) /
2;
$row->width = (int)
$row->rgt -
$row->lft +
1;
* Method to get various data necessary to make room in the tree at a location
* for a node and its children. The returned data object includes conditions
* for SQL WHERE clauses for updating left and right id values to make room for
* the node as well as the new left and right ids for the node.
* @param object $referenceNode A node object with at least a 'lft' and 'rgt' with
* which to make room in the tree around for a new node.
* @param integer $nodeWidth The width of the node for which to make room in the tree.
* @param string $position The position relative to the reference node where the room
* @return mixed Boolean false on failure or data object on success.
// Make sure the reference an object with a left and right id.
if (!is_object($referenceNode) ||
!(isset
($referenceNode->lft) && isset
($referenceNode->rgt)))
// A valid node cannot have a width less than 2.
// Run the calculations and build the data object by reference position.
$data->left_where =
'lft > ' .
$referenceNode->lft;
$data->right_where =
'rgt >= ' .
$referenceNode->lft;
$data->new_lft =
$referenceNode->lft +
1;
$data->new_rgt =
$referenceNode->lft +
$nodeWidth;
$data->new_parent_id =
$referenceNode->$k;
$data->new_level =
$referenceNode->level +
1;
$data->left_where =
'lft > ' .
($referenceNode->rgt);
$data->right_where =
'rgt >= ' .
($referenceNode->rgt);
$data->new_lft =
$referenceNode->rgt;
$data->new_rgt =
$referenceNode->rgt +
$nodeWidth -
1;
$data->new_parent_id =
$referenceNode->$k;
$data->new_level =
$referenceNode->level +
1;
$data->left_where =
'lft >= ' .
$referenceNode->lft;
$data->right_where =
'rgt >= ' .
$referenceNode->lft;
$data->new_lft =
$referenceNode->lft;
$data->new_rgt =
$referenceNode->lft +
$nodeWidth -
1;
$data->new_parent_id =
$referenceNode->parent_id;
$data->new_level =
$referenceNode->level;
$data->left_where =
'lft > ' .
$referenceNode->rgt;
$data->right_where =
'rgt > ' .
$referenceNode->rgt;
$data->new_lft =
$referenceNode->rgt +
1;
$data->new_rgt =
$referenceNode->rgt +
$nodeWidth;
$data->new_parent_id =
$referenceNode->parent_id;
$data->new_level =
$referenceNode->level;
// @codeCoverageIgnoreStart
echo
"\nRepositioning Data for $position" .
"\n-----------------------------------" .
"\nLeft Where: $data->left_where"
.
"\nRight Where: $data->right_where" .
"\nNew Lft: $data->new_lft" .
"\nNew Rgt: $data->new_rgt"
.
"\nNew Parent ID: $data->new_parent_id" .
"\nNew Level: $data->new_level" .
"\n";
// @codeCoverageIgnoreEnd
* Method to create a log table in the buffer optionally showing the query and/or data.
* @param boolean $showData True to show data
* @param boolean $showQuery True to show query
protected function _logtable($showData =
true, $showQuery =
true)
$sep =
"\n" .
str_pad('', 40, '-');
$buffer .=
"\n" .
$this->_db->getQuery() .
$sep;
$query =
$this->_db->getQuery(true)
->select($this->_tbl_key .
', parent_id, lft, rgt, level')
$this->_db->setQuery($query);
$rows =
$this->_db->loadRowList();
$buffer .=
sprintf("\n| %4s | %4s | %4s | %4s |", $this->_tbl_key, 'par', 'lft', 'rgt');
$buffer .=
sprintf("\n| %4s | %4s | %4s | %4s |", $row[0], $row[1], $row[2], $row[3]);
* Runs a query and unlocks the database on an error.
* @param mixed $query A string or JDatabaseQuery object.
* @param string $errorMessage Unused.
* @note Since 12.1 this method returns void and will rethrow the database exception.
* @throws Exception on database error.
protected function _runQuery($query, $errorMessage)
// Prepare to catch an exception.
$this->_db->setQuery($query)->execute();
// @codeCoverageIgnoreStart
// @codeCoverageIgnoreEnd
// Unlock the tables and rethrow.
Documentation generated on Tue, 19 Nov 2013 15:09:22 +0100 by phpDocumentor 1.4.3