From 2933cd2dd1247143f6d2de03031eb34cd30e8897 Mon Sep 17 00:00:00 2001 From: david Date: Sat, 6 Jan 2007 13:18:51 +0000 Subject: [PATCH] updated NestedSet to use Zend coding standards. still a few problems with return values using () that need to be looked at. --- draft/Node.php | 212 +-- draft/Node/AdjacencyList.php | 5 +- .../Node/AdjacencyList/LevelOrderIterator.php | 3 +- .../Node/AdjacencyList/PostOrderIterator.php | 3 +- draft/Node/AdjacencyList/PreOrderIterator.php | 3 +- draft/Node/Exception.php | 3 +- draft/Node/Interface.php | 406 +++--- draft/Node/MaterializedPath.php | 5 +- .../MaterializedPath/LevelOrderIterator.php | 48 +- .../MaterializedPath/PostOrderIterator.php | 48 +- .../MaterializedPath/PreOrderIterator.php | 48 +- draft/Node/NestedSet.php | 1283 +++++++++-------- draft/Node/NestedSet/LevelOrderIterator.php | 3 +- draft/Node/NestedSet/PostOrderIterator.php | 3 +- draft/Node/NestedSet/PreOrderIterator.php | 122 +- draft/Tree.php | 108 +- draft/Tree/AdjacencyList.php | 3 +- draft/Tree/Exception.php | 3 +- draft/Tree/Interface.php | 56 +- draft/Tree/MaterializedPath.php | 3 +- draft/Tree/NestedSet.php | 195 +-- 21 files changed, 1310 insertions(+), 1253 deletions(-) diff --git a/draft/Node.php b/draft/Node.php index 63b7204d0..b9705ecfa 100644 --- a/draft/Node.php +++ b/draft/Node.php @@ -31,119 +31,127 @@ */ class Doctrine_Node implements IteratorAggregate { - /** - * @param object $record reference to associated Doctrine_Record instance - */ - protected $record; + /** + * @param object $record reference to associated Doctrine_Record instance + */ + protected $record; - /** - * @param array $options - */ - protected $options; + /** + * @param array $options + */ + protected $options; - /** - * @param string $iteratorType (Pre | Post | Level) - */ - protected $iteratorType; + /** + * @param string $iteratorType (Pre | Post | Level) + */ + protected $iteratorType; - /** - * @param array $iteratorOptions - */ - protected $iteratorOptions; + /** + * @param array $iteratorOptions + */ + protected $iteratorOptions; - /** - * contructor, creates node with reference to record and any options - * - * @param object $record instance of Doctrine_Record - * @param array $options options - */ - public function __construct(&$record, $options) - { - $this->record = $record; - $this->options = $options; - } + /** + * contructor, creates node with reference to record and any options + * + * @param object $record instance of Doctrine_Record + * @param array $options options + */ + public function __construct(&$record, $options) + { + $this->record = $record; + $this->options = $options; + } - /** - * factory method to return node instance based upon chosen implementation - * - * @param object $record instance of Doctrine_Record - * @param string $impName implementation (NestedSet, AdjacencyList, MaterializedPath) - * @param array $options options - * @return object $options instance of Doctrine_Node - */ - public static function factory(&$record, $implName, $options = array()) { + /** + * factory method to return node instance based upon chosen implementation + * + * @param object $record instance of Doctrine_Record + * @param string $impName implementation (NestedSet, AdjacencyList, MaterializedPath) + * @param array $options options + * @return object $options instance of Doctrine_Node + */ + public static function factory(&$record, $implName, $options = array()) + { + $class = 'Doctrine_Node_' . $implName; - $class = 'Doctrine_Node_'.$implName; + if (!class_exists($class)) { + throw new Doctrine_Node_Exception("The class $class must exist and extend Doctrine_Node"); + } - if(!class_exists($class)) - throw new Doctrine_Node_Exception("The class $class must exist and extend Doctrine_Node"); + return new $class($record, $options); + } - return new $class($record, $options); - } + /** + * setter for record attribute + * + * @param object $record instance of Doctrine_Record + */ + public function setRecord(&$record) + { + $this->record = $record; + } - /** - * setter for record attribute - * - * @param object $record instance of Doctrine_Record - */ - public function setRecord(&$record) - { - $this->record = $record; - } + /** + * getter for record attribute + * + * @return object instance of Doctrine_Record + */ + public function getRecord() + { + return $this->record; + } - /** - * getter for record attribute - * - * @return object instance of Doctrine_Record - */ - public function getRecord() { - return $this->record; - } + /** + * convenience function for getIterator + * + * @param string $type type of iterator (Pre | Post | Level) + * @param array $options options + */ + public function traverse($type = 'Pre', $options = array()) + { + return $this->getIterator($type, $options); + } - /** - * convenience function for getIterator - * - * @param string $type type of iterator (Pre | Post | Level) - * @param array $options options - */ - public function traverse($type = 'Pre', $options = array()) { - return $this->getIterator($type, $options); - } + /** + * get iterator + * + * @param string $type type of iterator (Pre | Post | Level) + * @param array $options options + */ + public function getIterator($type = null, $options = null) + { + if ($type === null) { + $type = (isset($this->iteratorType) ? $this->iteratorType : 'Pre'); + } - /** - * get iterator - * - * @param string $type type of iterator (Pre | Post | Level) - * @param array $options options - */ - public function getIterator($type = null, $options = null) - { - if ($type === null) - $type = (isset($this->iteratorType) ? $this->iteratorType : 'Pre'); - - if ($options === null) - $options = (isset($this->iteratorOptions) ? $this->iteratorOptions : array()); - - $iteratorClass = 'Doctrine_Node_'.$this->record->getTable()->getTreeImplName().'_'.ucfirst(strtolower($type)).'OrderIterator'; - - return new $iteratorClass($this->record, $options); - } + if ($options === null) { + $options = (isset($this->iteratorOptions) ? $this->iteratorOptions : array()); + } - /** - * sets node's iterator type - * - * @param int - */ - public function setIteratorType($type) { - $this->iteratorType = $type; - } + $implName = $this->record->getTable()->getTreeImplName(); + $iteratorClass = 'Doctrine_Node_' . $implName . '_' . ucfirst(strtolower($type)) . 'OrderIterator'; - /** - * sets node's iterator options - * - * @param int - */ - public function setIteratorOptions($options) { - $this->iteratorOptions = $options; - } -} // END class \ No newline at end of file + return new $iteratorClass($this->record, $options); + } + + /** + * sets node's iterator type + * + * @param int + */ + public function setIteratorType($type) + { + $this->iteratorType = $type; + } + + /** + * sets node's iterator options + * + * @param int + */ + public function setIteratorOptions($options) + { + $this->iteratorOptions = $options; + } +} diff --git a/draft/Node/AdjacencyList.php b/draft/Node/AdjacencyList.php index be4850396..8dc1138a6 100644 --- a/draft/Node/AdjacencyList.php +++ b/draft/Node/AdjacencyList.php @@ -29,5 +29,6 @@ * @version $Revision$ * @author Joe Simms */ -class Doctrine_Node_AdjacencyList extends Doctrine_Node implements Doctrine_Node_Interface { } - \ No newline at end of file +class Doctrine_Node_AdjacencyList extends Doctrine_Node implements Doctrine_Node_Interface +{} + diff --git a/draft/Node/AdjacencyList/LevelOrderIterator.php b/draft/Node/AdjacencyList/LevelOrderIterator.php index 6cf056160..cb0521775 100644 --- a/draft/Node/AdjacencyList/LevelOrderIterator.php +++ b/draft/Node/AdjacencyList/LevelOrderIterator.php @@ -29,4 +29,5 @@ * @version $Revision$ * @author Joe Simms */ -class Doctrine_Node_AdjacencyList_LevelOrderIterator implements Iterator {} +class Doctrine_Node_AdjacencyList_LevelOrderIterator implements Iterator +{} diff --git a/draft/Node/AdjacencyList/PostOrderIterator.php b/draft/Node/AdjacencyList/PostOrderIterator.php index 4cadfb817..6da50ea39 100644 --- a/draft/Node/AdjacencyList/PostOrderIterator.php +++ b/draft/Node/AdjacencyList/PostOrderIterator.php @@ -29,4 +29,5 @@ * @version $Revision$ * @author Joe Simms */ -class Doctrine_Node_AdjacencyList_PostOrderIterator implements Iterator {} +class Doctrine_Node_AdjacencyList_PostOrderIterator implements Iterator +{} diff --git a/draft/Node/AdjacencyList/PreOrderIterator.php b/draft/Node/AdjacencyList/PreOrderIterator.php index f7887cbf8..4bf3bd865 100644 --- a/draft/Node/AdjacencyList/PreOrderIterator.php +++ b/draft/Node/AdjacencyList/PreOrderIterator.php @@ -29,4 +29,5 @@ * @version $Revision$ * @author Joe Simms */ -class Doctrine_Node_AdjacencyList_PreOrderIterator implements Iterator {} +class Doctrine_Node_AdjacencyList_PreOrderIterator implements Iterator +{} diff --git a/draft/Node/Exception.php b/draft/Node/Exception.php index a76238d36..099bbe673 100644 --- a/draft/Node/Exception.php +++ b/draft/Node/Exception.php @@ -29,4 +29,5 @@ * @version $Revision$ * @author Joe Simms */ -class Doctrine_Node_Exception extends Doctrine_Exception { } +class Doctrine_Node_Exception extends Doctrine_Exception +{} diff --git a/draft/Node/Interface.php b/draft/Node/Interface.php index f2f18a4ac..c6a6ce394 100644 --- a/draft/Node/Interface.php +++ b/draft/Node/Interface.php @@ -31,237 +31,237 @@ */ interface Doctrine_Node_Interface { - /** - * test if node has previous sibling - * - * @return bool - */ - public function hasPrevSibling(); + /** + * test if node has previous sibling + * + * @return bool + */ + public function hasPrevSibling(); - /** - * test if node has next sibling - * - * @return bool - */ - public function hasNextSibling(); + /** + * test if node has next sibling + * + * @return bool + */ + public function hasNextSibling(); - /** - * test if node has children - * - * @return bool - */ - public function hasChildren(); + /** + * test if node has children + * + * @return bool + */ + public function hasChildren(); - /** - * test if node has parent - * - * @return bool - */ - public function hasParent(); + /** + * test if node has parent + * + * @return bool + */ + public function hasParent(); - /** - * gets record of prev sibling or empty record - * - * @return object Doctrine_Record - */ - public function getPrevSibling(); - - /** - * gets record of next sibling or empty record - * - * @return object Doctrine_Record - */ - public function getNextSibling(); - - /** - * gets siblings for node - * - * @return array array of sibling Doctrine_Record objects - */ - public function getSiblings($includeNode = false); + /** + * gets record of prev sibling or empty record + * + * @return object Doctrine_Record + */ + public function getPrevSibling(); - /** - * gets record of first child or empty record - * - * @return object Doctrine_Record - */ - public function getFirstChild(); + /** + * gets record of next sibling or empty record + * + * @return object Doctrine_Record + */ + public function getNextSibling(); - /** - * gets record of last child or empty record - * - * @return object Doctrine_Record - */ - public function getLastChild(); + /** + * gets siblings for node + * + * @return array array of sibling Doctrine_Record objects + */ + public function getSiblings($includeNode = false); - /** - * gets children for node (direct descendants only) - * - * @return array array of sibling Doctrine_Record objects - */ - public function getChildren(); + /** + * gets record of first child or empty record + * + * @return object Doctrine_Record + */ + public function getFirstChild(); - /** - * gets descendants for node (direct descendants only) - * - * @return iterator iterator to traverse descendants from node - */ - public function getDescendants(); + /** + * gets record of last child or empty record + * + * @return object Doctrine_Record + */ + public function getLastChild(); - /** - * gets record of parent or empty record - * - * @return object Doctrine_Record - */ - public function getParent(); + /** + * gets children for node (direct descendants only) + * + * @return array array of sibling Doctrine_Record objects + */ + public function getChildren(); - /** - * gets ancestors for node - * - * @return object Doctrine_Collection - */ - public function getAncestors(); + /** + * gets descendants for node (direct descendants only) + * + * @return iterator iterator to traverse descendants from node + */ + public function getDescendants(); - /** - * gets path to node from root, uses record::toString() method to get node names - * - * @param string $seperator path seperator - * @param bool $includeNode whether or not to include node at end of path - * @return string string representation of path - */ - public function getPath($seperator = ' > ', $includeNode = false); + /** + * gets record of parent or empty record + * + * @return object Doctrine_Record + */ + public function getParent(); - /** - * gets level (depth) of node in the tree - * - * @return int - */ - public function getLevel(); + /** + * gets ancestors for node + * + * @return object Doctrine_Collection + */ + public function getAncestors(); - /** - * gets number of children (direct descendants) - * - * @return int - */ - public function getNumberChildren(); + /** + * gets path to node from root, uses record::toString() method to get node names + * + * @param string $seperator path seperator + * @param bool $includeNode whether or not to include node at end of path + * @return string string representation of path + */ + public function getPath($seperator = ' > ', $includeNode = false); - /** - * gets number of descendants (children and their children) - * - * @return int - */ - public function getNumberDescendants(); + /** + * gets level (depth) of node in the tree + * + * @return int + */ + public function getLevel(); - /** - * inserts node as parent of dest record - * - * @return bool - */ - public function insertAsParentOf(Doctrine_Record $dest); + /** + * gets number of children (direct descendants) + * + * @return int + */ + public function getNumberChildren(); - /** - * inserts node as previous sibling of dest record - * - * @return bool - */ - public function insertAsPrevSiblingOf(Doctrine_Record $dest); + /** + * gets number of descendants (children and their children) + * + * @return int + */ + public function getNumberDescendants(); - /** - * inserts node as next sibling of dest record - * - * @return bool - */ - public function insertAsNextSiblingOf(Doctrine_Record $dest); + /** + * inserts node as parent of dest record + * + * @return bool + */ + public function insertAsParentOf(Doctrine_Record $dest); - /** - * inserts node as first child of dest record - * - * @return bool - */ - public function insertAsFirstChildOf(Doctrine_Record $dest); + /** + * inserts node as previous sibling of dest record + * + * @return bool + */ + public function insertAsPrevSiblingOf(Doctrine_Record $dest); - /** - * inserts node as first child of dest record - * - * @return bool - */ - public function insertAsLastChildOf(Doctrine_Record $dest); + /** + * inserts node as next sibling of dest record + * + * @return bool + */ + public function insertAsNextSiblingOf(Doctrine_Record $dest); - /** - * moves node as prev sibling of dest record - * - */ - public function moveAsPrevSiblingOf(Doctrine_Record $dest); + /** + * inserts node as first child of dest record + * + * @return bool + */ + public function insertAsFirstChildOf(Doctrine_Record $dest); - /** - * moves node as next sibling of dest record - * - */ - public function moveAsNextSiblingOf(Doctrine_Record $dest); + /** + * inserts node as first child of dest record + * + * @return bool + */ + public function insertAsLastChildOf(Doctrine_Record $dest); - /** - * moves node as first child of dest record - * - */ - public function moveAsFirstChildOf(Doctrine_Record $dest); + /** + * moves node as prev sibling of dest record + * + */ + public function moveAsPrevSiblingOf(Doctrine_Record $dest); - /** - * moves node as last child of dest record - * - */ - public function moveAsLastChildOf(Doctrine_Record $dest); + /** + * moves node as next sibling of dest record + * + */ + public function moveAsNextSiblingOf(Doctrine_Record $dest); - /** - * adds node as last child of record - * - */ - public function addChild(Doctrine_Record $record); + /** + * moves node as first child of dest record + * + */ + public function moveAsFirstChildOf(Doctrine_Record $dest); - /** - * determines if node is leaf - * - * @return bool - */ - public function isLeaf(); + /** + * moves node as last child of dest record + * + */ + public function moveAsLastChildOf(Doctrine_Record $dest); - /** - * determines if node is root - * - * @return bool - */ - public function isRoot(); + /** + * adds node as last child of record + * + */ + public function addChild(Doctrine_Record $record); - /** - * determines if node is equal to subject node - * - * @return bool - */ - public function isEqualTo(Doctrine_Record $subj); + /** + * determines if node is leaf + * + * @return bool + */ + public function isLeaf(); - /** - * determines if node is child of subject node - * - * @return bool - */ - public function isDescendantOf(Doctrine_Record $subj); + /** + * determines if node is root + * + * @return bool + */ + public function isRoot(); - /** - * determines if node is child of or sibling to subject node - * - * @return bool - */ - public function isDescendantOfOrEqualTo(Doctrine_Record $subj); + /** + * determines if node is equal to subject node + * + * @return bool + */ + public function isEqualTo(Doctrine_Record $subj); - /** - * determines if node is valid - * - * @return bool - */ - public function isValidNode(); + /** + * determines if node is child of subject node + * + * @return bool + */ + public function isDescendantOf(Doctrine_Record $subj); - /** - * deletes node and it's descendants - * - */ - public function delete(); -} \ No newline at end of file + /** + * determines if node is child of or sibling to subject node + * + * @return bool + */ + public function isDescendantOfOrEqualTo(Doctrine_Record $subj); + + /** + * determines if node is valid + * + * @return bool + */ + public function isValidNode(); + + /** + * deletes node and it's descendants + * + */ + public function delete(); +} diff --git a/draft/Node/MaterializedPath.php b/draft/Node/MaterializedPath.php index df163f2c2..1887bf37d 100644 --- a/draft/Node/MaterializedPath.php +++ b/draft/Node/MaterializedPath.php @@ -29,5 +29,6 @@ * @version $Revision$ * @author Joe Simms */ - class Doctrine_Node_MaterializedPath extends Doctrine_Node implements Doctrine_Node_Interface { } - \ No newline at end of file +class Doctrine_Node_MaterializedPath extends Doctrine_Node implements Doctrine_Node_Interface +{} + diff --git a/draft/Node/MaterializedPath/LevelOrderIterator.php b/draft/Node/MaterializedPath/LevelOrderIterator.php index 3a3d8ab9c..fe43dc6c5 100644 --- a/draft/Node/MaterializedPath/LevelOrderIterator.php +++ b/draft/Node/MaterializedPath/LevelOrderIterator.php @@ -32,30 +32,36 @@ class Doctrine_Node_MaterializedPath_LevelOrderIterator implements Iterator { private $topNode = null; - + private $curNode = null; - - public function __construct($node, $opts) { - throw new Doctrine_Exception('Not yet implemented'); - } - - public function rewind() { - throw new Doctrine_Exception('Not yet implemented'); - } - - public function valid() { - throw new Doctrine_Exception('Not yet implemented'); - } - - public function current() { - throw new Doctrine_Exception('Not yet implemented'); - } - - public function key() { + + public function __construct($node, $opts) + { throw new Doctrine_Exception('Not yet implemented'); } - public function next() { + public function rewind() + { throw new Doctrine_Exception('Not yet implemented'); - } + } + + public function valid() + { + throw new Doctrine_Exception('Not yet implemented'); + } + + public function current() + { + throw new Doctrine_Exception('Not yet implemented'); + } + + public function key() + { + throw new Doctrine_Exception('Not yet implemented'); + } + + public function next() + { + throw new Doctrine_Exception('Not yet implemented'); + } } diff --git a/draft/Node/MaterializedPath/PostOrderIterator.php b/draft/Node/MaterializedPath/PostOrderIterator.php index 7c8c5a378..ae65bd11c 100644 --- a/draft/Node/MaterializedPath/PostOrderIterator.php +++ b/draft/Node/MaterializedPath/PostOrderIterator.php @@ -32,30 +32,36 @@ class Doctrine_Node_MaterializedPath_PostOrderIterator implements Iterator { private $topNode = null; - + private $curNode = null; - - public function __construct($node, $opts) { - throw new Doctrine_Exception('Not yet implemented'); - } - - public function rewind() { - throw new Doctrine_Exception('Not yet implemented'); - } - - public function valid() { - throw new Doctrine_Exception('Not yet implemented'); - } - - public function current() { - throw new Doctrine_Exception('Not yet implemented'); - } - - public function key() { + + public function __construct($node, $opts) + { throw new Doctrine_Exception('Not yet implemented'); } - public function next() { + public function rewind() + { throw new Doctrine_Exception('Not yet implemented'); - } + } + + public function valid() + { + throw new Doctrine_Exception('Not yet implemented'); + } + + public function current() + { + throw new Doctrine_Exception('Not yet implemented'); + } + + public function key() + { + throw new Doctrine_Exception('Not yet implemented'); + } + + public function next() + { + throw new Doctrine_Exception('Not yet implemented'); + } } diff --git a/draft/Node/MaterializedPath/PreOrderIterator.php b/draft/Node/MaterializedPath/PreOrderIterator.php index 9838cd73c..e76420d67 100644 --- a/draft/Node/MaterializedPath/PreOrderIterator.php +++ b/draft/Node/MaterializedPath/PreOrderIterator.php @@ -32,30 +32,36 @@ class Doctrine_Node_MaterializedPath_PreOrderIterator implements Iterator { private $topNode = null; - + private $curNode = null; - - public function __construct($node, $opts) { - throw new Doctrine_Exception('Not yet implemented'); - } - - public function rewind() { - throw new Doctrine_Exception('Not yet implemented'); - } - - public function valid() { - throw new Doctrine_Exception('Not yet implemented'); - } - - public function current() { - throw new Doctrine_Exception('Not yet implemented'); - } - - public function key() { + + public function __construct($node, $opts) + { throw new Doctrine_Exception('Not yet implemented'); } - public function next() { + public function rewind() + { throw new Doctrine_Exception('Not yet implemented'); - } + } + + public function valid() + { + throw new Doctrine_Exception('Not yet implemented'); + } + + public function current() + { + throw new Doctrine_Exception('Not yet implemented'); + } + + public function key() + { + throw new Doctrine_Exception('Not yet implemented'); + } + + public function next() + { + throw new Doctrine_Exception('Not yet implemented'); + } } diff --git a/draft/Node/NestedSet.php b/draft/Node/NestedSet.php index 939cc04cd..b0adf6511 100644 --- a/draft/Node/NestedSet.php +++ b/draft/Node/NestedSet.php @@ -31,643 +31,654 @@ */ class Doctrine_Node_NestedSet extends Doctrine_Node implements Doctrine_Node_Interface { - /** - * test if node has previous sibling - * - * @return bool - */ - public function hasPrevSibling() - { - return $this->isValidNode($this->getPrevSibling()); - } - - /** - * test if node has next sibling - * - * @return bool - */ - public function hasNextSibling() - { - return $this->isValidNode($this->getNextSibling()); - } - - /** - * test if node has children - * - * @return bool - */ - public function hasChildren() - { - return (($this->getRightValue() - $this->getLeftValue() ) >1 ); - } - - /** - * test if node has parent - * - * @return bool - */ - public function hasParent() - { - return !$this->isRoot(); - } - - /** - * gets record of prev sibling or empty record - * - * @return object Doctrine_Record - */ - public function getPrevSibling() - { - $q = $this->record->getTable()->createQuery(); - $result = $q->where('rgt = ?', $this->getLeftValue() - 1)->execute()->getFirst(); - - if(!$result) - $result = $this->record->getTable()->create(); - - return $result; - } - - /** - * gets record of next sibling or empty record - * - * @return object Doctrine_Record - */ - public function getNextSibling() - { - $q = $this->record->getTable()->createQuery(); - $result = $q->where('lft = ?', $this->getRightValue() + 1)->execute()->getFirst(); - - if(!$result) - $result = $this->record->getTable()->create(); - - return $result; - } - - /** - * gets siblings for node - * - * @return array array of sibling Doctrine_Record objects - */ - public function getSiblings($includeNode = false) - { - $parent = $this->getParent(); - $siblings = array(); - if($parent->exists()) + /** + * test if node has previous sibling + * + * @return bool + */ + public function hasPrevSibling() { - foreach($parent->getNode()->getChildren() as $child) - { - if($this->isEqualTo($child) && !$includeNode) - continue; - - $siblings[] = $child; - } - } - - return $siblings; - } - - /** - * gets record of first child or empty record - * - * @return object Doctrine_Record - */ - public function getFirstChild() - { - $q = $this->record->getTable()->createQuery(); - $result = $q->where('lft = ?', $this->getLeftValue() + 1)->execute()->getFirst(); - - if(!$result) - $result = $this->record->getTable()->create(); - - return $result; - } - - /** - * gets record of last child or empty record - * - * @return object Doctrine_Record - */ - public function getLastChild() - { - $q = $this->record->getTable()->createQuery(); - $result = $q->where('rgt = ?', $this->getRightValue() - 1)->execute()->getFirst(); - - if(!$result) - $result = $this->record->getTable()->create(); - - return $result; - } - - /** - * gets children for node (direct descendants only) - * - * @return array array of sibling Doctrine_Record objects - */ - public function getChildren() - { - return $this->getIterator('Pre', array('depth' => 1)); - } - - /** - * gets descendants for node (direct descendants only) - * - * @return iterator iterator to traverse descendants from node - */ - public function getDescendants() - { - return $this->getIterator(); - } - - /** - * gets record of parent or empty record - * - * @return object Doctrine_Record - */ - public function getParent() - { - $q = $this->record->getTable()->createQuery(); - - $parent = $q->where('lft < ? AND rgt > ?', array($this->getLeftValue(), $this->getRightValue())) - ->orderBy('rgt asc') - ->execute() - ->getFirst(); - - if(!$parent) - $parent = $this->record->getTable()->create(); - - return $parent; - } - - /** - * gets ancestors for node - * - * @return object Doctrine_Collection - */ - public function getAncestors() - { - $q = $this->record->getTable()->createQuery(); - - $ancestors = $q->where('lft < ? AND rgt > ?', array($this->getLeftValue(), $this->getRightValue())) - ->orderBy('lft asc') - ->execute(); - - return $ancestors; - } - - /** - * gets path to node from root, uses record::toString() method to get node names - * - * @param string $seperator path seperator - * @param bool $includeNode whether or not to include node at end of path - * @return string string representation of path - */ - public function getPath($seperator = ' > ', $includeRecord = false) - { - $path = array(); - $ancestors = $this->getAncestors(); - foreach($ancestors as $ancestor) - { - $path[] = $ancestor->toString(); - } - if($includeRecord) - $path[] = $this->getRecord()->toString(); - - return implode($seperator, $path); - } - - /** - * gets number of children (direct descendants) - * - * @return int - */ - public function getNumberChildren() - { - $count = 0; - $children = $this->getChildren(); - - while($children->next()) - { - $count++; - } - return $count; - } - - /** - * gets number of descendants (children and their children) - * - * @return int - */ - public function getNumberDescendants() - { - return ($this->getRightValue() - $this->getLeftValue() - 1) / 2; - } - - /** - * inserts node as parent of dest record - * - * @return bool - */ - public function insertAsParentOf(Doctrine_Record $dest) - { - // cannot insert a node that has already has a place within the tree - if($this->isValidNode()) - return false; - - // cannot insert as parent of root - if($dest->getNode()->isRoot()) - return false; - - $this->shiftRLValues($dest->getNode()->getLeftValue(), 1); - $this->shiftRLValues($dest->getNode()->getRightValue() + 2, 1); - - $newLeft = $dest->getNode()->getLeftValue(); - $newRight = $dest->getNode()->getRightValue() + 2; - $this->insertNode($newLeft, $newRight); - - return true; - } - - /** - * inserts node as previous sibling of dest record - * - * @return bool - */ - public function insertAsPrevSiblingOf(Doctrine_Record $dest) - { - // cannot insert a node that has already has a place within the tree - if($this->isValidNode()) - return false; - - $newLeft = $dest->getNode()->getLeftValue(); - $newRight = $dest->getNode()->getLeftValue() + 1; - - $this->shiftRLValues($newLeft, 2); - $this->insertNode($newLeft, $newRight); - - // update destination left/right values to prevent a refresh - // $dest->getNode()->setLeftValue($dest->getNode()->getLeftValue() + 2); - // $dest->getNode()->setRightValue($dest->getNode()->getRightValue() + 2); - - return true; - } - - /** - * inserts node as next sibling of dest record - * - * @return bool - */ - public function insertAsNextSiblingOf(Doctrine_Record $dest) - { - // cannot insert a node that has already has a place within the tree - if($this->isValidNode()) - return false; - - $newLeft = $dest->getNode()->getRightValue() + 1; - $newRight = $dest->getNode()->getRightValue() + 2; - - $this->shiftRLValues($newLeft, 2); - $this->insertNode($newLeft, $newRight); - - // update destination left/right values to prevent a refresh - // no need, node not affected - - return true; - } - - /** - * inserts node as first child of dest record - * - * @return bool - */ - public function insertAsFirstChildOf(Doctrine_Record $dest) - { - // cannot insert a node that has already has a place within the tree - if($this->isValidNode()) - return false; - - $newLeft = $dest->getNode()->getLeftValue() + 1; - $newRight = $dest->getNode()->getLeftValue() + 2; - - $this->shiftRLValues($newLeft, 2); - $this->insertNode($newLeft, $newRight); - - // update destination left/right values to prevent a refresh - // $dest->getNode()->setRightValue($dest->getNode()->getRightValue() + 2); - - return true; - } - - /** - * inserts node as last child of dest record - * - * @return bool - */ - public function insertAsLastChildOf(Doctrine_Record $dest) - { - // cannot insert a node that has already has a place within the tree - if($this->isValidNode()) - return false; - - $newLeft = $dest->getNode()->getRightValue(); - $newRight = $dest->getNode()->getRightValue() + 1; - - $this->shiftRLValues($newLeft, 2); - $this->insertNode($newLeft, $newRight); - - // update destination left/right values to prevent a refresh - // $dest->getNode()->setRightValue($dest->getNode()->getRightValue() + 2); - - return true; - } - - /** - * moves node as prev sibling of dest record - * - */ - public function moveAsPrevSiblingOf(Doctrine_Record $dest) - { - $this->updateNode($dest->getNode()->getLeftValue()); - } - - /** - * moves node as next sibling of dest record - * - */ - public function moveAsNextSiblingOf(Doctrine_Record $dest) - { - $this->updateNode($dest->getNode()->getRightValue() + 1); - } - - /** - * moves node as first child of dest record - * - */ - public function moveAsFirstChildOf(Doctrine_Record $dest) - { - $this->updateNode($dest->getNode()->getLeftValue() + 1); - } - - /** - * moves node as last child of dest record - * - */ - public function moveAsLastChildOf(Doctrine_Record $dest) - { - $this->updateNode($dest->getNode()->getRightValue()); - } - - /** - * adds node as last child of record - * - */ - public function addChild(Doctrine_Record $record) - { - $record->getNode()->insertAsLastChildOf($this->getRecord()); - } - - /** - * determines if node is leaf - * - * @return bool - */ - public function isLeaf() - { - return (($this->getRightValue()-$this->getLeftValue())==1); - } - - /** - * determines if node is root - * - * @return bool - */ - public function isRoot() - { - return ($this->getLeftValue()==1); - } - - /** - * determines if node is equal to subject node - * - * @return bool - */ - public function isEqualTo(Doctrine_Record $subj) - { - return (($this->getLeftValue()==$subj->getNode()->getLeftValue()) and ($this->getRightValue()==$subj->getNode()->getRightValue())); - } - - /** - * determines if node is child of subject node - * - * @return bool - */ - public function isDescendantOf(Doctrine_Record $subj) - { - return (($this->getLeftValue()>$subj->getNode()->getLeftValue()) and ($this->getRightValue()<$subj->getNode()->getRightValue())); - } - - /** - * determines if node is child of or sibling to subject node - * - * @return bool - */ - public function isDescendantOfOrEqualTo(Doctrine_Record $subj) - { - return (($this->getLeftValue()>=$subj->getNode()->getLeftValue()) and ($this->getRightValue()<=$subj->getNode()->getRightValue())); - } - - /** - * determines if node is valid - * - * @return bool - */ - public function isValidNode() - { - return ($this->getRightValue() > $this->getLeftValue()); - } - - /** - * deletes node and it's descendants - * - */ - public function delete() - { - // TODO: add the setting whether or not to delete descendants or relocate children - - $q = $this->record->getTable()->createQuery(); - - $componentName = $this->record->getTable()->getComponentName(); - - $coll = $q->where("$componentName.lft >= ? AND $componentName.rgt <= ?", array($this->getLeftValue(), $this->getRightValue()))->execute(); - $coll->delete(); - - $first = $this->getRightValue() + 1; - $delta = $this->getLeftValue() - $this->getRightValue() - 1; - $this->shiftRLValues($first, $delta); - - return true; - } - - /** - * sets node's left and right values and save's it - * - * @param int $destLeft node left value - * @param int $destRight node right value - */ - private function insertNode($destLeft = 0, $destRight = 0) - { - $this->setLeftValue($destLeft); - $this->setRightValue($destRight); - $this->record->save(); - } - - /** - * move node's and its children to location $destLeft and updates rest of tree - * - * @param int $destLeft destination left value - */ - private function updateNode($destLeft) - { - $left = $this->getLeftValue(); - $right = $this->getRightValue(); - - $treeSize = $right - $left + 1; - - $this->shiftRLValues($destLeft, $treeSize); - - if($left >= $destLeft){ // src was shifted too? - $left += $treeSize; - $right += $treeSize; - } - - // now there's enough room next to target to move the subtree - $this->shiftRLRange($left, $right, $destLeft - $left); - - // correct values after source - $this->shiftRLValues($right + 1, -$treeSize); - - $this->record->save(); - $this->record->refresh(); - } - - /** - * adds '$delta' to all Left and Right values that are >= '$first'. '$delta' can also be negative. - * - * @param int $first First node to be shifted - * @param int $delta Value to be shifted by, can be negative - */ - private function shiftRLValues($first, $delta) - { - $qLeft = $this->record->getTable()->createQuery(); - $qRight = $this->record->getTable()->createQuery(); - - // TODO: Wrap in transaction - - // shift left columns - $resultLeft = $qLeft->update($this->record->getTable()->getComponentName()) - ->set('lft', "lft + $delta") - ->where('lft >= ?', $first) - ->execute(); - - // shift right columns - $resultRight = $qRight->update($this->record->getTable()->getComponentName()) - ->set('rgt', "rgt + $delta") - ->where('rgt >= ?', $first) - ->execute(); - } - - /** - * adds '$delta' to all Left and Right values that are >= '$first' and <= '$last'. - * '$delta' can also be negative. - * - * @param int $first First node to be shifted (L value) - * @param int $last Last node to be shifted (L value) - * @param int $delta Value to be shifted by, can be negative - */ - private function shiftRLRange($first, $last, $delta) - { - $qLeft = $this->record->getTable()->createQuery(); - $qRight = $this->record->getTable()->createQuery(); - - // TODO : Wrap in transaction - - // shift left column values - $result = $qLeft->update($this->record->getTable()->getComponentName()) - ->set('lft', "lft + $delta") - ->where('lft >= ? AND lft <= ?', array($first, $last)) - ->execute(); - - // shift right column values - $result = $qRight->update($this->record->getTable()->getComponentName()) - ->set('rgt', "rgt + $delta") - ->where('rgt >= ? AND rgt <= ?', array($first, $last)) - ->execute(); - } - - /** - * gets record's left value - * - * @return int - */ - public function getLeftValue() - { - return $this->record->get('lft'); - } - - /** - * sets record's left value - * - * @param int - */ - public function setLeftValue($lft) - { - $this->record->set('lft', $lft); - } - - /** - * gets record's right value - * - * @return int - */ - public function getRightValue() - { - return $this->record->get('rgt'); - } - - /** - * sets record's right value - * - * @param int - */ - public function setRightValue($rgt) - { - $this->record->set('rgt', $rgt); - } - - /** - * gets level (depth) of node in the tree - * - * @return int - */ - public function getLevel() - { - if(!isset($this->level)) - { - $q = $this->record->getTable()->createQuery(); - $coll = $q->where('lft < ? AND rgt > ?', array($this->getLeftValue(), $this->getRightValue())) - ->execute(); - $this->level = $coll->count() ? $coll->count() : 0; + return $this->isValidNode($this->getPrevSibling()); } - return $this->level; - } + /** + * test if node has next sibling + * + * @return bool + */ + public function hasNextSibling() + { + return $this->isValidNode($this->getNextSibling()); + } - /** - * sets node's level - * - * @param int - */ - public function setLevel($level) - { - $this->level = $level; - } -} \ No newline at end of file + /** + * test if node has children + * + * @return bool + */ + public function hasChildren() + { + return (($this->getRightValue() - $this->getLeftValue() ) >1); + } + + /** + * test if node has parent + * + * @return bool + */ + public function hasParent() + { + return !$this->isRoot(); + } + + /** + * gets record of prev sibling or empty record + * + * @return object Doctrine_Record + */ + public function getPrevSibling() + { + $q = $this->record->getTable()->createQuery(); + $result = $q->where('rgt = ?', $this->getLeftValue() - 1)->execute()->getFirst(); + + if (!$result) { + $result = $this->record->getTable()->create(); + } + + return $result; + } + + /** + * gets record of next sibling or empty record + * + * @return object Doctrine_Record + */ + public function getNextSibling() + { + $q = $this->record->getTable()->createQuery(); + $result = $q->where('lft = ?', $this->getRightValue() + 1)->execute()->getFirst(); + + if (!$result) { + $result = $this->record->getTable()->create(); + } + + return $result; + } + + /** + * gets siblings for node + * + * @return array array of sibling Doctrine_Record objects + */ + public function getSiblings($includeNode = false) + { + $parent = $this->getParent(); + $siblings = array(); + if ($parent->exists()) { + foreach($parent->getNode()->getChildren() as $child) { + if ($this->isEqualTo($child) && !$includeNode) { + continue; + } + $siblings[] = $child; + } + } + + return $siblings; + } + + /** + * gets record of first child or empty record + * + * @return object Doctrine_Record + */ + public function getFirstChild() + { + $q = $this->record->getTable()->createQuery(); + $result = $q->where('lft = ?', $this->getLeftValue() + 1)->execute()->getFirst(); + + if (!$result) { + $result = $this->record->getTable()->create(); + } + + return $result; + } + + /** + * gets record of last child or empty record + * + * @return object Doctrine_Record + */ + public function getLastChild() + { + $q = $this->record->getTable()->createQuery(); + $result = $q->where('rgt = ?', $this->getRightValue() - 1)->execute()->getFirst(); + + if (!$result) { + $result = $this->record->getTable()->create(); + } + + return $result; + } + + /** + * gets children for node (direct descendants only) + * + * @return array array of sibling Doctrine_Record objects + */ + public function getChildren() + { + return $this->getIterator('Pre', array('depth' => 1)); + } + + /** + * gets descendants for node (direct descendants only) + * + * @return iterator iterator to traverse descendants from node + */ + public function getDescendants() + { + return $this->getIterator(); + } + + /** + * gets record of parent or empty record + * + * @return object Doctrine_Record + */ + public function getParent() + { + $q = $this->record->getTable()->createQuery(); + + $parent = $q->where('lft < ? AND rgt > ?', array($this->getLeftValue(), $this->getRightValue())) + ->orderBy('rgt asc') + ->execute() + ->getFirst(); + + if (!$parent) { + $parent = $this->record->getTable()->create(); + } + + return $parent; + } + + /** + * gets ancestors for node + * + * @return object Doctrine_Collection + */ + public function getAncestors() + { + $q = $this->record->getTable()->createQuery(); + + $ancestors = $q->where('lft < ? AND rgt > ?', array($this->getLeftValue(), $this->getRightValue())) + ->orderBy('lft asc') + ->execute(); + + return $ancestors; + } + + /** + * gets path to node from root, uses record::toString() method to get node names + * + * @param string $seperator path seperator + * @param bool $includeNode whether or not to include node at end of path + * @return string string representation of path + */ + public function getPath($seperator = ' > ', $includeRecord = false) + { + $path = array(); + $ancestors = $this->getAncestors(); + foreach ($ancestors as $ancestor) { + $path[] = $ancestor->toString(); + } + if ($includeRecord) { + $path[] = $this->getRecord()->toString(); + } + + return implode($seperator, $path); + } + + /** + * gets number of children (direct descendants) + * + * @return int + */ + public function getNumberChildren() + { + $count = 0; + $children = $this->getChildren(); + + while ($children->next()) { + $count++; + } + return $count; + } + + /** + * gets number of descendants (children and their children) + * + * @return int + */ + public function getNumberDescendants() + { + return ($this->getRightValue() - $this->getLeftValue() - 1) / 2; + } + + /** + * inserts node as parent of dest record + * + * @return bool + */ + public function insertAsParentOf(Doctrine_Record $dest) + { + // cannot insert a node that has already has a place within the tree + if ($this->isValidNode()) { + return false; + } + + // cannot insert as parent of root + if ($dest->getNode()->isRoot()) { + return false; + } + + $this->shiftRLValues($dest->getNode()->getLeftValue(), 1); + $this->shiftRLValues($dest->getNode()->getRightValue() + 2, 1); + + $newLeft = $dest->getNode()->getLeftValue(); + $newRight = $dest->getNode()->getRightValue() + 2; + $this->insertNode($newLeft, $newRight); + + return true; + } + + /** + * inserts node as previous sibling of dest record + * + * @return bool + */ + public function insertAsPrevSiblingOf(Doctrine_Record $dest) + { + // cannot insert a node that has already has a place within the tree + if ($this->isValidNode()) { + return false; + } + + $newLeft = $dest->getNode()->getLeftValue(); + $newRight = $dest->getNode()->getLeftValue() + 1; + + $this->shiftRLValues($newLeft, 2); + $this->insertNode($newLeft, $newRight); + + // update destination left/right values to prevent a refresh + // $dest->getNode()->setLeftValue($dest->getNode()->getLeftValue() + 2); + // $dest->getNode()->setRightValue($dest->getNode()->getRightValue() + 2); + + return true; + } + + /** + * inserts node as next sibling of dest record + * + * @return bool + */ + public function insertAsNextSiblingOf(Doctrine_Record $dest) + { + // cannot insert a node that has already has a place within the tree + if ($this->isValidNode()) { + return false; + } + + $newLeft = $dest->getNode()->getRightValue() + 1; + $newRight = $dest->getNode()->getRightValue() + 2; + + $this->shiftRLValues($newLeft, 2); + $this->insertNode($newLeft, $newRight); + + // update destination left/right values to prevent a refresh + // no need, node not affected + + return true; + } + + /** + * inserts node as first child of dest record + * + * @return bool + */ + public function insertAsFirstChildOf(Doctrine_Record $dest) + { + // cannot insert a node that has already has a place within the tree + if ($this->isValidNode()) { + return false; + } + + $newLeft = $dest->getNode()->getLeftValue() + 1; + $newRight = $dest->getNode()->getLeftValue() + 2; + + $this->shiftRLValues($newLeft, 2); + $this->insertNode($newLeft, $newRight); + + // update destination left/right values to prevent a refresh + // $dest->getNode()->setRightValue($dest->getNode()->getRightValue() + 2); + + return true; + } + + /** + * inserts node as last child of dest record + * + * @return bool + */ + public function insertAsLastChildOf(Doctrine_Record $dest) + { + // cannot insert a node that has already has a place within the tree + if ($this->isValidNode()) { + return false; + } + + $newLeft = $dest->getNode()->getRightValue(); + $newRight = $dest->getNode()->getRightValue() + 1; + + $this->shiftRLValues($newLeft, 2); + $this->insertNode($newLeft, $newRight); + + // update destination left/right values to prevent a refresh + // $dest->getNode()->setRightValue($dest->getNode()->getRightValue() + 2); + + return true; + } + + /** + * moves node as prev sibling of dest record + * + */ + public function moveAsPrevSiblingOf(Doctrine_Record $dest) + { + $this->updateNode($dest->getNode()->getLeftValue()); + } + + /** + * moves node as next sibling of dest record + * + */ + public function moveAsNextSiblingOf(Doctrine_Record $dest) + { + $this->updateNode($dest->getNode()->getRightValue() + 1); + } + + /** + * moves node as first child of dest record + * + */ + public function moveAsFirstChildOf(Doctrine_Record $dest) + { + $this->updateNode($dest->getNode()->getLeftValue() + 1); + } + + /** + * moves node as last child of dest record + * + */ + public function moveAsLastChildOf(Doctrine_Record $dest) + { + $this->updateNode($dest->getNode()->getRightValue()); + } + + /** + * adds node as last child of record + * + */ + public function addChild(Doctrine_Record $record) + { + $record->getNode()->insertAsLastChildOf($this->getRecord()); + } + + /** + * determines if node is leaf + * + * @return bool + */ + public function isLeaf() + { + return (($this->getRightValue() - $this->getLeftValue()) == 1); + } + + /** + * determines if node is root + * + * @return bool + */ + public function isRoot() + { + return ($this->getLeftValue() == 1); + } + + /** + * determines if node is equal to subject node + * + * @return bool + */ + public function isEqualTo(Doctrine_Record $subj) + { + // TODO: break this out to reduce line length <120 (Zend) + return (($this->getLeftValue()==$subj->getNode()->getLeftValue()) and ($this->getRightValue()==$subj->getNode()->getRightValue())); + } + + /** + * determines if node is child of subject node + * + * @return bool + */ + public function isDescendantOf(Doctrine_Record $subj) + { + // TODO: break this out to reduce line length <120 (Zend) + return (($this->getLeftValue()>$subj->getNode()->getLeftValue()) and ($this->getRightValue()<$subj->getNode()->getRightValue())); + } + + /** + * determines if node is child of or sibling to subject node + * + * @return bool + */ + public function isDescendantOfOrEqualTo(Doctrine_Record $subj) + { + // TODO: break this out to reduce line length <120 (Zend) + return (($this->getLeftValue()>=$subj->getNode()->getLeftValue()) and ($this->getRightValue()<=$subj->getNode()->getRightValue())); + } + + /** + * determines if node is valid + * + * @return bool + */ + public function isValidNode() + { + return ($this->getRightValue() > $this->getLeftValue()); + } + + /** + * deletes node and it's descendants + * + */ + public function delete() + { + // TODO: add the setting whether or not to delete descendants or relocate children + + $q = $this->record->getTable()->createQuery(); + + $componentName = $this->record->getTable()->getComponentName(); + + $params = array($this->getLeftValue(), $this->getRightValue()); + $coll = $q->where("$componentName.lft >= ? AND $componentName.rgt <= ?", $params)->execute(); + $coll->delete(); + + $first = $this->getRightValue() + 1; + $delta = $this->getLeftValue() - $this->getRightValue() - 1; + $this->shiftRLValues($first, $delta); + + return true; + } + + /** + * sets node's left and right values and save's it + * + * @param int $destLeft node left value + * @param int $destRight node right value + */ + private function insertNode($destLeft = 0, $destRight = 0) + { + $this->setLeftValue($destLeft); + $this->setRightValue($destRight); + $this->record->save(); + } + + /** + * move node's and its children to location $destLeft and updates rest of tree + * + * @param int $destLeft destination left value + */ + private function updateNode($destLeft) + { + $left = $this->getLeftValue(); + $right = $this->getRightValue(); + + $treeSize = $right - $left + 1; + + $this->shiftRLValues($destLeft, $treeSize); + + if ($left >= $destLeft) { // src was shifted too? + $left += $treeSize; + $right += $treeSize; + } + + // now there's enough room next to target to move the subtree + $this->shiftRLRange($left, $right, $destLeft - $left); + + // correct values after source + $this->shiftRLValues($right + 1, -$treeSize); + + $this->record->save(); + $this->record->refresh(); + } + + /** + * adds '$delta' to all Left and Right values that are >= '$first'. '$delta' can also be negative. + * + * @param int $first first node to be shifted + * @param int $delta value to be shifted by, can be negative + */ + private function shiftRLValues($first, $delta) + { + $qLeft = $this->record->getTable()->createQuery(); + $qRight = $this->record->getTable()->createQuery(); + + // TODO: Wrap in transaction + + // shift left columns + $resultLeft = $qLeft->update($this->record->getTable()->getComponentName()) + ->set('lft', "lft + $delta") + ->where('lft >= ?', $first) + ->execute(); + + // shift right columns + $resultRight = $qRight->update($this->record->getTable()->getComponentName()) + ->set('rgt', "rgt + $delta") + ->where('rgt >= ?', $first) + ->execute(); + } + + /** + * adds '$delta' to all Left and Right values that are >= '$first' and <= '$last'. + * '$delta' can also be negative. + * + * @param int $first first node to be shifted (L value) + * @param int $last last node to be shifted (L value) + * @param int $delta value to be shifted by, can be negative + */ + private function shiftRLRange($first, $last, $delta) + { + $qLeft = $this->record->getTable()->createQuery(); + $qRight = $this->record->getTable()->createQuery(); + + // TODO : Wrap in transaction + + // shift left column values + $result = $qLeft->update($this->record->getTable()->getComponentName()) + ->set('lft', "lft + $delta") + ->where('lft >= ? AND lft <= ?', array($first, $last)) + ->execute(); + + // shift right column values + $result = $qRight->update($this->record->getTable()->getComponentName()) + ->set('rgt', "rgt + $delta") + ->where('rgt >= ? AND rgt <= ?', array($first, $last)) + ->execute(); + } + + /** + * gets record's left value + * + * @return int + */ + public function getLeftValue() + { + return $this->record->get('lft'); + } + + /** + * sets record's left value + * + * @param int + */ + public function setLeftValue($lft) + { + $this->record->set('lft', $lft); + } + + /** + * gets record's right value + * + * @return int + */ + public function getRightValue() + { + return $this->record->get('rgt'); + } + + /** + * sets record's right value + * + * @param int + */ + public function setRightValue($rgt) + { + $this->record->set('rgt', $rgt); + } + + /** + * gets level (depth) of node in the tree + * + * @return int + */ + public function getLevel() + { + if (!isset($this->level)) { + $q = $this->record->getTable()->createQuery(); + $coll = $q->where('lft < ? AND rgt > ?', array($this->getLeftValue(), $this->getRightValue())) + ->execute(); + $this->level = $coll->count() ? $coll->count() : 0; + } + + return $this->level; + } + + /** + * sets node's level + * + * @param int + */ + public function setLevel($level) + { + $this->level = $level; + } +} diff --git a/draft/Node/NestedSet/LevelOrderIterator.php b/draft/Node/NestedSet/LevelOrderIterator.php index 7f7b461f0..cadaec3e2 100644 --- a/draft/Node/NestedSet/LevelOrderIterator.php +++ b/draft/Node/NestedSet/LevelOrderIterator.php @@ -29,4 +29,5 @@ * @version $Revision$ * @author Joe Simms */ -class Doctrine_Node_NestedSet_LevelOrderIterator implements Iterator {} +class Doctrine_Node_NestedSet_LevelOrderIterator implements Iterator +{} diff --git a/draft/Node/NestedSet/PostOrderIterator.php b/draft/Node/NestedSet/PostOrderIterator.php index 77d38f206..12d9d8670 100644 --- a/draft/Node/NestedSet/PostOrderIterator.php +++ b/draft/Node/NestedSet/PostOrderIterator.php @@ -29,4 +29,5 @@ * @version $Revision$ * @author Joe Simms */ -class Doctrine_Node_NestedSet_PostOrderIterator implements Iterator {} +class Doctrine_Node_NestedSet_PostOrderIterator implements Iterator +{} diff --git a/draft/Node/NestedSet/PreOrderIterator.php b/draft/Node/NestedSet/PreOrderIterator.php index c0fb44992..71c8782c1 100644 --- a/draft/Node/NestedSet/PreOrderIterator.php +++ b/draft/Node/NestedSet/PreOrderIterator.php @@ -30,9 +30,9 @@ * @author Joe Simms */ class Doctrine_Node_NestedSet_PreOrderIterator implements Iterator -{ +{ /** - * @var Doctrine_Collection $collection + * @var Doctrine_Collection $collection */ protected $collection; /** @@ -59,34 +59,32 @@ class Doctrine_Node_NestedSet_PreOrderIterator implements Iterator * @var integer $count */ protected $count; - - public function __construct($record, $opts) { - $componentName = $record->getTable()->getComponentName(); - - $q = $record->getTable()->createQuery(); + public function __construct($record, $opts) + { + $componentName = $record->getTable()->getComponentName(); - if(isset($opts['include_record']) && $opts['include_record']) - { - $query = $q->where("$componentName.lft >= ? AND $componentName.rgt <= ?", array($record->get('lft'), $record->get('rgt')))->orderBy('lft asc'); - } - else - { - $query = $q->where("$componentName.lft > ? AND $componentName.rgt < ?", array($record->get('lft'), $record->get('rgt')))->orderBy('lft asc'); - } + $q = $record->getTable()->createQuery(); - $this->maxLevel = isset($opts['depth']) ? ($opts['depth'] + $record->getNode()->getLevel()) : 0; - $this->options = $opts; - $this->collection = isset($opts['collection']) ? $opts['collection'] : $query->execute(); - $this->keys = $this->collection->getKeys(); - $this->count = $this->collection->count(); - $this->index = -1; - $this->level = $record->getNode()->getLevel(); - $this->prevLeft = $record->getNode()->getLeftValue(); - - echo $this->maxDepth; - // clear the table identity cache - $record->getTable()->clear(); + $params = array($record->get('lft'), $record->get('rgt')); + if (isset($opts['include_record']) && $opts['include_record']) { + $query = $q->where("$componentName.lft >= ? AND $componentName.rgt <= ?", $params)->orderBy('lft asc'); + } else { + $query = $q->where("$componentName.lft > ? AND $componentName.rgt < ?", $params)->orderBy('lft asc'); + } + + $this->maxLevel = isset($opts['depth']) ? ($opts['depth'] + $record->getNode()->getLevel()) : 0; + $this->options = $opts; + $this->collection = isset($opts['collection']) ? $opts['collection'] : $query->execute(); + $this->keys = $this->collection->getKeys(); + $this->count = $this->collection->count(); + $this->index = -1; + $this->level = $record->getNode()->getLevel(); + $this->prevLeft = $record->getNode()->getLeftValue(); + + echo $this->maxDepth; + // clear the table identity cache + $record->getTable()->clear(); } /** @@ -94,7 +92,8 @@ class Doctrine_Node_NestedSet_PreOrderIterator implements Iterator * * @return void */ - public function rewind() { + public function rewind() + { $this->index = -1; $this->key = null; } @@ -104,66 +103,73 @@ class Doctrine_Node_NestedSet_PreOrderIterator implements Iterator * * @return integer */ - public function key() { + public function key() + { return $this->key; } + /** * returns the current record * * @return Doctrine_Record */ - public function current() { + public function current() + { $record = $this->collection->get($this->key); $record->getNode()->setLevel($this->level); return $record; } + /** * advances the internal pointer * * @return void */ - public function next() { - while($current = $this->advanceIndex()) - { - if($this->maxLevel && ($this->level > $this->maxLevel)) - continue; - - return $current; - } - - return false; + public function next() + { + while ($current = $this->advanceIndex()) { + if ($this->maxLevel && ($this->level > $this->maxLevel)) { + continue; + } + + return $current; + } + + return false; } /** - * @return boolean whether or not the iteration will continue + * @return boolean whether or not the iteration will continue */ - public function valid() { + public function valid() + { return ($this->index < $this->count); } - - public function count() { + + public function count() + { return $this->count; } - - private function updateLevel() { - if(!(isset($this->options['include_record']) && $this->options['include_record'] && $this->index == 0)) - { - $left = $this->collection->get($this->key)->getNode()->getLeftValue(); - $this->level += $this->prevLeft - $left + 2; - $this->prevLeft = $left; + + private function updateLevel() + { + if (!(isset($this->options['include_record']) && $this->options['include_record'] && $this->index == 0)) { + $left = $this->collection->get($this->key)->getNode()->getLeftValue(); + $this->level += $this->prevLeft - $left + 2; + $this->prevLeft = $left; } } - - private function advanceIndex() { + + private function advanceIndex() + { $this->index++; $i = $this->index; - if(isset($this->keys[$i])) - { + if (isset($this->keys[$i])) { $this->key = $this->keys[$i]; $this->updateLevel(); - return $this->current(); + return $this->current(); } - return false; - } + return false; + } } diff --git a/draft/Tree.php b/draft/Tree.php index 522978486..92b1be2bc 100644 --- a/draft/Tree.php +++ b/draft/Tree.php @@ -29,61 +29,63 @@ * @version $Revision$ * @author Joe Simms */ -class Doctrine_Tree +class Doctrine_Tree { - /** - * @param object $table reference to associated Doctrine_Table instance - */ - protected $table; - - /** - * @param array $options - */ - protected $options; + /** + * @param object $table reference to associated Doctrine_Table instance + */ + protected $table; - /** - * contructor, creates tree with reference to table and any options - * - * @param object $table instance of Doctrine_Table - * @param array $options options - */ - public function __construct($table, $options) - { - $this->table = $table; - $this->options = $options; - } + /** + * @param array $options + */ + protected $options; - /** - * Used to define table attributes required for the given implementation - * - */ - public function setTableDefinition() - { - throw new Doctrine_Tree_Exception('Table attributes have not been defined for this Tree implementation.'); - } + /** + * constructor, creates tree with reference to table and any options + * + * @param object $table instance of Doctrine_Table + * @param array $options options + */ + public function __construct($table, $options) + { + $this->table = $table; + $this->options = $options; + } - /** - * this method is used for setting up relations and attributes and should be used by specific implementations - * - */ - public function setUp() - { - - } + /** + * Used to define table attributes required for the given implementation + * + * @throws Doctrine_Tree_Exception if table attributes have not been defined + */ + public function setTableDefinition() + { + throw new Doctrine_Tree_Exception('Table attributes have not been defined for this Tree implementation.'); + } - /** - * factory method to return tree instance based upon chosen implementation - * - * @param object $table instance of Doctrine_Table - * @param string $impName implementation (NestedSet, AdjacencyList, MaterializedPath) - * @param array $options options - * @return object $options instance of Doctrine_Node - */ - public static function factory( &$table, $implName, $options = array()) - { - $class = 'Doctrine_Tree_'.$implName; - if(!class_exists($class)) - throw new Doctrine_Exception('The chosen class must extend Doctrine_Tree'); - return new $class($table, $options); - } -} // END class \ No newline at end of file + /** + * this method is used for setting up relations and attributes and should be used by specific implementations + * + */ + public function setUp() + { + } + + /** + * factory method to return tree instance based upon chosen implementation + * + * @param object $table instance of Doctrine_Table + * @param string $impName implementation (NestedSet, AdjacencyList, MaterializedPath) + * @param array $options options + * @return object $options instance of Doctrine_Node + * @throws Doctrine_Exception if class does not extend Doctrine_Tree + */ + public static function factory(&$table, $implName, $options = array()) + { + $class = 'Doctrine_Tree_' . $implName; + if (!class_exists($class)) { + throw new Doctrine_Exception('The chosen class must extend Doctrine_Tree'); + } + return new $class($table, $options); + } +} diff --git a/draft/Tree/AdjacencyList.php b/draft/Tree/AdjacencyList.php index 0c7505ec0..9ddefbe71 100644 --- a/draft/Tree/AdjacencyList.php +++ b/draft/Tree/AdjacencyList.php @@ -29,4 +29,5 @@ * @version $Revision$ * @author Joe Simms */ -class Doctrine_Tree_AdjacencyList extends Doctrine_Tree implements Doctrine_Tree_Interface { } +class Doctrine_Tree_AdjacencyList extends Doctrine_Tree implements Doctrine_Tree_Interface +{} diff --git a/draft/Tree/Exception.php b/draft/Tree/Exception.php index 7cb11c8ab..423d0df0c 100644 --- a/draft/Tree/Exception.php +++ b/draft/Tree/Exception.php @@ -29,4 +29,5 @@ * @version $Revision$ * @author Konsta Vesterinen */ -class Doctrine_Tree_Exception extends Doctrine_Exception { } +class Doctrine_Tree_Exception extends Doctrine_Exception +{} diff --git a/draft/Tree/Interface.php b/draft/Tree/Interface.php index 3f3f174ee..d97f2f8df 100644 --- a/draft/Tree/Interface.php +++ b/draft/Tree/Interface.php @@ -31,34 +31,34 @@ */ interface Doctrine_Tree_Interface { - /** - * creates root node from given record or from a new record - * - * @param object $record instance of Doctrine_Record - */ - public function createRoot(Doctrine_Record $record = null); + /** + * creates root node from given record or from a new record + * + * @param object $record instance of Doctrine_Record + */ + public function createRoot(Doctrine_Record $record = null); - /** - * returns root node - * - * @return object $record instance of Doctrine_Record - */ - public function findRoot(); + /** + * returns root node + * + * @return object $record instance of Doctrine_Record + */ + public function findRoot(); - /** - * optimised method to returns iterator for traversal of the entire tree from root - * - * @param array $options options - * @return object $iterator instance of Doctrine_Node__PreOrderIterator - */ - public function fetchTree($options = array()); + /** + * optimised method to returns iterator for traversal of the entire tree from root + * + * @param array $options options + * @return object $iterator instance of Doctrine_Node__PreOrderIterator + */ + public function fetchTree($options = array()); - /** - * optimised method that returns iterator for traversal of the tree from the given record primary key - * - * @param mixed $pk primary key as used by table::find() to locate node to traverse tree from - * @param array $options options - * @return iterator instance of Doctrine_Node__PreOrderIterator - */ - public function fetchBranch($pk, $options = array()); -} \ No newline at end of file + /** + * optimised method that returns iterator for traversal of the tree from the given record primary key + * + * @param mixed $pk primary key as used by table::find() to locate node to traverse tree from + * @param array $options options + * @return iterator instance of Doctrine_Node__PreOrderIterator + */ + public function fetchBranch($pk, $options = array()); +} diff --git a/draft/Tree/MaterializedPath.php b/draft/Tree/MaterializedPath.php index 9e5460687..dd0609b16 100644 --- a/draft/Tree/MaterializedPath.php +++ b/draft/Tree/MaterializedPath.php @@ -29,4 +29,5 @@ * @version $Revision$ * @author Joe Simms */ -class Doctrine_Tree_MaterializedPath extends Doctrine_Tree implements Doctrine_Tree_Interface { } \ No newline at end of file +class Doctrine_Tree_MaterializedPath extends Doctrine_Tree implements Doctrine_Tree_Interface +{} diff --git a/draft/Tree/NestedSet.php b/draft/Tree/NestedSet.php index 32013409b..e9e62506d 100644 --- a/draft/Tree/NestedSet.php +++ b/draft/Tree/NestedSet.php @@ -31,113 +31,114 @@ */ class Doctrine_Tree_NestedSet extends Doctrine_Tree implements Doctrine_Tree_Interface { - /** - * used to define table attributes required for the NestetSet implementation - * adds lft and rgt columns for corresponding left and right values - * - */ - public function setTableDefinition() - { - $this->table->setColumn("lft","integer",11); - $this->table->setColumn("rgt","integer",11); - } - - /** - * creates root node from given record or from a new record - * - * @param object $record instance of Doctrine_Record - */ - public function createRoot(Doctrine_Record $record = null) - { - if(!$record) + /** + * used to define table attributes required for the NestetSet implementation + * adds lft and rgt columns for corresponding left and right values + * + */ + public function setTableDefinition() { - $record = $this->table->create(); - } - - $record->set('lft', '1'); - $record->set('rgt', '2'); - $record->save(); - - return $record; - } - - /** - * returns root node - * - * @return object $record instance of Doctrine_Record - */ - public function findRoot() - { - $q = $this->table->createQuery(); - $root = $q->where('lft = ?', 1) - ->execute()->getFirst(); - - // if no record is returned, create record - if(!$root) - { - $root = $this->table->create(); + $this->table->setColumn("lft","integer",11); + $this->table->setColumn("rgt","integer",11); } - // set level to prevent additional query to determine level - $root->getNode()->setLevel(0); - - return $root; - } - /** - * optimised method to returns iterator for traversal of the entire tree from root - * - * @param array $options options - * @return object $iterator instance of Doctrine_Node_NestedSet_PreOrderIterator - */ - public function fetchTree($options = array()) - { - // fetch tree - $q = $this->table->createQuery(); - - $tree = $q->where('lft >= ?', 1) - ->orderBy('lft asc') - ->execute(); - - $root = $tree->getFirst(); - - // if no record is returned, create record - if(!$root) + /** + * creates root node from given record or from a new record + * + * @param object $record instance of Doctrine_Record + */ + public function createRoot(Doctrine_Record $record = null) { - $root = $this->table->create(); + if (!$record) { + $record = $this->table->create(); + } + + $record->set('lft', '1'); + $record->set('rgt', '2'); + $record->save(); + + return $record; } - if($root->exists()) + /** + * returns root node + * + * @return object $record instance of Doctrine_Record + */ + public function findRoot() { - // set level to prevent additional query - $root->getNode()->setLevel(0); + $q = $this->table->createQuery(); + $root = $q->where('lft = ?', 1) + ->execute()->getFirst(); - // default to include root node - $options = array_merge(array('include_record'=>true), $options); - - // remove root node from collection if not required - if($options['include_record'] == false) - $tree->remove(0); + // if no record is returned, create record + if (!$root) { + $root = $this->table->create(); + } - // set collection for iterator - $options['collection'] = $tree; + // set level to prevent additional query to determine level + $root->getNode()->setLevel(0); - return $root->getNode()->traverse('Pre', $options); + return $root; } - } - /** - * optimised method that returns iterator for traversal of the tree from the given record primary key - * - * @param mixed $pk primary key as used by table::find() to locate node to traverse tree from - * @param array $options options - * @return iterator instance of Doctrine_Node__PreOrderIterator - */ - public function fetchBranch($pk, $options = array()) - { - $record = $this->table->find($pk); - if($record->exists()) + + /** + * optimised method to returns iterator for traversal of the entire tree from root + * + * @param array $options options + * @return object $iterator instance of Doctrine_Node_NestedSet_PreOrderIterator + */ + public function fetchTree($options = array()) { - $options = array_merge(array('include_record'=>true), $options); - return $record->getNode()->traverse('Pre', $options); + // fetch tree + $q = $this->table->createQuery(); + + $tree = $q->where('lft >= ?', 1) + ->orderBy('lft asc') + ->execute(); + + $root = $tree->getFirst(); + + // if no record is returned, create record + if (!$root) { + $root = $this->table->create(); + } + + if ($root->exists()) { + // set level to prevent additional query + $root->getNode()->setLevel(0); + + // default to include root node + $options = array_merge(array('include_record'=>true), $options); + + // remove root node from collection if not required + if($options['include_record'] == false) + $tree->remove(0); + + // set collection for iterator + $options['collection'] = $tree; + + return $root->getNode()->traverse('Pre', $options); + } + + // TODO: no default return value or exception thrown? } - } -} \ No newline at end of file + + /** + * optimised method that returns iterator for traversal of the tree from the given record primary key + * + * @param mixed $pk primary key as used by table::find() to locate node to traverse tree from + * @param array $options options + * @return iterator instance of Doctrine_Node__PreOrderIterator + */ + public function fetchBranch($pk, $options = array()) + { + $record = $this->table->find($pk); + if ($record->exists()) { + $options = array_merge(array('include_record'=>true), $options); + return $record->getNode()->traverse('Pre', $options); + } + + // TODO: if record doesn't exist, throw exception or similar? + } +}