public function providerBuild() { $rootOnly = new Tree(); $rootOnly->set('root_only'); $bTItems = ['0', '00', '000', '001', '01', '010', '011', '1', '10', '100', '101', '11', '110', '111']; foreach ($bTItems as $item) { $treeItem = 'bT' . $item; ${$treeItem} = new Tree(); ${$treeItem}->set(['v' => $item]); } foreach ($bTItems as $item) { if (strlen($item) < 3) { $treeItem = 'bT' . $item; $leftItem = $treeItem . '0'; $rightItem = $treeItem . '1'; ${$treeItem}->add(${$leftItem}); ${$treeItem}->add(${$rightItem}); } } $binaryTree = new Tree(); $binaryTree->set('binary_tree'); $binaryTree->add($bT0); $binaryTree->add($bT1); return ['root_only' => ['tree_name' => 'root_only', 'mptt' => [['lft' => 0, 'rgt' => 1, 'value' => 'ROOT_ITEM']], 'expected' => $rootOnly], 'binary' => ['tree_name' => 'binary_tree', 'mptt' => [['lft' => 0, 'rgt' => 29, 'v' => 'ROOT_ITEM'], ['lft' => 1, 'rgt' => 14, 'v' => '0'], ['lft' => 2, 'rgt' => 7, 'v' => '00'], ['lft' => 3, 'rgt' => 4, 'v' => '000'], ['lft' => 5, 'rgt' => 6, 'v' => '001'], ['lft' => 8, 'rgt' => 13, 'v' => '01'], ['lft' => 9, 'rgt' => 10, 'v' => '010'], ['lft' => 11, 'rgt' => 12, 'v' => '011'], ['lft' => 15, 'rgt' => 28, 'v' => '1'], ['lft' => 16, 'rgt' => 21, 'v' => '10'], ['lft' => 17, 'rgt' => 18, 'v' => '100'], ['lft' => 19, 'rgt' => 20, 'v' => '101'], ['lft' => 22, 'rgt' => 27, 'v' => '11'], ['lft' => 23, 'rgt' => 24, 'v' => '110'], ['lft' => 25, 'rgt' => 26, 'v' => '111']], 'expected' => $binaryTree]]; }
/** * Build a tree from Modified Pre-order Tree Traversal data. * * @param string $treeName The name of the tree. * @param mixed[] $mptt The Modified Pre-order Tree Traversal data. * @return Tree The tree. * @throws InvalidArgumentException If the MPTT data is bad. */ public function build(string $treeName, array $mptt) : Tree { $mpttItems = count($mptt); if ($mpttItems < 1) { throw new InvalidArgumentException('needs MPTT entries to build tree.'); } if (!isset($mptt[0][$this->left], $mptt[0][$this->right])) { throw new InvalidArgumentException('needs MPTT root with ' . $this->left . ' and ' . $this->right . ' fields.'); } $rootNode = new Tree(); $level = 0; $treePtrs = []; $children = []; $children[$level] = ($mptt[0][$this->right] - $mptt[0][$this->left] - 1) / 2; unset($mptt[0][$this->left], $mptt[0][$this->right]); $rootNode->set($treeName); $treePtrs[$level++] =& $rootNode; for ($item = 1; $item < $mpttItems; ++$item) { if (!isset($mptt[$item][$this->left], $mptt[$item][$this->right])) { throw new InvalidArgumentException('needs MPTT data at ' . $item . ' with ' . $this->left . ' and ' . $this->right . ' fields.'); } $node = new Tree(); $childNodes = ($mptt[$item][$this->right] - $mptt[$item][$this->left] - 1) / 2; unset($mptt[$item][$this->left], $mptt[$item][$this->right]); $node->set($mptt[$item]); $treePtrs[$level - 1]->add($node); // We have processed the node, update the child counts, removing a level if it has been fully processed. for ($lev = $level - 1; $lev >= 0; --$lev) { if (--$children[$lev] === 0) { unset($children[--$level]); } } // If we have children update the tree pointers and level. if ($childNodes > 0) { $children[$level] = $childNodes; $treePtrs[$level++] =& $node; } // Unset the local reference to node so that we can use it as a variable again. unset($node); } return $rootNode; }
/** * @dataProvider providerUseValue */ public function testUseValue($value) { $obj = new Tree(); $obj->set($value); $this->assertSame($value, $obj->get()); }
/** * Multi Level Menu. * * @uses Evoke\Model\Data\Tree */ public function testMultiLevelMenuRealTree() { $letters = ['a', 'b', 'c', 'd']; $firstLevelItems = [3, 2, 1, 0]; $secondLevelItems = [[1, 2, 3], [2, 0], [1]]; $treeElements = []; $tree = new Tree(); $tree->set('multi'); // Use a real tree because mocking this is too complex. foreach ($letters as $index => $letter) { $treeElements[$letter] = new Tree(); $treeElements[$letter]->set(['href' => '/0/' . $letter, 'text' => '0 ' . $letter]); for ($i = 0; $i < $firstLevelItems[$index]; $i++) { $firstLevelIndex = $letter . $i; $treeElements[$firstLevelIndex] = new Tree(); $treeElements[$firstLevelIndex]->set(['href' => '/1/' . $firstLevelIndex, 'text' => '1 ' . $firstLevelIndex]); for ($j = 0; $j < $secondLevelItems[$index][$i]; $j++) { $secondLevelIndex = $letter . $i . $letters[$j]; $treeElements[$secondLevelIndex] = new Tree(); $treeElements[$secondLevelIndex]->set(['href' => '/2/' . $secondLevelIndex, 'text' => '2 ' . $secondLevelIndex]); $treeElements[$firstLevelIndex]->add($treeElements[$secondLevelIndex]); } $treeElements[$letter]->add($treeElements[$firstLevelIndex]); } $tree->add($treeElements[$letter]); } $obj = new Menu(); $obj->set($tree); $expected = ['ul', ['class' => 'menu multi'], [['li', ['class' => 'menu_item level_0'], [['a', ['href' => '/0/a'], '0 a'], ['ul', [], [['li', ['class' => 'menu_item level_1'], [['a', ['href' => '/1/a0'], '1 a0'], ['ul', [], [['li', ['class' => 'menu_item level_2'], [['a', ['href' => '/2/a0a'], '2 a0a']]]]]]], ['li', ['class' => 'menu_item level_1'], [['a', ['href' => '/1/a1'], '1 a1'], ['ul', [], [['li', ['class' => 'menu_item level_2'], [['a', ['href' => '/2/a1a'], '2 a1a']]], ['li', ['class' => 'menu_item level_2'], [['a', ['href' => '/2/a1b'], '2 a1b']]]]]]], ['li', ['class' => 'menu_item level_1'], [['a', ['href' => '/1/a2'], '1 a2'], ['ul', [], [['li', ['class' => 'menu_item level_2'], [['a', ['href' => '/2/a2a'], '2 a2a']]], ['li', ['class' => 'menu_item level_2'], [['a', ['href' => '/2/a2b'], '2 a2b']]], ['li', ['class' => 'menu_item level_2'], [['a', ['href' => '/2/a2c'], '2 a2c']]]]]]]]]]], ['li', ['class' => 'menu_item level_0'], [['a', ['href' => '/0/b'], '0 b'], ['ul', [], [['li', ['class' => 'menu_item level_1'], [['a', ['href' => '/1/b0'], '1 b0'], ['ul', [], [['li', ['class' => 'menu_item level_2'], [['a', ['href' => '/2/b0a'], '2 b0a']]], ['li', ['class' => 'menu_item level_2'], [['a', ['href' => '/2/b0b'], '2 b0b']]]]]]], ['li', ['class' => 'menu_item level_1'], [['a', ['href' => '/1/b1'], '1 b1']]]]]]], ['li', ['class' => 'menu_item level_0'], [['a', ['href' => '/0/c'], '0 c'], ['ul', [], [['li', ['class' => 'menu_item level_1'], [['a', ['href' => '/1/c0'], '1 c0'], ['ul', [], [['li', ['class' => 'menu_item level_2'], [['a', ['href' => '/2/c0a'], '2 c0a']]]]]]]]]]], ['li', ['class' => 'menu_item level_0'], [['a', ['href' => '/0/d'], '0 d']]]]]; $actual = $obj->get(); $this->assertSame($expected, $actual); }