Changeset 4759
- Timestamp:
- 08/08/08 19:51:36 (5 months ago)
- Location:
- branches/1.0
- Files:
-
- 2 added
- 6 modified
-
lib/Doctrine/Data/Import.php (modified) (1 diff)
-
lib/Doctrine/Node/NestedSet.php (modified) (16 diffs)
-
lib/Doctrine/Tree/NestedSet.php (modified) (7 diffs)
-
tests/Data/ImportTestCase.php (modified) (2 diffs)
-
tests/models/NestedSet_MultiRootNode.php (added)
-
tests/NestedSet/MultiRootTestCase.php (added)
-
tests/run.php (modified) (1 diff)
-
tests/Ticket/1215TestCase.php (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
branches/1.0/lib/Doctrine/Data/Import.php
r4709 r4759 311 311 312 312 if( ! $parent) { 313 $record->save(); // save, so that createRoot can do: root id = id 313 314 Doctrine::getTable($model)->getTree()->createRoot($record); 314 315 } else { -
branches/1.0/lib/Doctrine/Node/NestedSet.php
r4657 r4759 367 367 $newLevel = $dest->getNode()->getLevel(); 368 368 369 // Make space for new node 370 $this->shiftRLValues($dest->getNode()->getRightValue() + 1, 2, $newRoot); 371 372 // Slide child nodes over one and down one to allow new parent to wrap them 373 $componentName = $this->_tree->getBaseComponent(); 374 $q = new Doctrine_Query(); 375 $q->update($componentName); 376 $q->set("$componentName.lft", "$componentName.lft + 1"); 377 $q->set("$componentName.rgt", "$componentName.rgt + 1"); 378 $q->set("$componentName.level", "$componentName.level + 1"); 379 $q->where("$componentName.lft >= ? AND $componentName.rgt <= ?", array($newLeft, $newRight)); 380 $q = $this->_tree->returnQueryWithRootId($q, $newRoot); 381 $q->execute(); 382 383 $this->record['level'] = $newLevel; 384 $this->insertNode($newLeft, $newRight, $newRoot); 369 $conn = $this->record->getTable()->getConnection(); 370 try { 371 $conn->beginInternalTransaction(); 372 373 // Make space for new node 374 $this->shiftRLValues($dest->getNode()->getRightValue() + 1, 2, $newRoot); 375 376 // Slide child nodes over one and down one to allow new parent to wrap them 377 $componentName = $this->_tree->getBaseComponent(); 378 $q = new Doctrine_Query(); 379 $q->update($componentName); 380 $q->set("$componentName.lft", "$componentName.lft + 1"); 381 $q->set("$componentName.rgt", "$componentName.rgt + 1"); 382 $q->set("$componentName.level", "$componentName.level + 1"); 383 $q->where("$componentName.lft >= ? AND $componentName.rgt <= ?", array($newLeft, $newRight)); 384 $q = $this->_tree->returnQueryWithRootId($q, $newRoot); 385 $q->execute(); 386 387 $this->record['level'] = $newLevel; 388 $this->insertNode($newLeft, $newRight, $newRoot); 389 390 $conn->commit(); 391 } catch (Exception $e) { 392 $conn->rollback(); 393 throw $e; 394 } 385 395 386 396 return true; … … 413 423 $newRoot = $dest->getNode()->getRootValue(); 414 424 415 $this->shiftRLValues($newLeft, 2, $newRoot); 416 $this->record['level'] = $dest['level']; 417 $this->insertNode($newLeft, $newRight, $newRoot); 418 // update destination left/right values to prevent a refresh 419 // $dest->getNode()->setLeftValue($dest->getNode()->getLeftValue() + 2); 420 // $dest->getNode()->setRightValue($dest->getNode()->getRightValue() + 2); 425 $conn = $this->record->getTable()->getConnection(); 426 try { 427 $conn->beginInternalTransaction(); 428 429 $this->shiftRLValues($newLeft, 2, $newRoot); 430 $this->record['level'] = $dest['level']; 431 $this->insertNode($newLeft, $newRight, $newRoot); 432 // update destination left/right values to prevent a refresh 433 // $dest->getNode()->setLeftValue($dest->getNode()->getLeftValue() + 2); 434 // $dest->getNode()->setRightValue($dest->getNode()->getRightValue() + 2); 435 436 $conn->commit(); 437 } catch (Exception $e) { 438 $conn->rollback(); 439 throw $e; 440 } 421 441 422 442 return true; … … 449 469 $newRoot = $dest->getNode()->getRootValue(); 450 470 451 $this->shiftRLValues($newLeft, 2, $newRoot); 452 $this->record['level'] = $dest['level']; 453 $this->insertNode($newLeft, $newRight, $newRoot); 454 455 // update destination left/right values to prevent a refresh 456 // no need, node not affected 471 $conn = $this->record->getTable()->getConnection(); 472 try { 473 $conn->beginInternalTransaction(); 474 475 $this->shiftRLValues($newLeft, 2, $newRoot); 476 $this->record['level'] = $dest['level']; 477 $this->insertNode($newLeft, $newRight, $newRoot); 478 // update destination left/right values to prevent a refresh 479 // no need, node not affected 480 481 $conn->commit(); 482 } catch (Exception $e) { 483 $conn->rollback(); 484 throw $e; 485 } 457 486 458 487 return true; … … 485 514 $newRoot = $dest->getNode()->getRootValue(); 486 515 487 $this->shiftRLValues($newLeft, 2, $newRoot); 488 $this->record['level'] = $dest['level'] + 1; 489 $this->insertNode($newLeft, $newRight, $newRoot); 490 491 // update destination left/right values to prevent a refresh 492 // $dest->getNode()->setRightValue($dest->getNode()->getRightValue() + 2); 516 $conn = $this->record->getTable()->getConnection(); 517 try { 518 $conn->beginInternalTransaction(); 519 520 $this->shiftRLValues($newLeft, 2, $newRoot); 521 $this->record['level'] = $dest['level'] + 1; 522 $this->insertNode($newLeft, $newRight, $newRoot); 523 524 // update destination left/right values to prevent a refresh 525 // $dest->getNode()->setRightValue($dest->getNode()->getRightValue() + 2); 526 527 $conn->commit(); 528 } catch (Exception $e) { 529 $conn->rollback(); 530 throw $e; 531 } 493 532 494 533 return true; … … 521 560 $newRoot = $dest->getNode()->getRootValue(); 522 561 523 $this->shiftRLValues($newLeft, 2, $newRoot); 524 $this->record['level'] = $dest['level'] + 1; 525 $this->insertNode($newLeft, $newRight, $newRoot); 526 527 // update destination left/right values to prevent a refresh 528 // $dest->getNode()->setRightValue($dest->getNode()->getRightValue() + 2); 562 $conn = $this->record->getTable()->getConnection(); 563 try { 564 $conn->beginInternalTransaction(); 565 566 $this->shiftRLValues($newLeft, 2, $newRoot); 567 $this->record['level'] = $dest['level'] + 1; 568 $this->insertNode($newLeft, $newRight, $newRoot); 569 570 // update destination left/right values to prevent a refresh 571 // $dest->getNode()->setRightValue($dest->getNode()->getRightValue() + 2); 572 573 $conn->commit(); 574 } catch (Exception $e) { 575 $conn->rollback(); 576 throw $e; 577 } 529 578 530 579 return true; … … 545 594 546 595 try { 547 $conn->begin Transaction();596 $conn->beginInternalTransaction(); 548 597 549 598 // Move between trees: Detach from old tree & insert into new tree … … 751 800 $oldLevel = $this->record['level']; 752 801 802 $conn = $this->record->getTable()->getConnection(); 753 803 try { 754 $conn = $this->record->getTable()->getConnection(); 755 $conn->beginTransaction(); 804 $conn->beginInternalTransaction(); 756 805 757 806 // Detach from old tree (close gap in old tree) … … 867 916 { 868 917 if ($record === null) { 869 return ($this->getRightValue() > $this->getLeftValue());918 return ($this->getRightValue() > $this->getLeftValue()); 870 919 } else if ( $record instanceof Doctrine_Record ) { 871 return ($record->getNode()->getRightValue() > $record->getNode()->getLeftValue());920 return ($record->getNode()->getRightValue() > $record->getNode()->getLeftValue()); 872 921 } else { 873 return false;922 return false; 874 923 } 875 924 } … … 891 940 public function delete() 892 941 { 893 // TODO: add the setting whether or not to delete descendants or relocate children 894 $oldRoot = $this->getRootValue(); 895 $q = $this->_tree->getBaseQuery(); 896 897 $baseAlias = $this->_tree->getBaseAlias(); 898 $componentName = $this->_tree->getBaseComponent(); 899 900 $q = $q->addWhere("$baseAlias.lft >= ? AND $baseAlias.rgt <= ?", array($this->getLeftValue(), $this->getRightValue())); 901 902 $q = $this->_tree->returnQueryWithRootId($q, $oldRoot); 903 904 $coll = $q->execute(); 905 906 $coll->delete(); 907 908 $first = $this->getRightValue() + 1; 909 $delta = $this->getLeftValue() - $this->getRightValue() - 1; 910 $this->shiftRLValues($first, $delta, $oldRoot); 942 $conn = $this->record->getTable()->getConnection(); 943 try { 944 $conn->beginInternalTransaction(); 945 946 // TODO: add the setting whether or not to delete descendants or relocate children 947 $oldRoot = $this->getRootValue(); 948 $q = $this->_tree->getBaseQuery(); 949 950 $baseAlias = $this->_tree->getBaseAlias(); 951 $componentName = $this->_tree->getBaseComponent(); 952 953 $q = $q->addWhere("$baseAlias.lft >= ? AND $baseAlias.rgt <= ?", array($this->getLeftValue(), $this->getRightValue())); 954 955 $q = $this->_tree->returnQueryWithRootId($q, $oldRoot); 956 957 $coll = $q->execute(); 958 959 $coll->delete(); 960 961 $first = $this->getRightValue() + 1; 962 $delta = $this->getLeftValue() - $this->getRightValue() - 1; 963 $this->shiftRLValues($first, $delta, $oldRoot); 964 965 $conn->commit(); 966 } catch (Exception $e) { 967 $conn->rollback(); 968 throw $e; 969 } 911 970 912 971 return true; … … 942 1001 $treeSize = $right - $left + 1; 943 1002 944 // Make room in the new branch 945 $this->shiftRLValues($destLeft, $treeSize, $rootId); 946 947 if ($left >= $destLeft) { // src was shifted too? 948 $left += $treeSize; 949 $right += $treeSize; 950 } 951 952 // update level for descendants 953 $q = new Doctrine_Query(); 954 $q = $q->update($componentName) 955 ->set($componentName . '.level', 'level + ?') 956 ->where($componentName . '.lft > ? AND ' . $componentName . '.rgt < ?', 957 array($levelDiff, $left, $right)); 958 $q = $this->_tree->returnQueryWithRootId($q, $rootId); 959 $q->execute(); 960 961 // now there's enough room next to target to move the subtree 962 $this->shiftRLRange($left, $right, $destLeft - $left, $rootId); 963 964 // correct values after source (close gap in old tree) 965 $this->shiftRLValues($right + 1, -$treeSize, $rootId); 966 967 $this->record->save(); 968 $this->record->refresh(); 1003 $conn = $this->record->getTable()->getConnection(); 1004 try { 1005 $conn->beginInternalTransaction(); 1006 1007 // Make room in the new branch 1008 $this->shiftRLValues($destLeft, $treeSize, $rootId); 1009 1010 if ($left >= $destLeft) { // src was shifted too? 1011 $left += $treeSize; 1012 $right += $treeSize; 1013 } 1014 1015 // update level for descendants 1016 $q = new Doctrine_Query(); 1017 $q = $q->update($componentName) 1018 ->set($componentName . '.level', 'level + ?') 1019 ->where($componentName . '.lft > ? AND ' . $componentName . '.rgt < ?', 1020 array($levelDiff, $left, $right)); 1021 $q = $this->_tree->returnQueryWithRootId($q, $rootId); 1022 $q->execute(); 1023 1024 // now there's enough room next to target to move the subtree 1025 $this->shiftRLRange($left, $right, $destLeft - $left, $rootId); 1026 1027 // correct values after source (close gap in old tree) 1028 $this->shiftRLValues($right + 1, -$treeSize, $rootId); 1029 1030 $this->record->save(); 1031 $this->record->refresh(); 1032 1033 $conn->commit(); 1034 } catch (Exception $e) { 1035 $conn->rollback(); 1036 throw $e; 1037 } 969 1038 970 1039 return true; … … 973 1042 /** 974 1043 * adds '$delta' to all Left and Right values that are >= '$first'. '$delta' can also be negative. 1044 * 1045 * Note: This method does wrap its database queries in a transaction. This should be done 1046 * by the invoking code. 975 1047 * 976 1048 * @param int $first First node to be shifted … … 985 1057 $componentName = $this->_tree->getBaseComponent(); 986 1058 $qLeft = $qLeft->update($componentName) 987 ->set($componentName . '.lft', 'lft + ?')988 ->where($componentName . '.lft >= ?', array($delta, $first));1059 ->set($componentName . '.lft', 'lft + ?') 1060 ->where($componentName . '.lft >= ?', array($delta, $first)); 989 1061 990 1062 $qLeft = $this->_tree->returnQueryWithRootId($qLeft, $rootId); … … 994 1066 // shift right columns 995 1067 $resultRight = $qRight->update($componentName) 996 ->set($componentName . '.rgt', 'rgt + ?')997 ->where($componentName . '.rgt >= ?', array($delta, $first));1068 ->set($componentName . '.rgt', 'rgt + ?') 1069 ->where($componentName . '.rgt >= ?', array($delta, $first)); 998 1070 999 1071 $qRight = $this->_tree->returnQueryWithRootId($qRight, $rootId); … … 1005 1077 * adds '$delta' to all Left and Right values that are >= '$first' and <= '$last'. 1006 1078 * '$delta' can also be negative. 1079 * 1080 * Note: This method does wrap its database queries in a transaction. This should be done 1081 * by the invoking code. 1007 1082 * 1008 1083 * @param int $first First node to be shifted (L value) … … 1018 1093 $componentName = $this->_tree->getBaseComponent(); 1019 1094 $qLeft = $qLeft->update($componentName) 1020 ->set($componentName . '.lft', 'lft + ?')1021 ->where($componentName . '.lft >= ? AND ' . $componentName . '.lft <= ?', array($delta, $first, $last));1095 ->set($componentName . '.lft', 'lft + ?') 1096 ->where($componentName . '.lft >= ? AND ' . $componentName . '.lft <= ?', array($delta, $first, $last)); 1022 1097 1023 1098 $qLeft = $this->_tree->returnQueryWithRootId($qLeft, $rootId); … … 1027 1102 // shift right column values 1028 1103 $qRight = $qRight->update($componentName) 1029 ->set($componentName . '.rgt', 'rgt + ?')1030 ->where($componentName . '.rgt >= ? AND ' . $componentName . '.rgt <= ?', array($delta, $first, $last));1104 ->set($componentName . '.rgt', 'rgt + ?') 1105 ->where($componentName . '.rgt >= ? AND ' . $componentName . '.rgt <= ?', array($delta, $first, $last)); 1031 1106 1032 1107 $qRight = $this->_tree->returnQueryWithRootId($qRight, $rootId); -
branches/1.0/lib/Doctrine/Tree/NestedSet.php
r4707 r4759 30 30 * @version $Revision$ 31 31 * @author Joe Simms <joe.simms@websites4.com> 32 * @author Roman Borschel <roman@code-factory.org> 32 33 */ 33 34 class Doctrine_Tree_NestedSet extends Doctrine_Tree implements Doctrine_Tree_Interface … … 70 71 71 72 /** 72 * creates root node from given record or from a new record 73 * Creates root node from given record or from a new record. 74 * 75 * Note: When using a tree with multiple root nodes (hasManyRoots), you MUST pass in a 76 * record to use as the root. This can either be a new/transient record that already has 77 * the root id column set to some numeric value OR a persistent record. In the latter case 78 * the records id will be assigned to the root id. You must use numeric columns for the id 79 * and root id columns. 73 80 * 74 81 * @param object $record instance of Doctrine_Record … … 76 83 public function createRoot(Doctrine_Record $record = null) 77 84 { 85 if ($this->getAttribute('hasManyRoots')) { 86 if ( ! $record || ( ! $record->exists() && $record->getNode()->getRootValue() <= 0) 87 || $record->getTable()->isIdentifierComposite()) { 88 throw new Doctrine_Tree_Exception("Node must have a root id set or must " 89 . " be persistent and have a single-valued numeric primary key in order to" 90 . " be created as a root node. Automatic assignment of a root id on" 91 . " transient/new records is no longer supported."); 92 } 93 94 if ($record->exists() && $record->getNode()->getRootValue() <= 0) { 95 // Default: root_id = id 96 $identifier = $record->getTable()->getIdentifier(); 97 $record->getNode()->setRootValue($record->get($identifier)); 98 } 99 } 100 78 101 if ( ! $record) { 79 102 $record = $this->table->create(); 80 }81 82 // if tree is many roots, and no root id has been set, then get next root id83 if ($root = $this->getAttribute('hasManyRoots') && $record->getNode()->getRootValue() <= 0) {84 $record->getNode()->setRootValue($this->getNextRootId());85 103 } 86 104 … … 109 127 * 110 128 * @param integer $rootId 129 * @todo Better $rootid = null and exception if $rootId == null && hasManyRoots? 130 * Fetching with id = 1 is too magical and cant work reliably anyway. 111 131 */ 112 132 public function fetchRoot($rootId = 1) … … 214 234 * 215 235 * @return integer 236 * @deprecated THIS METHOD IS DEPRECATED. ROOT IDS ARE NO LONGER AUTOMATICALLY GENERATED. 237 * ROOT ID MUST BE ASSIGNED MANUALLY OR USING THE DEFAULT BEHAVIOR WHERE 238 * ROOT_ID = ID. SEE createRoot() FOR DETAILS. 216 239 */ 217 240 public function getNextRootId() … … 224 247 * 225 248 * @return integer 249 * @deprecated THIS METHOD IS DEPRECATED. ROOT IDS ARE NO LONGER AUTOMATICALLY GENERATED. 250 * ROOT ID MUST BE ASSIGNED MANUALLY OR USING THE DEFAULT BEHAVIOR WHERE 251 * ROOT_ID = ID. SEE createRoot() FOR DETAILS. 226 252 */ 227 253 public function getMaxRootId() 228 254 { 229 255 $component = $this->table->getComponentName(); 230 $column = $this->getAttribute('rootColumnName');256 $column = $this->getAttribute('rootColumnName'); 231 257 232 258 // cannot get this dql to work, cannot retrieve result using $coll[0]->max … … 326 352 $this->_baseQuery = $this->_createBaseQuery(); 327 353 } 328 329 /**330 * Enter description here...331 *332 * @param unknown_type $graph333 */334 /*335 public function computeLevels($tree)336 {337 $right = array();338 $isArray = is_array($tree);339 $rootColumnName = $this->getAttribute('rootColumnName');340 341 for ($i = 0, $count = count($tree); $i < $count; $i++) {342 if ($rootColumnName && $i > 0 && $tree[$i][$rootColumnName] != $tree[$i-1][$rootColumnName]) {343 $right = array();344 }345 346 if (count($right) > 0) {347 while (count($right) > 0 && $right[count($right)-1] < $tree[$i]['rgt']) {348 //echo count($right);349 array_pop($right);350 }351 }352 353 if ($isArray) {354 $tree[$i]['level'] = count($right);355 } else {356 $tree[$i]->getNode()->setLevel(count($right));357 }358 359 $right[] = $tree[$i]['rgt'];360 }361 return $tree;362 }363 */364 354 } -
branches/1.0/tests/Data/ImportTestCase.php
r4758 r4759 309 309 $this->assertEqual($i[0]['rgt'], 6); 310 310 $this->assertEqual($i[0]['level'], 0); 311 $this->assertEqual($i[0]['root_id'], '1');311 $this->assertEqual($i[0]['root_id'], $i[0]['id']); 312 312 313 313 $this->assertEqual($i[1]['name'], 'Item 1.1'); … … 321 321 $this->assertEqual($i[3]['rgt'], 12); 322 322 $this->assertEqual($i[3]['level'], 0); 323 $this->assertEqual($i[3]['root_id'], '2');323 $this->assertEqual($i[3]['root_id'], $i[3]['id']); 324 324 325 325 $this->assertEqual($i[4]['name'], 'Item 2.1'); -
branches/1.0/tests/run.php
r4754 r4759 373 373 $unsorted->addTestCase(new Doctrine_Template_TestCase()); 374 374 $unsorted->addTestCase(new Doctrine_NestedSet_SingleRoot_TestCase()); 375 $unsorted->addTestCase(new Doctrine_NestedSet_MultiRoot_TestCase()); 375 376 $unsorted->addTestCase(new Doctrine_PessimisticLocking_TestCase()); 376 377 $test->addTestCase($unsorted); -
branches/1.0/tests/Ticket/1215TestCase.php
r4629 r4759 39 39 } 40 40 41 /* 42 TEST NO LONGER VALID AS DOCTRINE-GENERATED ROOT IDS ARE NO LONGER SUPPORTED. 43 SEE Doctrine_Tree_NestedSet#createRoot(), Doctrine_Tree_NestedSet#getNextRootId() AND 44 Doctrine_Tree_NestedSet#getMaxRootId() FOR DETAILS. 45 41 46 public function prepareData() 42 47 { … … 65 70 $tree = Doctrine::getTable('Ticket_1215_TreeManyRoots')->getTree(); 66 71 $this->assertEqual($tree->getMaxRootId(), 2); 67 } 72 }*/ 68 73 } 69 74