tree_config($object, $options); } } // ----------------------------------------------------------------- /** * runtime configuration of this nestedsets tree * * @param object the DataMapper object * @param mixed optional, array of options or NULL * @return object the updated DataMapper object * @access public */ function tree_config($object, $options = array() ) { // make sure the load-time options parameter is an array if ( ! is_array($options) ) { $options = array(); } // make sure the model options parameter is an array if ( ! isset($object->nestedsets) OR ! is_array($object->nestedsets) ) { $object->nestedsets = array(); } // loop through all options foreach( array( $object->nestedsets, $options ) as $optarray ) { foreach( $optarray as $key => $value ) { switch ( $key ) { case 'name': $this->_nodename = (string) $value; break; case 'symlink': $this->_symlinkindex = (string) $value; break; case 'left': $this->_leftindex = (string) $value; break; case 'right': $this->_rightindex = (string) $value; break; case 'root': $this->_rootfield = (string) $value; break; case 'value': $this->_rootindex = (int) $value; break; case 'follow': $this->use_symlink_pointers = (bool) $value; break; default: break; } } } } // ----------------------------------------------------------------- /** * select a specific root if the table contains multiple trees * * @param object the DataMapper object * @return object the updated DataMapper object * @access public */ function select_root($object, $tree = NULL) { // set the filter value $this->_rootindex = $tree; // return the object return $object; } // ----------------------------------------------------------------- // Tree constructors // ----------------------------------------------------------------- /** * create a new tree root * * @param object the DataMapper object * @return object the updated DataMapper object * @access public */ function new_root($object) { // set the pointers for the root object $object->id = NULL; $object->{$this->_leftindex} = 1; $object->{$this->_rightindex} = 2; // add a root index if needed if ( in_array($this->_rootfield, $object->fields) && ! is_null($this->_rootindex) ) { $object->{$this->_rootfield} = $this->_rootindex; } // create the new tree root, and return the updated object return $this->_insertNew($object); } // ----------------------------------------------------------------- /** * creates a new first child of 'node' * * @param object the DataMapper object * @param object the parent node * @return object the updated DataMapper object * @access public */ function new_first_child($object, $node = NULL) { // a node passed? if ( is_null($node) ) { // no, use the object itself $node = $object->get_clone(); } // we need a valid node for this to work if ( ! $node->exists() ) { return $node; } // set the pointers for the root object $object->id = NULL; $object->{$this->_leftindex} = $node->{$this->_leftindex} + 1; $object->{$this->_rightindex} = $node->{$this->_leftindex} + 2; // add a root index if needed if ( in_array($this->_rootfield, $object->fields) && ! is_null($this->_rootindex) ) { $object->{$this->_rootfield} = $this->_rootindex; } // shift nodes to make room for the new child $this->_shiftRLValues($node, $object->{$this->_leftindex}, 2); // create the new tree node, and return the updated object return $this->_insertNew($object); } // ----------------------------------------------------------------- /** * creates a new last child of 'node' * * @param object the DataMapper object * @param object the parent node * @return object the updated DataMapper object * @access public */ function new_last_child($object, $node = NULL) { // a node passed? if ( is_null($node) ) { // no, use the object itself $node = $object->get_clone(); } // we need a valid node for this to work if ( ! $node->exists() ) { return $node; } // set the pointers for the root object $object->id = NULL; $object->{$this->_leftindex} = $node->{$this->_rightindex}; $object->{$this->_rightindex} = $node->{$this->_rightindex} + 1; // add a root index if needed if ( in_array($this->_rootfield, $object->fields) && ! is_null($this->_rootindex) ) { $object->{$this->_rootfield} = $this->_rootindex; } // shift nodes to make room for the new child $this->_shiftRLValues($node, $object->{$this->_leftindex}, 2); // create the new tree node, and return the updated object return $this->_insertNew($object); } // ----------------------------------------------------------------- /** * creates a new sibling before 'node' * * @param object the DataMapper object * @param object the sibling node * @return object the updated DataMapper object * @access public */ function new_previous_sibling($object, $node = NULL) { // a node passed? if ( is_null($node) ) { // no, use the object itself $node = $object->get_clone(); } // we need a valid node for this to work if ( ! $node->exists() ) { return $node; } // set the pointers for the root object $object->id = NULL; $object->{$this->_leftindex} = $node->{$this->_leftindex}; $object->{$this->_rightindex} = $node->{$this->_leftindex} + 1; // add a root index if needed if ( in_array($this->_rootfield, $object->fields) && ! is_null($this->_rootindex) ) { $object->{$this->_rootfield} = $this->_rootindex; } // shift nodes to make room for the new sibling $this->_shiftRLValues($node, $object->{$this->_leftindex}, 2); // create the new tree node, and return the updated object return $this->_insertNew($object); } // ----------------------------------------------------------------- /** * creates a new sibling after 'node' * * @param object the DataMapper object * @param object the sibling node * @return object the updated DataMapper object * @access public */ function new_next_sibling($object, $node = NULL) { // a node passed? if ( is_null($node) ) { // no, use the object itself $node = $object->get_clone(); } // we need a valid node for this to work if ( ! $node->exists() ) { return $node; } // set the pointers for the root object $object->id = NULL; $object->{$this->_leftindex} = $node->{$this->_rightindex} + 1; $object->{$this->_rightindex} = $node->{$this->_rightindex} + 2; // add a root index if needed if ( in_array($this->_rootfield, $object->fields) && ! is_null($this->_rootindex) ) { $object->{$this->_rootfield} = $this->_rootindex; } // shift nodes to make room for the new sibling $this->_shiftRLValues($node, $object->{$this->_leftindex}, 2); // create the new tree node, and return the updated object return $this->_insertNew($object); } // ----------------------------------------------------------------- // Tree queries // ----------------------------------------------------------------- /** * returns the root of the (selected) tree * * @param object the DataMapper object * @return object the updated DataMapper object * @access public */ function get_root($object) { // add a root index if needed if ( in_array($this->_rootfield, $object->fields) && ! is_null($this->_rootindex) ) { $object->db->where($this->_rootfield, $this->_rootindex); } // get the tree's root node return $object->where($this->_leftindex, 1)->get(); } // ----------------------------------------------------------------- /** * returns the parent of the child 'node' * * @param object the DataMapper object * @param object the child node * @return object the updated DataMapper object * @access public */ function get_parent($object, $node = NULL) { // a node passed? if ( is_null($node) ) { // no, use the object itself $node =& $object; } // we need a valid node for this to work if ( ! $node->exists() ) { return $node; } // add a root index if needed if ( in_array($this->_rootfield, $object->fields) && ! is_null($this->_rootindex) ) { $object->db->where($this->_rootfield, $this->_rootindex); } // get the node's parent node $object->where($this->_leftindex . ' <', $node->{$this->_leftindex}); $object->where($this->_rightindex . ' >', $node->{$this->_rightindex}); return $object->order_by($this->_rightindex, 'asc')->limit(1)->get(); } // ----------------------------------------------------------------- /** * returns the node with the requested left index pointer * * @param object the DataMapper object * @param integer a node's left index value * @return object the updated DataMapper object * @access public */ function get_node_where_left($object, $left_id) { // add a root index if needed if ( in_array($this->_rootfield, $object->fields) && ! is_null($this->_rootindex) ) { $object->db->where($this->_rootfield, $this->_rootindex); } // get the node's parent node $object->where($this->_leftindex, $left_id); return $object->get(); } // ----------------------------------------------------------------- /** * returns the node with the requested right index pointer * * @param object the DataMapper object * @param integer a node's right index value * @return object the updated DataMapper object * @access public */ function get_node_where_right($object, $right_id) { // add a root index if needed if ( in_array($this->_rootfield, $object->fields) && ! is_null($this->_rootindex) ) { $object->db->where($this->_rootfield, $this->_rootindex); } // get the node's parent node $object->where($this->_rightindex, $right_id); return $object->get(); } // ----------------------------------------------------------------- /** * returns the first child of the given node * * @param object the DataMapper object * @param object the parent node * @return object the updated DataMapper object * @access public */ function get_first_child($object, $node = NULL) { // a node passed? if ( is_null($node) ) { // no, use the object itself $node =& $object; } // we need a valid node for this to work if ( ! $node->exists() ) { return $node; } // add a root index if needed if ( in_array($this->_rootfield, $object->fields) && ! is_null($this->_rootindex) ) { $object->db->where($this->_rootfield, $this->_rootindex); } // get the node's first child node $object->where($this->_leftindex, $node->{$this->_leftindex}+1); return $object->get(); } // ----------------------------------------------------------------- /** * returns the last child of the given node * * @param object the DataMapper object * @param object the parent node * @return object the updated DataMapper object * @access public */ function get_last_child($object, $node = NULL) { // a node passed? if ( is_null($node) ) { // no, use the object itself $node =& $object; } // we need a valid node for this to work if ( ! $node->exists() ) { return $node; } // add a root index if needed if ( in_array($this->_rootfield, $object->fields) && ! is_null($this->_rootindex) ) { $object->db->where($this->_rootfield, $this->_rootindex); } // get the node's last child node $object->where($this->_rightindex, $node->{$this->_rightindex}-1); return $object->get(); } // ----------------------------------------------------------------- /** * returns the previous sibling of the given node * * @param object the DataMapper object * @param object the sibling node * @return object the updated DataMapper object * @access public */ function get_previous_sibling($object, $node = NULL) { // a node passed? if ( is_null($node) ) { // no, use the object itself $node =& $object; } // we need a valid node for this to work if ( ! $node->exists() ) { return $node; } // add a root index if needed if ( in_array($this->_rootfield, $object->fields) && ! is_null($this->_rootindex) ) { $object->db->where($this->_rootfield, $this->_rootindex); } // get the node's previous sibling node $object->where($this->_rightindex, $node->{$this->_leftindex}-1); return $object->get(); } // ----------------------------------------------------------------- /** * returns the next sibling of the given node * * @param object the DataMapper object * @param object the sibling node * @return object the updated DataMapper object * @access public */ function get_next_sibling($object, $node = NULL) { // a node passed? if ( is_null($node) ) { // no, use the object itself $node =& $object; } // we need a valid node for this to work if ( ! $node->exists() ) { return $node; } // add a root index if needed if ( in_array($this->_rootfield, $object->fields) && ! is_null($this->_rootindex) ) { $object->db->where($this->_rootfield, $this->_rootindex); } // get the node's next sibling node $object->where($this->_leftindex, $node->{$this->_rightindex}+1); return $object->get(); } // ----------------------------------------------------------------- // Boolean tree functions // ----------------------------------------------------------------- /** * check if the object is a valid tree node * * @param object the DataMapper object * @return boolean * @access public */ function is_valid_node($object) { if ( ! $object->exists() ) { return FALSE; } elseif ( ! isset($object->{$this->_leftindex}) OR ! is_numeric($object->{$this->_leftindex}) OR $object->{$this->_leftindex} <=0 ) { return FALSE; } elseif ( ! isset($object->{$this->_rightindex}) OR ! is_numeric($object->{$this->_rightindex}) OR $object->{$this->_rightindex} <=0 ) { return FALSE; } elseif ( $object->{$this->_leftindex} >= $object->{$this->_rightindex} ) { return FALSE; } elseif ( ! empty($this->_rootfield) && ! in_array($this->_rootfield, $object->fields) ) { return FALSE; } elseif ( ! empty($this->_rootfield) && ( ! is_numeric($object->{$this->_rootfield}) OR $object->{$this->_rootfield} <=0 ) ) { return FALSE; } // all looks well... return TRUE; } // ----------------------------------------------------------------- /** * check if the object is a tree root * * @param object the DataMapper object * @return boolean * @access public */ function is_root($object) { return ( $object->exists() && $this->is_valid_node($object) && $object->{$this->_leftindex} == 1 ); } // ----------------------------------------------------------------- /** * check if the object is a tree leaf (node with no children) * * @param object the DataMapper object * @return boolean * @access public */ function is_leaf($object) { return ( $object->exists() && $this->is_valid_node($object) && $object->{$this->_rightindex} - $object->{$this->_leftindex} == 1 ); } // ----------------------------------------------------------------- /** * check if the object is a child node * * @param object the DataMapper object * @return boolean * @access public */ function is_child($object) { return ( $object->exists() && $this->is_valid_node($object) && $object->{$this->_leftindex} > 1 ); } // ----------------------------------------------------------------- /** * check if the object is a child of node * * @param object the DataMapper object * @param object the parent node * @return boolean * @access public */ function is_child_of($object, $node = NULL) { // validate the objects if ( ! $this->is_valid_node($object) OR ! $this->is_valid_node($node) ) { return FALSE; } return ( $object->{$this->_leftindex} > $node->{$this->_leftindex} && $object->{$this->_rightindex} < $node->{$this->_rightindex} ); } // ----------------------------------------------------------------- /** * check if the object is the parent of node * * @param object the DataMapper object * @param object the parent node * @return boolean * @access public */ function is_parent_of($object, $node = NULL) { // validate the objects if ( ! $this->is_valid_node($object) OR ! $this->is_valid_node($node) ) { return FALSE; } // fetch the parent of our child node $parent = $node->get_clone()->get_parent(); return ( $parent->id === $object->id ); } // ----------------------------------------------------------------- /** * check if the object has a parent * * Note: this is an alias for is_child() * * @param object the DataMapper object * @return boolean * @access public */ function has_parent($object) { return $this->is_child($object); } // ----------------------------------------------------------------- /** * check if the object has children * * Note: this is an alias for ! is_leaf() * * @param object the DataMapper object * @return boolean * @access public */ function has_children($object) { return $this->is_leaf($object) ? FALSE : TRUE; } // ----------------------------------------------------------------- /** * check if the object has a previous silbling * * @param object the DataMapper object * @return boolean * @access public */ function has_previous_sibling($object) { // fetch the result using a clone $node = $object->get_clone(); return $this->is_valid_node($node->get_previous_sibling($object)); } // ----------------------------------------------------------------- /** * check if the object has a next silbling * * @param object the DataMapper object * @return boolean * @access public */ function has_next_sibling($object) { // fetch the result using a clone $node = $object->get_clone(); return $this->is_valid_node($node->get_next_sibling($object)); } // ----------------------------------------------------------------- // Integer tree functions // ----------------------------------------------------------------- /** * return the count of the objects children * * @param object the DataMapper object * @return integer * @access public */ function count_children($object) { return ( $object->exists() ? (($object->{$this->_rightindex} - $object->{$this->_leftindex} - 1) / 2) : FALSE ); } // ----------------------------------------------------------------- /** * return the node level, where the root = 0 * * @param object the DataMapper object * @return mixed integer, of FALSE in case no valid object was passed * @access public */ function level($object) { if ( $object->exists() ) { // add a root index if needed if ( in_array($this->_rootfield, $object->fields) && ! is_null($this->_rootindex) ) { $object->db->where($this->_rootfield, $this->_rootindex); } $object->where($this->_leftindex.' <', $object->{$this->_leftindex}); $object->where($this->_rightindex.' >', $object->{$this->_rightindex}); return $object->count(); } else { return FALSE; } } // ----------------------------------------------------------------- // Tree reorganisation // ----------------------------------------------------------------- /** * move the object as next sibling of 'node' * * @param object the DataMapper object * @param object the sibling node * @return object the updated DataMapper object * @access public */ function make_next_sibling_of($object, $node) { return $this->_moveSubtree($object, $node, $node->{$this->_rightindex}+1); } // ----------------------------------------------------------------- /** * move the object as previous sibling of 'node' * * @param object the DataMapper object * @param object the sibling node * @return object the updated DataMapper object * @access public */ function make_previous_sibling_of($object, $node) { return $this->_moveSubtree($object, $node, $node->{$this->_leftindex}); } // ----------------------------------------------------------------- /** * move the object as first child of 'node' * * @param object the DataMapper object * @param object the sibling node * @return object the updated DataMapper object * @access public */ function make_first_child_of($object, $node) { return $this->_moveSubtree($object, $node, $node->{$this->_leftindex}+1); } // ----------------------------------------------------------------- /** * move the object as last child of 'node' * * @param object the DataMapper object * @param object the sibling node * @return object the updated DataMapper object * @access public */ function make_last_child_of($object, $node) { return $this->_moveSubtree($object, $node, $node->{$this->_rightindex}); } // ----------------------------------------------------------------- // Tree destructors // ----------------------------------------------------------------- /** * deletes the entire tree structure including all records * * @param object the DataMapper object * @param mixed optional, id of the tree to delete * @return object the updated DataMapper object * @access public */ function remove_tree($object, $tree_id = NULL) { // if we have multiple roots if ( in_array($this->_rootfield, $object->fields) ) { // was a tree id passed? if ( ! is_null($tree_id) ) { // only delete the selected one $object->db->where($this->_rootfield, $tree_id)->delete($object->table); } elseif ( ! is_null($this->_rootindex) ) { // only delete the selected one $object->db->where($this->_rootfield, $this->_rootindex)->delete($object->table); } else { // delete them all $object->db->truncate($object->table); } } else { // delete them all $object->db->truncate($object->table); } // return the cleared object return $object->clear(); } // ----------------------------------------------------------------- /** * deletes the current object, and all childeren * * @param object the DataMapper object * @return object the updated DataMapper object * @access public */ function remove_node($object) { // we need a valid node to do this if ( $object->exists() ) { // if we have multiple roots if ( in_array($this->_rootfield, $object->fields) && ! is_null($this->_rootindex) ) { // only delete the selected one $object->db->where($this->_rootfield, $this->_rootindex); } // clone the object, we need to it shift later $clone = $object->get_clone(); // select the node and all children $object->db->where($this->_leftindex . ' >=', $object->{$this->_leftindex}); $object->db->where($this->_rightindex . ' <=', $object->{$this->_rightindex}); // delete them all $object->db->delete($object->table); // re-index the tree $this->_shiftRLValues($clone, $object->{$this->_rightindex} + 1, $clone->{$this->_leftindex} - $object->{$this->_rightindex} -1); } // return the cleared object return $object->clear(); } // ----------------------------------------------------------------- // dump methods // ----------------------------------------------------------------- /** * returns the tree in a key-value format suitable for html dropdowns * * @param object the DataMapper object * @param string optional, name of the column to use * @param boolean if true, the object itself (root of the dump) will not be included * @return array * @access public */ public function dump_dropdown($object, $field = FALSE, $skip_root = TRUE) { // check if a specific field has been requested if ( empty($field) OR ! isset($this->fields[$field]) ) { // no field given, check if a generic name is defined if ( ! empty($this->_nodename) ) { // yes, so use it $field = $this->_nodename; } else { // can't continue without a name return FALSE; } } // fetch the tree as an array $tree = $this->dump_tree($object, NULL, 'array', $skip_root); // storage for the result $result = array(); if ( $tree ) { // loop trough the tree foreach ( $tree as $key => $value ) { $result[$value['__id']] = str_repeat(' ', ($value['__level']) * 3) . ($value['__level'] ? '» ' : '') . $value[$field]; } } // return the result return $result; } // ----------------------------------------------------------------- /** * dumps the entire tree in HTML or TAB formatted output * * @param object the DataMapper object * @param array list of columns to include in the dump * @param string type of output requested, possible values 'html', 'tab', 'csv', 'array' ('array' = default) * @param boolean if true, the object itself (root of the dump) will not be included * @return mixed * @access public */ public function dump_tree($object, $attributes = NULL, $type = 'array', $skip_root = TRUE) { if ( $this->is_valid_node($object) ) { // do we need a sub-selection of attributes? if ( is_array($attributes) ) { // make sure required fields are present $fields = array_merge($attributes, array('id', $this->_leftindex, $this->_rightindex)); if ( ! empty($this->_nodename) && ! isset($fields[$this->_nodename] ) ) { $fields[] = $this->_nodename; } // add a select $object->db->select($fields); } // create the where clause for this query if ( $skip_root === TRUE ) { // select only all children $object->db->where($this->_leftindex . ' >', $object->{$this->_leftindex}); $object->db->where($this->_rightindex . ' <', $object->{$this->_rightindex}); $level = -1; } else { // select the node and all children $object->db->where($this->_leftindex . ' >=', $object->{$this->_leftindex}); $object->db->where($this->_rightindex . ' <=', $object->{$this->_rightindex}); $level = -2; } // if we have multiple roots if ( in_array($this->_rootfield, $object->fields) && ! is_null($this->_rootindex) ) { // only delete the selected one $object->db->where($this->_rootfield, $this->_rootindex); } // fetch the result $result = $object->db->order_by($this->_leftindex)->get($object->table)->result_array(); // store the last left pointer $last_left = $object->{$this->_leftindex}; // create the path if ( ! empty($this->_nodename) ) { $path = array( $object->{$this->_nodename} ); } else { $path = array(); } // add level and path to the result foreach ( $result as $key => $value ) { // for now, just store the ID $result[$key]['__id'] = $value['id']; // calculate the nest level of this node $level += $last_left - $value[$this->_leftindex] + 2; $last_left = $value[$this->_leftindex]; $result[$key]['__level'] = $level; // create the relative path to this node $result[$key]['__path'] = ''; if ( ! empty($this->_nodename) ) { $path[$level] = $value[$this->_nodename]; for ( $i = 0; $i <= $level; $i++ ) { $result[$key]['__path'] .= '/' . $path[$i]; } } } // convert the result to output if ( in_array($type, array('tab', 'csv', 'html')) ) { // storage for the result $convert = ''; // loop through the elements foreach ( $result as $key => $value ) { // prefix based on requested type switch ($type) { case 'tab'; $convert .= str_repeat("\t", $value['__level'] * 4 ); break; case 'csv'; break; case 'html'; $convert .= str_repeat(" ", $value['__level'] * 4 ); break; } // print the attributes requested if ( ! is_null($attributes) ) { $att = reset($attributes); while($att){ if ( is_numeric($value[$att]) ) { $convert .= $value[$att]; } else { $convert .= '"'.$value[$att].'"'; } $att = next($attributes); if ($att) { $convert .= ($type == 'csv' ? "," : " "); } } } // postfix based on requested type switch ($type) { case 'tab'; $convert .= "\n"; break; case 'csv'; $convert .= "\n"; break; case 'html'; $convert .= "
"; break; } } return $convert; } else { return $result; } } return FALSE; } // ----------------------------------------------------------------- // internal methods // ----------------------------------------------------------------- /** * makes room for a new node (or nodes) by shifting the left and right * id's of nodes with larger values than our object by $delta * * note that $delta can also be negative! * * @param object the DataMapper object * @param integer left value of the start node * @param integer number of positions to shift * @return object the updated DataMapper object * @access private */ private function _shiftRLValues($object, $first, $delta) { // we need a valid object if ( $object->exists() ) { // if we have multiple roots if ( in_array($this->_rootfield, $object->fields) && ! is_null($this->_rootindex) ) { // select the correct one $object->where($this->_rootfield, $this->_rootindex); } // select the range $object->where($this->_leftindex.' >=', $first); $object->update(array($this->_leftindex => $this->_leftindex.' + '.$delta), FALSE); // if we have multiple roots if ( in_array($this->_rootfield, $object->fields) && ! is_null($this->_rootindex) ) { // select the correct one $object->where($this->_rootfield, $this->_rootindex); } // select the range $object->where($this->_rightindex.' >=', $first); $object->update(array($this->_rightindex => $this->_rightindex.' + '.$delta), FALSE); } // return the object return $object; } // ----------------------------------------------------------------- /** * shifts a range of nodes up or down the left and right index by $delta * * note that $delta can also be negative! * * @param object the DataMapper object * @param integer left value of the start node * @param integer right value of the end node * @param integer number of positions to shift * @return object the updated DataMapper object * @access private */ private function _shiftRLRange($object, $first, $last, $delta) { // we need a valid object if ( $object->exists() ) { // if we have multiple roots if ( in_array($this->_rootfield, $object->fields) && ! is_null($this->_rootindex) ) { // select the correct one $object->where($this->_rootfield, $this->_rootindex); } // select the range $object->where($this->_leftindex.' >=', $first); $object->where($this->_rightindex.' <=', $last); $object->update(array($this->_leftindex => $this->_leftindex.' + '.$delta, $this->_rightindex => $this->_rightindex.' + '.$delta), FALSE); } // return the object return $object; } // ----------------------------------------------------------------- /** * inserts a new record into the tree * * @param object the DataMapper object * @return object the updated DataMapper object * @access private */ private function _insertNew($object) { // for now, just save the object $object->save(); // return the object return $object; } // ----------------------------------------------------------------- /** * move a section of the tree to another location within the tree * * @param object the DataMapper object we're going to move * @param integer the destination node's left id value * @return object the updated DataMapper object * @access private */ private function _moveSubtree($object, $node, $destination_id) { // if we have multiple roots if ( in_array($this->_rootfield, $object->fields) ) { // make sure both nodes are part of the same tree if ( $object->{$this->_rootfield} != $node->{$this->_rootfield} ) { return FALSE; } } // determine the size of the tree to move $treesize = $object->{$this->_rightindex} - $object->{$this->_leftindex} + 1; // get the objects left- and right pointers $left_id = $object->{$this->_leftindex}; $right_id = $object->{$this->_rightindex}; // shift to make some space $this->_shiftRLValues($node, $destination_id, $treesize); // enough room now, start the move $this->_shiftRLRange($node, $left_id, $right_id, $destination_id - $left_id); // and correct index values after the source $this->_shiftRLValues($object, $right_id + 1, $treesize * -1); // return the object return $object->get_by_id($object->id); } } /* End of file nestedsets.php */ /* Location: ./application/datamapper/nestedsets.php */