[Beeframework-svn] SF.net SVN: beeframework:[42] trunk
Brought to you by:
b_hartmann,
m_plomer
From: <m_p...@us...> - 2013-06-28 14:49:47
|
Revision: 42 http://sourceforge.net/p/beeframework/code/42 Author: m_plomer Date: 2013-06-28 14:49:43 +0000 (Fri, 28 Jun 2013) Log Message: ----------- - Tree-based API of nested set behavior completed Modified Paths: -------------- trunk/examples/classes/Treetest/Node.php trunk/examples/classes/Treetest/TreeDao.php trunk/examples/index2.php trunk/framework/Bee/Persistence/Behaviors/NestedSet/IDelegate.php trunk/framework/Bee/Persistence/Behaviors/NestedSet/ITreeNode.php trunk/framework/Bee/Persistence/Behaviors/NestedSet/NodeInfo.php trunk/framework/Bee/Persistence/Behaviors/NestedSet/Strategy.php trunk/framework/Bee/Persistence/Behaviors/NestedSet/TreeStrategy.php trunk/framework/Bee/Persistence/Doctrine2/Behaviors/DelegateBase.php trunk/framework/Bee/Persistence/Doctrine2/Behaviors/GenericNestedSetDelegate.php trunk/framework/Bee/Persistence/Doctrine2/Log4PHPLogger.php trunk/framework/Bee/Persistence/Pdo/Behaviors/DelegateBase.php trunk/framework/Bee/Persistence/Pdo/Behaviors/GenericNestedSetDelegate.php trunk/tests/Bee/Persistence/Behaviors/NestedSet/DelegateMock.php trunk/tests/Bee/Persistence/Behaviors/NestedSet/TestTreeNode.php Modified: trunk/examples/classes/Treetest/Node.php =================================================================== --- trunk/examples/classes/Treetest/Node.php 2013-06-27 03:18:25 UTC (rev 41) +++ trunk/examples/classes/Treetest/Node.php 2013-06-28 14:49:43 UTC (rev 42) @@ -25,11 +25,7 @@ * * @Entity * @Table( - * name="tree_test", - * uniqueConstraints={ - * @UniqueConstraint(name="grp_lft", columns={"root_id", "lft"}), - * @UniqueConstraint(name="grp_rgt", columns={"root_id", "rgt"}) - * } + * name="tree_test" * ) */ class Node implements ITreeNode { @@ -50,7 +46,7 @@ /** * @var int - * @Column(name="root_id", type="integer", nullable=false) + * @Column(name="root_id", type="integer", nullable=true) */ private $rootId; @@ -84,9 +80,11 @@ /** * @param $name string + * @param null $rootId */ - public function __construct($name) { + public function __construct($name, $rootId = null) { $this->name = $name; + $this->rootId = $rootId; } /** @@ -166,18 +164,18 @@ return $this->rootId; } - /** - * @return ITreeNode + * @return ITreeNode[] */ - public function getParent() { - return $this->parent; + public function getChildren() { + return $this->children; } /** - * @return ITreeNode[] + * @param ITreeNode $child + * @return void */ - public function &getChildren() { - return $this->children; + public function appendChild(ITreeNode $child) { + array_push($this->children, $child); } } Modified: trunk/examples/classes/Treetest/TreeDao.php =================================================================== --- trunk/examples/classes/Treetest/TreeDao.php 2013-06-27 03:18:25 UTC (rev 41) +++ trunk/examples/classes/Treetest/TreeDao.php 2013-06-28 14:49:43 UTC (rev 42) @@ -28,15 +28,22 @@ class TreeDao extends DaoBase { + const ENTITY_CLASS_NAME = 'Treetest\Node'; + /** * @var NestedSetStrategy */ private $nestedSetStrategy; + /** + * @var GenericNestedSetDelegate + */ + private $nestedSetDelegate; + public function __construct(EntityManager $entityManager) { - $delagate = new GenericNestedSetDelegate($entityManager, 'Treetest\Node'); -// $delagate->setGroupFieldName('root_id'); - $this->nestedSetStrategy = new NestedSetStrategy($delagate); + $this->setEntityManager($entityManager); + $this->nestedSetDelegate = new GenericNestedSetDelegate($entityManager, self::ENTITY_CLASS_NAME); + $this->nestedSetStrategy = new NestedSetStrategy($this->nestedSetDelegate); } /** @@ -45,4 +52,22 @@ public function getNestedSetStrategy() { return $this->nestedSetStrategy; } + + public function loadTree($rootNodeIdOrEntity) { + if(!$rootNodeIdOrEntity instanceof Node) { + $rootNodeIdOrEntity = $this->getEntityManager()->find(self::ENTITY_CLASS_NAME, $rootNodeIdOrEntity); + } + + // obtain NodeInfo (left / right boundaries + group info) for the tree part rooted at given node + $rootNodeInfo = $this->nestedSetDelegate->getNodeInfo($rootNodeIdOrEntity); + + // construct our base query + $qb = $this->getEntityManager()->createQueryBuilder()->select('e')->from(self::ENTITY_CLASS_NAME, 'e'); + + // augment query with subtree limits + $this->nestedSetDelegate->augmentQueryWithSubtreeLimits($qb, $rootNodeInfo); + + // execute query and create tree structure from result + return $this->nestedSetStrategy->buildTreeStructure($qb->getQuery()->execute()); + } } Modified: trunk/examples/index2.php =================================================================== --- trunk/examples/index2.php 2013-06-27 03:18:25 UTC (rev 41) +++ trunk/examples/index2.php 2013-06-28 14:49:43 UTC (rev 42) @@ -22,54 +22,62 @@ */ use Treetest\Node; +use Bee\Persistence\Behaviors\NestedSet\ITreeNode; require_once('bootstrap.php'); -function addChild(&$children, Node $node) { +function addChild(ITreeNode $parent, Node $node) { global $entityManager; $entityManager->persist($node); - array_push($children, $node); + $parent->appendChild($node); } -$root = new Node('Root Node'); +$treeDao = $ctx->getBean('treeDao', 'Treetest\TreeDao'); + +$root = new Node('Root Node', 15); $entityManager->persist($root); -addChild($root->getChildren(), new Node('Child 1-1')); -addChild($root->getChildren(), new Node('Child 1-2')); +addChild($root, new Node('Child 1-1')); +addChild($root, new Node('Child 1-2')); $child13 = new Node('Child 1-3'); -addChild($root->getChildren(), $child13); +addChild($root, $child13); -addChild($child13->getChildren(), new Node('Child 1-3-1')); -addChild($child13->getChildren(), new Node('Child 1-3-2')); +addChild($child13, new Node('Child 1-3-1')); +addChild($child13, new Node('Child 1-3-2')); -$treeDao = $ctx->getBean('treeDao', 'Treetest\TreeDao'); - $treeDao->getNestedSetStrategy()->saveStructure($root); $entityManager->flush(); $entityManager->close(); +//exit(); + + // new EM instance $entityManager = $ctx->getBean('entityManager'); $c12 = $entityManager->getRepository('Treetest\Node')->findOneBy(array('name' => 'Child 1-2')); -addChild($c12->getChildren(), new Node('Child 1-2-1')); -addChild($c12->getChildren(), new Node('Child 1-2-2')); +addChild($c12, new Node('Child 1-2-1')); +addChild($c12, new Node('Child 1-2-2')); $treeDao->getNestedSetStrategy()->saveStructure($c12); $entityManager->flush(); $entityManager->close(); +//exit(); + // new EM instance $entityManager = $ctx->getBean('entityManager'); +//$entityManager = new \Doctrine\ORM\EntityManager(); + $c12 = $entityManager->getRepository('Treetest\Node')->findOneBy(array('name' => 'Child 1-2')); -//array_pop($c12->getChildren()); +$treeDao->getNestedSetStrategy()->saveStructure($c12); -$treeDao->getNestedSetStrategy()->saveStructure($c12); $entityManager->flush(); $entityManager->close(); +var_dump($treeDao->loadTree($root)); Modified: trunk/framework/Bee/Persistence/Behaviors/NestedSet/IDelegate.php =================================================================== --- trunk/framework/Bee/Persistence/Behaviors/NestedSet/IDelegate.php 2013-06-27 03:18:25 UTC (rev 41) +++ trunk/framework/Bee/Persistence/Behaviors/NestedSet/IDelegate.php 2013-06-28 14:49:43 UTC (rev 42) @@ -44,17 +44,23 @@ * @param NodeInfo $nodeInfo * @param bool|int $newLft * @param bool|int $newLvl - * @param mixed $restriction - * @return + * @return void */ - public function setPosition($nestedSetEntity, NodeInfo $nodeInfo, $newLft = false, $newLvl = false, $restriction = false); + public function setPosition($nestedSetEntity, NodeInfo $nodeInfo, $newLft = false, $newLvl = false); /** + * @param NodeInfo $parentNodeInfo + * @return void + */ + public function unsetChildGroupKeys(NodeInfo $parentNodeInfo); + + /** * @param mixed $nestedSetEntity * @param int $delta * @param int $lowerBoundIncl * @param int $upperBoundExcl - * @param mixed $restriction + * @param array $groupKey + * @return void */ - public function shift($nestedSetEntity, $delta, $lowerBoundIncl, $upperBoundExcl, $restriction = false); + public function shift($nestedSetEntity, $delta, $lowerBoundIncl, $upperBoundExcl, array $groupKey); } Modified: trunk/framework/Bee/Persistence/Behaviors/NestedSet/ITreeNode.php =================================================================== --- trunk/framework/Bee/Persistence/Behaviors/NestedSet/ITreeNode.php 2013-06-27 03:18:25 UTC (rev 41) +++ trunk/framework/Bee/Persistence/Behaviors/NestedSet/ITreeNode.php 2013-06-28 14:49:43 UTC (rev 42) @@ -25,12 +25,13 @@ interface ITreeNode { /** - * @return ITreeNode + * @return ITreeNode[] */ - public function getParent(); + public function getChildren(); /** - * @return ITreeNode[] + * @param ITreeNode $child + * @return void */ - public function getChildren(); + public function appendChild(ITreeNode $child); } Modified: trunk/framework/Bee/Persistence/Behaviors/NestedSet/NodeInfo.php =================================================================== --- trunk/framework/Bee/Persistence/Behaviors/NestedSet/NodeInfo.php 2013-06-27 03:18:25 UTC (rev 41) +++ trunk/framework/Bee/Persistence/Behaviors/NestedSet/NodeInfo.php 2013-06-28 14:49:43 UTC (rev 42) @@ -21,7 +21,6 @@ * Date: 07.05.13 * Time: 17:43 */ - class NodeInfo { const LEFT_KEY = 'lft'; @@ -43,6 +42,11 @@ */ public $lvl; + /** + * @var array + */ + public $groupKey; + public function __construct(array $tuple = null) { if(!is_null($tuple)) { $this->lft = is_numeric($tuple[self::LEFT_KEY]) ? $tuple[self::LEFT_KEY] : false; @@ -97,9 +101,21 @@ return $this->rgt > 0 && $this->lft > 0; } - function __toString() { - return "NodeInfo(lft:{$this->lft}|rgt:{$this->rgt}|lvl:{$this->lvl})"; + /** + * @return array + */ + public function getGroupKey() { + return $this->groupKey; } + /** + * @param array $groupKey + */ + public function setGroupKey($groupKey) { + $this->groupKey = $groupKey; + } + function __toString() { + return "NodeInfo(lft:{$this->lft}|rgt:{$this->rgt}|lvl:{$this->lvl}|group:[".implode(',', $this->groupKey)."])"; + } } Modified: trunk/framework/Bee/Persistence/Behaviors/NestedSet/Strategy.php =================================================================== --- trunk/framework/Bee/Persistence/Behaviors/NestedSet/Strategy.php 2013-06-27 03:18:25 UTC (rev 41) +++ trunk/framework/Bee/Persistence/Behaviors/NestedSet/Strategy.php 2013-06-28 14:49:43 UTC (rev 42) @@ -178,7 +178,7 @@ if ($subjectInfo->isInTree()) { // ... temporarily move it to a neutral position, so as to avoid any conflicts (e.g. SQL constraints) // (keep its original level for now) - $this->delegate->setPosition($subject, $subjectInfo, -$subjectInfo->getSpan(), $subjectInfo->lvl, $groupRestriction); + $this->delegate->setPosition($subject, $subjectInfo, -$subjectInfo->getSpan(), $subjectInfo->lvl); $subjectInfo->update(-$subjectInfo->getSpan(), $subjectInfo->lvl); } @@ -189,7 +189,7 @@ self::getLog()->debug("setting final position of subject to lft = $newLeft, lvl = $level"); // move subject to final position - $this->delegate->setPosition($subject, $subjectInfo, $newLeft, $level, $groupRestriction); + $this->delegate->setPosition($subject, $subjectInfo, $newLeft, $level); $subjectInfo->update($newLeft, $level); } @@ -210,10 +210,10 @@ } // store the subtree in the negative area (in case we do not want to delete it, but rather move it to a different tree) - $this->delegate->setPosition($subject, $subjectInfo, -$subjectInfo->getSpan(), 0, $groupRestriction); + $this->delegate->setPosition($subject, $subjectInfo, -$subjectInfo->getSpan(), 0); // restore numbering consistency - $this->delegate->shift($subject, -$subjectInfo->getSpan(), $subjectInfo->rgt + 1, false, $groupRestriction); + $this->delegate->shift($subject, -$subjectInfo->getSpan(), $subjectInfo->rgt + 1, false, $subjectInfo->getGroupKey()); } /** Modified: trunk/framework/Bee/Persistence/Behaviors/NestedSet/TreeStrategy.php =================================================================== --- trunk/framework/Bee/Persistence/Behaviors/NestedSet/TreeStrategy.php 2013-06-27 03:18:25 UTC (rev 41) +++ trunk/framework/Bee/Persistence/Behaviors/NestedSet/TreeStrategy.php 2013-06-28 14:49:43 UTC (rev 42) @@ -44,12 +44,13 @@ public function saveStructure(ITreeNode $structureRoot) { $rootNodeInfo = $this->nodeInfoCache->contains($structureRoot) ? $this->nodeInfoCache->offsetGet($structureRoot) : $this->delegate->getNodeInfo($structureRoot); + $this->delegate->unsetChildGroupKeys($rootNodeInfo); $oldNext = $rootNodeInfo->rgt + 1; - $next = $this->calculateNodeInfo($structureRoot, $rootNodeInfo->lft, $rootNodeInfo->lvl); + $next = $this->calculateNodeInfo($structureRoot, $rootNodeInfo->lft, $rootNodeInfo->lvl, $rootNodeInfo->getGroupKey()); $delta = $next - $oldNext; - $this->delegate->shift($structureRoot, $delta, $oldNext, false); + $this->delegate->shift($structureRoot, $delta, $oldNext, false, $rootNodeInfo->getGroupKey()); $myDelegate = $this->delegate; $this->walkTree($structureRoot, function(ITreeNode $currentNode, NodeInfo $nodeInfo) use ($myDelegate) { @@ -57,6 +58,11 @@ }); } + /** + * Walk the tree under $structureRoot iteratively in preorder and apply the given lambda $func to each node. + * @param ITreeNode $structureRoot + * @param $func + */ protected function walkTree(ITreeNode $structureRoot, $func) { $stack = array($structureRoot); while(count($stack) > 0) { @@ -72,9 +78,10 @@ * @param ITreeNode $currentNode * @param $lft * @param $lvl + * @param array $groupKey * @return mixed */ - protected function calculateNodeInfo(ITreeNode $currentNode, $lft, $lvl) { + protected function calculateNodeInfo(ITreeNode $currentNode, $lft, $lvl, array $groupKey) { $nodeInfo = new NodeInfo(); $this->nodeInfoCache->attach($currentNode, $nodeInfo); @@ -82,9 +89,10 @@ $nodeInfo->lft = $lft; $lft++; foreach($currentNode->getChildren() as $child) { - $lft = $this->calculateNodeInfo($child, $lft, $lvl + 1); + $lft = $this->calculateNodeInfo($child, $lft, $lvl + 1, $groupKey); } $nodeInfo->rgt = $lft; + $nodeInfo->setGroupKey($groupKey); return $nodeInfo->rgt + 1; } @@ -95,4 +103,46 @@ protected function getNodeInfoCache() { return $this->nodeInfoCache; } + + /** + * @param ITreeNode[] $nodesList + * @return ITreeNode + */ + public function buildTreeStructure(array $nodesList) { + $nodeStack = new \SplStack(); + + $lastNode = null; + $lastLevel = false; + + foreach ($nodesList as $node) { + $level = $this->delegate->getNodeInfo($node)->lvl; + + if ($lastLevel !== false) { + if ($level > $lastLevel) { + // dive exactly one level + // must be exactly 1 larger than last level (otherwise intermediate nodes are probably missing) + \Bee_Utils_Assert::isTrue($level == $lastLevel + 1, sprintf('Malformed nodes list, missing intermediate levels between %d and %d', $lastLevel, $level)); + // use last node as current parent + $nodeStack->push($lastNode); + } else { + // $lastLevel >= $level; emerge one or multiple levels + for ($i = $lastLevel; $i > $level; $i--) { + $nodeStack->pop(); + } + } + + // add to current parent (which must exist!!) + \Bee_Utils_Assert::isTrue(!$nodeStack->isEmpty(), sprintf('No current parent on level %d (if this happens on level 0, it usually means that your query returned multiple roots for this tree set)', $level)); + + $nodeStack->top()->appendChild($node); + } + + $lastLevel = $level; + $lastNode = $node; + } + + return $nodeStack->bottom(); + } + + } Modified: trunk/framework/Bee/Persistence/Doctrine2/Behaviors/DelegateBase.php =================================================================== --- trunk/framework/Bee/Persistence/Doctrine2/Behaviors/DelegateBase.php 2013-06-27 03:18:25 UTC (rev 41) +++ trunk/framework/Bee/Persistence/Doctrine2/Behaviors/DelegateBase.php 2013-06-28 14:49:43 UTC (rev 42) @@ -31,11 +31,18 @@ private $entityName; /** + * @var array + */ + private $groupKeyFields; + + /** * @param string $entityName + * @param array $groupKeyFields */ - public function __construct($entityName) { + public function __construct($entityName, array $groupKeyFields) { \Bee_Utils_Assert::hasText($entityName, 'Entity name required, must not be empty'); $this->entityName = $entityName; + $this->groupKeyFields = $groupKeyFields; } /** @@ -44,4 +51,11 @@ public function getEntityName() { return $this->entityName; } + + /** + * @return array + */ + public function getGroupKeyFields() { + return $this->groupKeyFields; + } } Modified: trunk/framework/Bee/Persistence/Doctrine2/Behaviors/GenericNestedSetDelegate.php =================================================================== --- trunk/framework/Bee/Persistence/Doctrine2/Behaviors/GenericNestedSetDelegate.php 2013-06-27 03:18:25 UTC (rev 41) +++ trunk/framework/Bee/Persistence/Doctrine2/Behaviors/GenericNestedSetDelegate.php 2013-06-28 14:49:43 UTC (rev 42) @@ -16,6 +16,7 @@ * limitations under the License. */ use Bee\Persistence\Behaviors\NestedSet\IDelegate; +use Bee\Persistence\Behaviors\NestedSet\ITreeNode; use Bee\Persistence\Behaviors\NestedSet\NodeInfo; use Doctrine\ORM\EntityManager; use Doctrine\ORM\QueryBuilder; @@ -25,7 +26,7 @@ * Date: 21.06.13 * Time: 16:01 */ - + class GenericNestedSetDelegate extends DelegateBase implements IDelegate { /** @@ -46,13 +47,13 @@ /** * @param EntityManager $entityManager * @param string $entityName + * @param array $rootKeyFields */ - public function __construct(EntityManager $entityManager, $entityName) { - parent::__construct($entityName); + public function __construct(EntityManager $entityManager, $entityName, array $rootKeyFields = array('rootId')) { + parent::__construct($entityName, $rootKeyFields); $this->setEntityManager($entityManager); } - /** * @param string $leftFieldName */ @@ -85,14 +86,30 @@ $result->lft = $bw->getPropertyValue($this->leftFieldName); $result->rgt = $bw->getPropertyValue($this->rightFieldName); $result->lvl = $bw->getPropertyValue($this->levelFieldName); - if(is_null($result->lft)) { + if (is_null($result->lft)) { $result->lft = 1; $result->lvl = 0; } + $result->setGroupKey($this->extractGroupKey($bw)); return $result; } /** + * @param mixed|\Bee_Beans_BeanWrapper $nestedSetEntityOrBeanWrapper + * @return array + */ + protected function extractGroupKey($nestedSetEntityOrBeanWrapper) { + if (!($nestedSetEntityOrBeanWrapper instanceof \Bee_Beans_BeanWrapper)) { + $nestedSetEntityOrBeanWrapper = new \Bee_Beans_BeanWrapper($nestedSetEntityOrBeanWrapper); + } + $groupKey = array(); + foreach ($this->getGroupKeyFields() as $groupFieldName) { + $groupKey[$groupFieldName] = $nestedSetEntityOrBeanWrapper->getPropertyValue($groupFieldName); + } + return $groupKey; + } + + /** * @param mixed $nestedSetEntity * @param mixed $restriction * @return NodeInfo @@ -106,41 +123,106 @@ * @param NodeInfo $nodeInfo * @param bool|int $newLft * @param bool|int $newLvl - * @param mixed $restriction */ - public function setPosition($nestedSetEntity, NodeInfo $nodeInfo, $newLft = false, $newLvl = false, $restriction = false) { + public function setPosition($nestedSetEntity, NodeInfo $nodeInfo, $newLft = false, $newLvl = false) { $bw = new \Bee_Beans_BeanWrapper($nestedSetEntity); $bw->setPropertyValue($this->leftFieldName, $nodeInfo->lft); $bw->setPropertyValue($this->rightFieldName, $nodeInfo->rgt); $bw->setPropertyValue($this->levelFieldName, $nodeInfo->lvl); + + $grpKey = $nodeInfo->getGroupKey(); + foreach ($this->getGroupKeyFields() as $groupKeyField) { + $bw->setPropertyValue($groupKeyField, $grpKey[$groupKeyField]); + } // todo: implement the other cases (i.e. for the non-tree strategy API) } /** + * @param NodeInfo $parentNodeInfo + * @return void + */ + public function unsetChildGroupKeys(NodeInfo $parentNodeInfo) { + $qb = $this->createUpdateBaseQueryBuilder($parentNodeInfo->getGroupKey()); + + // set group key to null... + foreach ($this->getGroupKeyFields() as $groupKeyField) { + $qb->set("e.$groupKeyField", 'NULL'); + } + + // .. on all children of the parent node given by the NodeInfo + $qb->andWhere("e.{$this->leftFieldName} > :parentLft") + ->setParameter('parentLft', $parentNodeInfo->lft) + ->andWhere("e.{$this->rightFieldName} < :parentRgt") + ->setParameter('parentRgt', $parentNodeInfo->rgt); + $qb->getQuery()->execute(); + } + + /** * @param mixed $nestedSetEntity * @param int $delta * @param int $lowerBoundIncl * @param int $upperBoundExcl - * @param mixed $restriction + * @param array $groupKey */ - public function shift($nestedSetEntity, $delta, $lowerBoundIncl, $upperBoundExcl, $restriction = false) { - $this->buildShiftQuery($this->leftFieldName, $delta, $lowerBoundIncl, $upperBoundExcl, $restriction)->execute(); - $this->buildShiftQuery($this->rightFieldName, $delta, $lowerBoundIncl, $upperBoundExcl, $restriction)->execute(); + public function shift($nestedSetEntity, $delta, $lowerBoundIncl, $upperBoundExcl, array $groupKey) { + $this->buildShiftQuery($this->leftFieldName, $delta, $lowerBoundIncl, $upperBoundExcl, $groupKey)->execute(); + $this->buildShiftQuery($this->rightFieldName, $delta, $lowerBoundIncl, $upperBoundExcl, $groupKey)->execute(); // todo: implement the other cases (i.e. for the non-tree strategy API) } /** + * @param QueryBuilder $qb + * @param NodeInfo $rootNodeInfo + * @param string $rootEntityAlias + * @param bool $maxLvl + */ + public function augmentQueryWithSubtreeLimits(QueryBuilder $qb, NodeInfo $rootNodeInfo, $rootEntityAlias = 'e', $maxLvl = false) { + if ($rootEntityAlias) { + $rootEntityAlias = $rootEntityAlias . '.'; + } + + // limit to subtree of this root node + $qb->andWhere("$rootEntityAlias{$this->leftFieldName} >= :limitLft") + ->setParameter('limitLft', $rootNodeInfo->lft) + ->andWhere("$rootEntityAlias{$this->rightFieldName} <= :limitRgt") + ->setParameter('limitRgt', $rootNodeInfo->rgt); + + // make sure we get only results from current group + $this->augmentQueryWithGroupLimits($qb, $rootNodeInfo->getGroupKey()); + + // apply max level restriction if needed + if ($maxLvl) { + $qb->andWhere("$rootEntityAlias{$this->leftFieldName} <= :maxLvl")->setParameter('maxLvl', $maxLvl); + } + + // proper ordering + $qb->orderBy("$rootEntityAlias{$this->leftFieldName}", 'ASC'); + } + + /** * @param string $fieldName * @param int $delta * @param int $lowerBoundIncl * @param int $upperBoundExcl - * @param mixed $restriction + * @param array $groupKey * @return \Doctrine\ORM\Query */ - protected function buildShiftQuery($fieldName, $delta, $lowerBoundIncl, $upperBoundExcl, $restriction = false) { - return $this->getEntityManager()->createQueryBuilder()->update($this->getEntityName(), 'e') - ->set('e.'.$fieldName, 'e.'.$fieldName .' + :delta')->setParameter('delta', $delta) - ->where('e.'.$fieldName . ' >= :lbIncl')->setParameter('lbIncl', $lowerBoundIncl)->getQuery(); -// ->orderBy('e.'.$fieldName, $delta > 0 ? 'DESC' : 'ASC')->getQuery(); + protected function buildShiftQuery($fieldName, $delta, $lowerBoundIncl, $upperBoundExcl, array $groupKey) { + $qb = $this->createUpdateBaseQueryBuilder($groupKey); + $qb->set("e.$fieldName", "e.$fieldName + :delta")->setParameter('delta', $delta) + ->andWhere("e.$fieldName >= :lbIncl")->setParameter('lbIncl', $lowerBoundIncl); + return $qb->getQuery(); } + + protected function createUpdateBaseQueryBuilder(array $groupKey) { + $qb = $this->getEntityManager()->createQueryBuilder()->update($this->getEntityName(), 'e'); + return $this->augmentQueryWithGroupLimits($qb, $groupKey); + } + + protected function augmentQueryWithGroupLimits(QueryBuilder $qb, array $groupKey) { + foreach ($this->getGroupKeyFields() as $groupFieldName) { + $qb->andWhere("e.$groupFieldName = :$groupFieldName")->setParameter($groupFieldName, $groupKey[$groupFieldName]); + } + return $qb; + } } Modified: trunk/framework/Bee/Persistence/Doctrine2/Log4PHPLogger.php =================================================================== --- trunk/framework/Bee/Persistence/Doctrine2/Log4PHPLogger.php 2013-06-27 03:18:25 UTC (rev 41) +++ trunk/framework/Bee/Persistence/Doctrine2/Log4PHPLogger.php 2013-06-28 14:49:43 UTC (rev 42) @@ -46,7 +46,7 @@ * @return void */ public function startQuery($sql, array $params = null, array $types = null) { - self::getLog()->trace('SQL : [' . $sql . '] PARAMS : [' . serialize($params) . '] TYPES: ['. serialize($types) . ']'); + self::getLog()->trace('SQL : [' . $sql . '] PARAMS : [' . implode(', ', $params) . '] TYPES: ['. implode(', ', $types) . ']'); $this->startTime = microtime(true); } Modified: trunk/framework/Bee/Persistence/Pdo/Behaviors/DelegateBase.php =================================================================== --- trunk/framework/Bee/Persistence/Pdo/Behaviors/DelegateBase.php 2013-06-27 03:18:25 UTC (rev 41) +++ trunk/framework/Bee/Persistence/Pdo/Behaviors/DelegateBase.php 2013-06-28 14:49:43 UTC (rev 42) @@ -79,9 +79,10 @@ * @param bool $restriction * @return string */ - protected function getDomainRestrictionString($entity, array &$params, $restriction = false) { + protected function getDomainRestrictionString($entity, array &$params, array $groupKey = array()) { $result = '1=1'; if ($this->getGroupFieldName()) { + //todo: fix this! if ($restriction === false) { // determine group value $restriction = $this->getGroup($entity); Modified: trunk/framework/Bee/Persistence/Pdo/Behaviors/GenericNestedSetDelegate.php =================================================================== --- trunk/framework/Bee/Persistence/Pdo/Behaviors/GenericNestedSetDelegate.php 2013-06-27 03:18:25 UTC (rev 41) +++ trunk/framework/Bee/Persistence/Pdo/Behaviors/GenericNestedSetDelegate.php 2013-06-28 14:49:43 UTC (rev 42) @@ -102,21 +102,20 @@ /** * @param mixed $nestedSetEntity * @param NodeInfo $nodeInfo - * @param int $newLft - * @param int $newLvl - * @param mixed $restriction + * @param bool|int $newLft + * @param bool|int $newLvl */ - public function setPosition($nestedSetEntity, NodeInfo $nodeInfo, $newLft = false, $newLvl = false, $restriction = false) { + public function setPosition($nestedSetEntity, NodeInfo $nodeInfo, $newLft = false, $newLvl = false) { if ($nodeInfo->hasStructure()) { $params = array(':pos_delta' => $newLft - $nodeInfo->lft, ':lvl_delta' => $newLvl - $nodeInfo->lvl, ':lft' => $nodeInfo->lft, ':rgt' => $nodeInfo->rgt); $qryString = sprintf(self::SET_POSITION_QUERY_TEMPLATE, $this->leftFieldName, $this->rightFieldName, $this->levelFieldName, - $this->getQueryDomain(), $this->getDomainRestrictionString($nestedSetEntity, $params, $restriction)); + $this->getQueryDomain(), $this->getDomainRestrictionString($nestedSetEntity, $params, $nodeInfo->getGroupKey())); } else { $params = array(':lft' => $newLft, ':rgt' => $newLft + $nodeInfo->getSpan() - 1, ':lvl' => $newLvl); $qryString = sprintf(self::SET_POSITION_QUERY_BY_ID_TEMPLATE, $this->leftFieldName, $this->rightFieldName, $this->levelFieldName, $this->getQueryDomain(), $this->getIdentityRestrictionString($nestedSetEntity, $params), - $this->getDomainRestrictionString($nestedSetEntity, $params, $restriction)); + $this->getDomainRestrictionString($nestedSetEntity, $params, $nodeInfo->getGroupKey())); } $this->getPdo()->prepare($qryString)->execute($params); } @@ -126,9 +125,9 @@ * @param int $delta * @param int $lowerBoundIncl * @param int $upperBoundExcl - * @param mixed $restriction + * @param array $groupKey */ - public function shift($nestedSetEntity, $delta, $lowerBoundIncl, $upperBoundExcl, $restriction = false) { + public function shift($nestedSetEntity, $delta, $lowerBoundIncl, $upperBoundExcl, array $groupKey) { $params = array(':delta' => $delta, ':lower_bound' => $lowerBoundIncl); if ($upperBoundExcl !== false) { $params[':upper_bound'] = $upperBoundExcl; @@ -136,7 +135,7 @@ $qryTempl = $upperBoundExcl !== false ? self::SHIFT_QUERY_TEMPLATE : self::SHIFT_QUERY_OPEN_TEMPLATE; $qryDomain = $this->getQueryDomain(); - $domRes = $this->getDomainRestrictionString($nestedSetEntity, $params, $restriction); + $domRes = $this->getDomainRestrictionString($nestedSetEntity, $params, $groupKey); // order updates only if supported by the driver and not operating on a joined relation $orderUpdate = $this->pdoSupportsFeature(FeatureDetector::FEATURE_ORDERED_UPDATE) && stripos($qryDomain, ' JOIN ') === false; Modified: trunk/tests/Bee/Persistence/Behaviors/NestedSet/DelegateMock.php =================================================================== --- trunk/tests/Bee/Persistence/Behaviors/NestedSet/DelegateMock.php 2013-06-27 03:18:25 UTC (rev 41) +++ trunk/tests/Bee/Persistence/Behaviors/NestedSet/DelegateMock.php 2013-06-28 14:49:43 UTC (rev 42) @@ -58,10 +58,9 @@ * @param NodeInfo $nodeInfo * @param bool|int $newLft * @param bool|int $newLvl - * @param mixed $restriction */ - public function setPosition($nestedSetEntity, NodeInfo $nodeInfo, $newLft = false, $newLvl = false, $restriction = false) { - array_push($this->protocol, sprintf('id=%s; lft=%d; rgt=%d; lvl=%d;', $nestedSetEntity->getId(), $nodeInfo->lft, $nodeInfo->rgt, $nodeInfo->lvl)); + public function setPosition($nestedSetEntity, NodeInfo $nodeInfo, $newLft = false, $newLvl = false) { + array_push($this->protocol, sprintf('id=%s; lft=%d; rgt=%d; lvl=%d; grp=[%s]', $nestedSetEntity->getId(), $nodeInfo->lft, $nodeInfo->rgt, $nodeInfo->lvl, implode(',', $nodeInfo->getGroupKey()))); } /** @@ -69,9 +68,17 @@ * @param int $delta * @param int $lowerBoundIncl * @param int $upperBoundExcl - * @param mixed $restriction + * @param array $groupKey */ - public function shift($nestedSetEntity, $delta, $lowerBoundIncl, $upperBoundExcl, $restriction = false) { - array_push($this->protocol, sprintf('delta=%d; %d<=lft/rgt%s;', $delta, $lowerBoundIncl, $upperBoundExcl !== false ? '<'.$upperBoundExcl : '')); + public function shift($nestedSetEntity, $delta, $lowerBoundIncl, $upperBoundExcl, array $groupKey) { + array_push($this->protocol, sprintf('delta=%d; %d<=lft/rgt%s; grp=[%s]', $delta, $lowerBoundIncl, $upperBoundExcl !== false ? '<'.$upperBoundExcl : '', implode(',', $groupKey))); } + + /** + * @param NodeInfo $parentNodeInfo + * @return void + */ + public function unsetChildGroupKeys(NodeInfo $parentNodeInfo) { + // TODO: Implement unsetChildGroupKeys() method. + } } Modified: trunk/tests/Bee/Persistence/Behaviors/NestedSet/TestTreeNode.php =================================================================== --- trunk/tests/Bee/Persistence/Behaviors/NestedSet/TestTreeNode.php 2013-06-27 03:18:25 UTC (rev 41) +++ trunk/tests/Bee/Persistence/Behaviors/NestedSet/TestTreeNode.php 2013-06-28 14:49:43 UTC (rev 42) @@ -40,33 +40,18 @@ private $children; /** + * @param array $id * @param TestTreeNode[] $children * @param array $nodeInfo + * @return \Bee\Persistence\Behaviors\NestedSet\TestTreeNode */ public function __construct($id, $children, array $nodeInfo) { parent::__construct($nodeInfo); $this->id = $id; $this->children = $children; - foreach($this->children as $child) { - $child->setParent($this); - } } /** - * @return ITreeNode - */ - public function getParent() { - return $this->parent; - } - - /** - * @param \Bee\Persistence\Behaviors\NestedSet\ITreeNode $parent - */ - public function setParent($parent) { - $this->parent = $parent; - } - - /** * @return ITreeNode[] */ public function getChildren() { @@ -79,4 +64,12 @@ public function getId() { return $this->id; } + + /** + * @param ITreeNode $child + * @return void + */ + public function appendChild(ITreeNode $child) { + array_push($this->children, $child); + } } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |