Changeset 4759

Show
Ignore:
Timestamp:
08/08/08 19:51:36 (5 months ago)
Author:
romanb
Message:

Fixed #1126. Fixed #1165.

Location:
branches/1.0
Files:
2 added
6 modified

Legend:

Unmodified
Added
Removed
  • branches/1.0/lib/Doctrine/Data/Import.php

    r4709 r4759  
    311311 
    312312            if( ! $parent) { 
     313                $record->save(); // save, so that createRoot can do: root id = id 
    313314                Doctrine::getTable($model)->getTree()->createRoot($record); 
    314315            } else { 
  • branches/1.0/lib/Doctrine/Node/NestedSet.php

    r4657 r4759  
    367367                $newLevel = $dest->getNode()->getLevel(); 
    368368                 
    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                } 
    385395         
    386396        return true; 
     
    413423        $newRoot = $dest->getNode()->getRootValue(); 
    414424         
    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        } 
    421441                         
    422442        return true; 
     
    449469        $newRoot = $dest->getNode()->getRootValue(); 
    450470 
    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        } 
    457486 
    458487        return true; 
     
    485514        $newRoot = $dest->getNode()->getRootValue(); 
    486515 
    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        } 
    493532 
    494533        return true; 
     
    521560        $newRoot = $dest->getNode()->getRootValue(); 
    522561 
    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        } 
    529578         
    530579        return true; 
     
    545594             
    546595        try { 
    547             $conn->beginTransaction(); 
     596            $conn->beginInternalTransaction(); 
    548597 
    549598            // Move between trees: Detach from old tree & insert into new tree 
     
    751800        $oldLevel = $this->record['level']; 
    752801         
     802        $conn = $this->record->getTable()->getConnection(); 
    753803        try { 
    754             $conn = $this->record->getTable()->getConnection(); 
    755             $conn->beginTransaction(); 
     804            $conn->beginInternalTransaction(); 
    756805             
    757806            // Detach from old tree (close gap in old tree) 
     
    867916    { 
    868917        if ($record === null) { 
    869           return ($this->getRightValue() > $this->getLeftValue()); 
     918            return ($this->getRightValue() > $this->getLeftValue()); 
    870919        } else if ( $record instanceof Doctrine_Record ) { 
    871           return ($record->getNode()->getRightValue() > $record->getNode()->getLeftValue()); 
     920            return ($record->getNode()->getRightValue() > $record->getNode()->getLeftValue()); 
    872921        } else { 
    873           return false; 
     922            return false; 
    874923        } 
    875924    } 
     
    891940    public function delete() 
    892941    { 
    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        } 
    911970         
    912971        return true;  
     
    9421001        $treeSize = $right - $left + 1; 
    9431002 
    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        } 
    9691038         
    9701039        return true; 
     
    9731042    /** 
    9741043     * 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. 
    9751047     * 
    9761048     * @param int $first         First node to be shifted 
     
    9851057        $componentName = $this->_tree->getBaseComponent(); 
    9861058        $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)); 
    9891061         
    9901062        $qLeft = $this->_tree->returnQueryWithRootId($qLeft, $rootId); 
     
    9941066        // shift right columns 
    9951067        $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)); 
    9981070 
    9991071        $qRight = $this->_tree->returnQueryWithRootId($qRight, $rootId); 
     
    10051077     * adds '$delta' to all Left and Right values that are >= '$first' and <= '$last'.  
    10061078     * '$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. 
    10071082     * 
    10081083     * @param int $first     First node to be shifted (L value) 
     
    10181093        $componentName = $this->_tree->getBaseComponent(); 
    10191094        $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)); 
    10221097         
    10231098        $qLeft = $this->_tree->returnQueryWithRootId($qLeft, $rootId); 
     
    10271102        // shift right column values 
    10281103        $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)); 
    10311106 
    10321107        $qRight = $this->_tree->returnQueryWithRootId($qRight, $rootId); 
  • branches/1.0/lib/Doctrine/Tree/NestedSet.php

    r4707 r4759  
    3030 * @version     $Revision$ 
    3131 * @author      Joe Simms <joe.simms@websites4.com> 
     32 * @author      Roman Borschel <roman@code-factory.org> 
    3233 */ 
    3334class Doctrine_Tree_NestedSet extends Doctrine_Tree implements Doctrine_Tree_Interface 
     
    7071 
    7172    /** 
    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. 
    7380     * 
    7481     * @param object $record        instance of Doctrine_Record 
     
    7683    public function createRoot(Doctrine_Record $record = null) 
    7784    { 
     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         
    78101        if ( ! $record) { 
    79102            $record = $this->table->create(); 
    80         } 
    81  
    82         // if tree is many roots, and no root id has been set, then get next root id 
    83         if ($root = $this->getAttribute('hasManyRoots') && $record->getNode()->getRootValue() <= 0) { 
    84             $record->getNode()->setRootValue($this->getNextRootId()); 
    85103        } 
    86104 
     
    109127     * 
    110128     * @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. 
    111131     */ 
    112132    public function fetchRoot($rootId = 1) 
     
    214234     * 
    215235     * @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. 
    216239     */ 
    217240    public function getNextRootId() 
     
    224247     * 
    225248     * @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. 
    226252     */ 
    227253    public function getMaxRootId() 
    228254    { 
    229255        $component = $this->table->getComponentName(); 
    230         $column    = $this->getAttribute('rootColumnName'); 
     256        $column = $this->getAttribute('rootColumnName'); 
    231257 
    232258        // cannot get this dql to work, cannot retrieve result using $coll[0]->max 
     
    326352        $this->_baseQuery = $this->_createBaseQuery(); 
    327353    } 
    328  
    329     /** 
    330      * Enter description here... 
    331      * 
    332      * @param unknown_type $graph 
    333      */ 
    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     */ 
    364354} 
  • branches/1.0/tests/Data/ImportTestCase.php

    r4758 r4759  
    309309            $this->assertEqual($i[0]['rgt'], 6); 
    310310            $this->assertEqual($i[0]['level'], 0); 
    311             $this->assertEqual($i[0]['root_id'], '1'); 
     311            $this->assertEqual($i[0]['root_id'], $i[0]['id']); 
    312312 
    313313            $this->assertEqual($i[1]['name'], 'Item 1.1'); 
     
    321321            $this->assertEqual($i[3]['rgt'], 12); 
    322322            $this->assertEqual($i[3]['level'], 0); 
    323             $this->assertEqual($i[3]['root_id'], '2'); 
     323            $this->assertEqual($i[3]['root_id'], $i[3]['id']); 
    324324 
    325325            $this->assertEqual($i[4]['name'], 'Item 2.1'); 
  • branches/1.0/tests/run.php

    r4754 r4759  
    373373$unsorted->addTestCase(new Doctrine_Template_TestCase()); 
    374374$unsorted->addTestCase(new Doctrine_NestedSet_SingleRoot_TestCase()); 
     375$unsorted->addTestCase(new Doctrine_NestedSet_MultiRoot_TestCase()); 
    375376$unsorted->addTestCase(new Doctrine_PessimisticLocking_TestCase()); 
    376377$test->addTestCase($unsorted); 
  • branches/1.0/tests/Ticket/1215TestCase.php

    r4629 r4759  
    3939    } 
    4040     
     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     
    4146    public function prepareData() 
    4247    { 
     
    6570        $tree = Doctrine::getTable('Ticket_1215_TreeManyRoots')->getTree(); 
    6671        $this->assertEqual($tree->getMaxRootId(), 2); 
    67     } 
     72    }*/ 
    6873} 
    6974