TreeBehaviorTest.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice.
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  11. * @link http://cakephp.org CakePHP(tm) Project
  12. * @since CakePHP(tm) v 3.0.0
  13. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  14. */
  15. namespace Cake\Test\TestCase\Model\Behavior;
  16. use Cake\Collection\Collection;
  17. use Cake\Event\Event;
  18. use Cake\Model\Behavior\TranslateBehavior;
  19. use Cake\ORM\Entity;
  20. use Cake\ORM\TableRegistry;
  21. use Cake\TestSuite\TestCase;
  22. /**
  23. * Translate behavior test case
  24. */
  25. class TreeBehaviorTest extends TestCase {
  26. /**
  27. * fixtures
  28. *
  29. * @var array
  30. */
  31. public $fixtures = [
  32. 'core.number_tree',
  33. 'core.menu_link_tree'
  34. ];
  35. public function setUp() {
  36. parent::setUp();
  37. $this->table = TableRegistry::get('NumberTrees');
  38. $this->table->addBehavior('Tree');
  39. }
  40. public function tearDown() {
  41. parent::tearDown();
  42. TableRegistry::clear();
  43. }
  44. /**
  45. * Tests the find('path') method
  46. *
  47. * @return void
  48. */
  49. public function testFindPath() {
  50. $nodes = $this->table->find('path', ['for' => 9]);
  51. $this->assertEquals([1, 6, 9], $nodes->extract('id')->toArray());
  52. $nodes = $this->table->find('path', ['for' => 10]);
  53. $this->assertEquals([1, 6, 10], $nodes->extract('id')->toArray());
  54. $nodes = $this->table->find('path', ['for' => 5]);
  55. $this->assertEquals([1, 2, 5], $nodes->extract('id')->toArray());
  56. $nodes = $this->table->find('path', ['for' => 1]);
  57. $this->assertEquals([1], $nodes->extract('id')->toArray());
  58. // find path with scope
  59. $table = TableRegistry::get('MenuLinkTrees');
  60. $table->addBehavior('Tree', ['scope' => ['menu' => 'main-menu']]);
  61. $nodes = $table->find('path', ['for' => 5]);
  62. $this->assertEquals([1, 3, 4, 5], $nodes->extract('id')->toArray());
  63. }
  64. /**
  65. * Tests the childCount() method
  66. *
  67. * @return void
  68. */
  69. public function testChildCount() {
  70. // direct children for the root node
  71. $table = $this->table;
  72. $countDirect = $this->table->childCount($table->get(1), true);
  73. $this->assertEquals(2, $countDirect);
  74. // counts all the children of root
  75. $count = $this->table->childCount($table->get(1), false);
  76. $this->assertEquals(9, $count);
  77. // counts direct children
  78. $count = $this->table->childCount($table->get(2), false);
  79. $this->assertEquals(3, $count);
  80. // count children for a middle-node
  81. $count = $this->table->childCount($table->get(6), false);
  82. $this->assertEquals(4, $count);
  83. // count leaf children
  84. $count = $this->table->childCount($table->get(10), false);
  85. $this->assertEquals(0, $count);
  86. // test scoping
  87. $table = TableRegistry::get('MenuLinkTrees');
  88. $table->addBehavior('Tree', ['scope' => ['menu' => 'main-menu']]);
  89. $count = $table->childCount($table->get(3), false);
  90. $this->assertEquals(2, $count);
  91. }
  92. /**
  93. * Tests the childCount() plus callable scoping
  94. *
  95. * @return void
  96. */
  97. public function testCallableScoping() {
  98. $table = TableRegistry::get('MenuLinkTrees');
  99. $table->addBehavior('Tree', [
  100. 'scope' => function ($query) {
  101. return $query->where(['menu' => 'main-menu']);
  102. }
  103. ]);
  104. $count = $table->childCount($table->get(1), false);
  105. $this->assertEquals(4, $count);
  106. }
  107. /**
  108. * Tests the find('children') method
  109. *
  110. * @return void
  111. */
  112. public function testFindChildren() {
  113. $table = TableRegistry::get('MenuLinkTrees');
  114. $table->addBehavior('Tree', ['scope' => ['menu' => 'main-menu']]);
  115. // root
  116. $nodeIds = [];
  117. $nodes = $table->find('children', ['for' => 1])->all();
  118. $this->assertEquals([2, 3, 4, 5], $nodes->extract('id')->toArray());
  119. // leaf
  120. $nodeIds = [];
  121. $nodes = $table->find('children', ['for' => 5])->all();
  122. $this->assertEquals(0, count($nodes->extract('id')->toArray()));
  123. // direct children
  124. $nodes = $table->find('children', ['for' => 1, 'direct' => true])->all();
  125. $this->assertEquals([2, 3], $nodes->extract('id')->toArray());
  126. }
  127. /**
  128. * Tests that find('children') will throw an exception if the node was not found
  129. *
  130. * @expectedException \Cake\ORM\Error\RecordNotFoundException
  131. * @return void
  132. */
  133. public function testFindChildrenException() {
  134. $table = TableRegistry::get('MenuLinkTrees');
  135. $table->addBehavior('Tree', ['scope' => ['menu' => 'main-menu']]);
  136. $query = $table->find('children', ['for' => 500]);
  137. }
  138. /**
  139. * Tests the find('treeList') method
  140. *
  141. * @return void
  142. */
  143. public function testFindTreeList() {
  144. $table = TableRegistry::get('MenuLinkTrees');
  145. $table->addBehavior('Tree', ['scope' => ['menu' => 'main-menu']]);
  146. $result = $table->find('treeList')->toArray();
  147. $expected = [
  148. 1 => 'Link 1',
  149. 2 => '_Link 2',
  150. 3 => '_Link 3',
  151. 4 => '__Link 4',
  152. 5 => '___Link 5',
  153. 6 => 'Link 6',
  154. 7 => '_Link 7',
  155. 8 => 'Link 8'
  156. ];
  157. $this->assertEquals($expected, $result);
  158. }
  159. /**
  160. * Tests the find('treeList') method with custom options
  161. *
  162. * @return void
  163. */
  164. public function testFindTreeListCustom() {
  165. $table = TableRegistry::get('MenuLinkTrees');
  166. $table->addBehavior('Tree', ['scope' => ['menu' => 'main-menu']]);
  167. $result = $table
  168. ->find('treeList', ['keyPath' => 'url', 'valuePath' => 'id', 'spacer' => ' '])
  169. ->toArray();
  170. $expected = [
  171. '/link1.html' => '1',
  172. 'http://example.com' => ' 2',
  173. '/what/even-more-links.html' => ' 3',
  174. '/lorem/ipsum.html' => ' 4',
  175. '/what/the.html' => ' 5',
  176. '/yeah/another-link.html' => '6',
  177. 'http://cakephp.org' => ' 7',
  178. '/page/who-we-are.html' => '8'
  179. ];
  180. $this->assertEquals($expected, $result);
  181. }
  182. /**
  183. * Tests the moveUp() method
  184. *
  185. * @return void
  186. */
  187. public function testMoveUp() {
  188. $table = TableRegistry::get('MenuLinkTrees');
  189. $table->addBehavior('Tree', ['scope' => ['menu' => 'main-menu']]);
  190. // top level, wont move
  191. $node = $this->table->moveUp($table->get(1), 10);
  192. $this->assertEquals(['lft' => 1, 'rght' => 10], $node->extract(['lft', 'rght']));
  193. // edge cases
  194. $this->assertFalse($this->table->moveUp($table->get(1), 0));
  195. $node = $this->table->moveUp($table->get(1), -10);
  196. $this->assertEquals(['lft' => 1, 'rght' => 10], $node->extract(['lft', 'rght']));
  197. // move inner node
  198. $node = $table->moveUp($table->get(3), 1);
  199. $nodes = $table->find('children', ['for' => 1])->all();
  200. $this->assertEquals([3, 4, 5, 2], $nodes->extract('id')->toArray());
  201. $this->assertEquals(['lft' => 2, 'rght' => 7], $node->extract(['lft', 'rght']));
  202. }
  203. /**
  204. * Tests moving a node with no siblings
  205. *
  206. * @return void
  207. */
  208. public function testMoveLeaf() {
  209. $table = TableRegistry::get('MenuLinkTrees');
  210. $table->addBehavior('Tree', ['scope' => ['menu' => 'main-menu']]);
  211. $node = $table->moveUp($table->get(5), 1);
  212. $this->assertEquals(['lft' => 6, 'rght' => 7], $node->extract(['lft', 'rght']));
  213. }
  214. /**
  215. * Tests moving a node to the top
  216. *
  217. * @return void
  218. */
  219. public function testMoveTop() {
  220. $table = TableRegistry::get('MenuLinkTrees');
  221. $table->addBehavior('Tree', ['scope' => ['menu' => 'main-menu']]);
  222. $node = $table->moveUp($table->get(8), true);
  223. $this->assertEquals(['lft' => 1, 'rght' => 2], $node->extract(['lft', 'rght']));
  224. $nodes = $table->find()
  225. ->select(['id'])
  226. ->where(function($exp) {
  227. return $exp->isNull('parent_id');
  228. })
  229. ->where(['menu' => 'main-menu'])
  230. ->order(['lft' => 'ASC'])
  231. ->all();
  232. $this->assertEquals([8, 1, 6], $nodes->extract('id')->toArray());
  233. }
  234. /**
  235. * Tests the moveDown() method
  236. *
  237. * @return void
  238. */
  239. public function testMoveDown() {
  240. $table = TableRegistry::get('MenuLinkTrees');
  241. $table->addBehavior('Tree', ['scope' => ['menu' => 'main-menu']]);
  242. // latest node, wont move
  243. $node = $this->table->moveDown($table->get(8), 10);
  244. $this->assertEquals(['lft' => 21, 'rght' => 22], $node->extract(['lft', 'rght']));
  245. // edge cases
  246. $this->assertFalse($this->table->moveDown($table->get(8), 0));
  247. // move inner node
  248. $node = $table->moveDown($table->get(2), 1);
  249. $nodes = $table->find('children', ['for' => 1])->all();
  250. $this->assertEquals([3, 4, 5, 2], $nodes->extract('id')->toArray());
  251. $this->assertEquals(['lft' => 11, 'rght' => 12], $node->extract(['lft', 'rght']));
  252. }
  253. /**
  254. * Tests moving a node that has no siblings
  255. *
  256. * @return void
  257. */
  258. public function testMoveLeafDown() {
  259. $table = TableRegistry::get('MenuLinkTrees');
  260. $table->addBehavior('Tree', ['scope' => ['menu' => 'main-menu']]);
  261. $node = $table->moveDown($table->get(5), 1);
  262. $this->assertEquals(['lft' => 6, 'rght' => 7], $node->extract(['lft', 'rght']));
  263. }
  264. /**
  265. * Tests moving a node to the bottom
  266. *
  267. * @return void
  268. */
  269. public function testMoveToBottom() {
  270. $table = TableRegistry::get('MenuLinkTrees');
  271. $table->addBehavior('Tree', ['scope' => ['menu' => 'main-menu']]);
  272. $node = $table->moveDown($table->get(1), true);
  273. $this->assertEquals(['lft' => 7, 'rght' => 16], $node->extract(['lft', 'rght']));
  274. $nodes = $table->find()
  275. ->select(['id'])
  276. ->where(function($exp) {
  277. return $exp->isNull('parent_id');
  278. })
  279. ->where(['menu' => 'main-menu'])
  280. ->order(['lft' => 'ASC'])
  281. ->all();
  282. $this->assertEquals([6, 8, 1], $nodes->extract('id')->toArray());
  283. }
  284. /**
  285. * Tests the recover function
  286. *
  287. * @return void
  288. */
  289. public function testRecover() {
  290. $table = TableRegistry::get('NumberTrees');
  291. $table->addBehavior('Tree');
  292. $expected = $table->find()->order('lft')->hydrate(false)->toArray();
  293. $table->updateAll(['lft' => null, 'rght' => null], []);
  294. $table->recover();
  295. $result = $table->find()->order('lft')->hydrate(false)->toArray();
  296. $this->assertEquals($expected, $result);
  297. }
  298. /**
  299. * Tests the recover function with a custom scope
  300. *
  301. * @return void
  302. */
  303. public function testRecoverScoped() {
  304. $table = TableRegistry::get('MenuLinkTrees');
  305. $table->addBehavior('Tree', ['scope' => ['menu' => 'main-menu']]);
  306. $expected = $table->find()
  307. ->where(['menu' => 'main-menu'])
  308. ->order('lft')
  309. ->hydrate(false)
  310. ->toArray();
  311. $expected2 = $table->find()
  312. ->where(['menu' => 'categories'])
  313. ->order('lft')
  314. ->hydrate(false)
  315. ->toArray();
  316. $table->updateAll(['lft' => null, 'rght' => null], ['menu' => 'main-menu']);
  317. $table->recover();
  318. $result = $table->find()
  319. ->where(['menu' => 'main-menu'])
  320. ->order('lft')
  321. ->hydrate(false)
  322. ->toArray();
  323. $this->assertEquals($expected, $result);
  324. $result2 = $table->find()
  325. ->where(['menu' => 'categories'])
  326. ->order('lft')
  327. ->hydrate(false)
  328. ->toArray();
  329. $this->assertEquals($expected2, $result2);
  330. }
  331. /**
  332. * Tests adding a new orphan node
  333. *
  334. * @return void
  335. */
  336. public function testAddOrphan() {
  337. $table = TableRegistry::get('NumberTrees');
  338. $table->addBehavior('Tree');
  339. $entity = new Entity(
  340. ['name' => 'New Orphan', 'parent_id' => null],
  341. ['markNew' => true]
  342. );
  343. $expected = $table->find()->order('lft')->hydrate(false)->toArray();
  344. $this->assertSame($entity, $table->save($entity));
  345. $this->assertEquals(23, $entity->lft);
  346. $this->assertEquals(24, $entity->rght);
  347. $expected[] = $entity->toArray();
  348. $results = $table->find()->order('lft')->hydrate(false)->toArray();
  349. $this->assertEquals($expected, $results);
  350. }
  351. /**
  352. * Tests that adding a child node as a decendant of one of the roots works
  353. *
  354. * @return void
  355. */
  356. public function testAddMiddle() {
  357. $table = TableRegistry::get('NumberTrees');
  358. $table->addBehavior('Tree');
  359. $entity = new Entity(
  360. ['name' => 'laptops', 'parent_id' => 1],
  361. ['markNew' => true]
  362. );
  363. $this->assertSame($entity, $table->save($entity));
  364. $this->assertEquals(20, $entity->lft);
  365. $this->assertEquals(21, $entity->rght);
  366. $result = $table->find()->order('lft')->hydrate(false)->toArray();
  367. $table->recover();
  368. $expected = $table->find()->order('lft')->hydrate(false)->toArray();
  369. $this->assertEquals($expected, $result);
  370. }
  371. /**
  372. * Tests adding a leaf to the tree
  373. *
  374. * @return void
  375. */
  376. public function testAddLeaf() {
  377. $table = TableRegistry::get('NumberTrees');
  378. $table->addBehavior('Tree');
  379. $entity = new Entity(
  380. ['name' => 'laptops', 'parent_id' => 2],
  381. ['markNew' => true]
  382. );
  383. $this->assertSame($entity, $table->save($entity));
  384. $this->assertEquals(9, $entity->lft);
  385. $this->assertEquals(10, $entity->rght);
  386. $results = $table->find()->order('lft')->hydrate(false)->toArray();
  387. $table->recover();
  388. $expected = $table->find()->order('lft')->hydrate(false)->toArray();
  389. $this->assertEquals($expected, $results);
  390. }
  391. /**
  392. * Tests moving a subtree to the right
  393. *
  394. * @return void
  395. */
  396. public function testReParentSubTreeRight() {
  397. $table = TableRegistry::get('NumberTrees');
  398. $table->addBehavior('Tree');
  399. $entity = $table->get(2);
  400. $entity->parent_id = 6;
  401. $this->assertSame($entity, $table->save($entity));
  402. $this->assertEquals(11, $entity->lft);
  403. $this->assertEquals(18, $entity->rght);
  404. $result = $table->find()->order('lft')->hydrate(false);
  405. $expected = [1, 6, 7, 8, 9, 10, 2, 3, 4, 5, 11];
  406. $this->assertTreeNumbers($expected, $table);
  407. }
  408. /**
  409. * Tests moving a subtree to the left
  410. *
  411. * @return void
  412. */
  413. public function testReParentSubTreeLeft() {
  414. $table = TableRegistry::get('NumberTrees');
  415. $table->addBehavior('Tree');
  416. $entity = $table->get(6);
  417. $entity->parent_id = 2;
  418. $this->assertSame($entity, $table->save($entity));
  419. $this->assertEquals(9, $entity->lft);
  420. $this->assertEquals(18, $entity->rght);
  421. $result = $table->find()->order('lft')->hydrate(false)->toArray();
  422. $table->recover();
  423. $expected = $table->find()->order('lft')->hydrate(false)->toArray();
  424. $this->assertEquals($expected, $result);
  425. }
  426. /**
  427. * Test moving a leaft to the left
  428. *
  429. * @return void
  430. */
  431. public function testReParentLeafLeft() {
  432. $table = TableRegistry::get('NumberTrees');
  433. $table->addBehavior('Tree');
  434. $entity = $table->get(10);
  435. $entity->parent_id = 2;
  436. $this->assertSame($entity, $table->save($entity));
  437. $this->assertEquals(9, $entity->lft);
  438. $this->assertEquals(10, $entity->rght);
  439. $result = $table->find()->order('lft')->hydrate(false)->toArray();
  440. $table->recover();
  441. $expected = $table->find()->order('lft')->hydrate(false)->toArray();
  442. $this->assertEquals($expected, $result);
  443. }
  444. /**
  445. * Test moving a leaft to the left
  446. *
  447. * @return void
  448. */
  449. public function testReParentLeafRight() {
  450. $table = TableRegistry::get('NumberTrees');
  451. $table->addBehavior('Tree');
  452. $entity = $table->get(5);
  453. $entity->parent_id = 6;
  454. $this->assertSame($entity, $table->save($entity));
  455. $this->assertEquals(17, $entity->lft);
  456. $this->assertEquals(18, $entity->rght);
  457. $result = $table->find()->order('lft')->hydrate(false);
  458. $expected = [1, 2, 3, 4, 6, 7, 8, 9, 10, 5, 11];
  459. $this->assertTreeNumbers($expected, $table);
  460. }
  461. /**
  462. * Tests moving a subtree as a new root
  463. *
  464. * @return void
  465. */
  466. public function testRootingSubTree() {
  467. $table = TableRegistry::get('NumberTrees');
  468. $table->addBehavior('Tree');
  469. $entity = $table->get(2);
  470. $entity->parent_id = null;
  471. $this->assertSame($entity, $table->save($entity));
  472. $this->assertEquals(15, $entity->lft);
  473. $this->assertEquals(22, $entity->rght);
  474. $result = $table->find()->order('lft')->hydrate(false);
  475. $expected = [1, 6, 7, 8, 9, 10, 11, 2, 3, 4, 5];
  476. $this->assertTreeNumbers($expected, $table);
  477. }
  478. /**
  479. * Tests that trying to create a cycle throws an exception
  480. *
  481. * @expectedException RuntimeException
  482. * @expectedExceptionMessage Cannot use node "5" as parent for entity "2"
  483. * @return void
  484. */
  485. public function testReparentCycle() {
  486. $table = TableRegistry::get('NumberTrees');
  487. $table->addBehavior('Tree');
  488. $entity = $table->get(2);
  489. $entity->parent_id = 5;
  490. $table->save($entity);
  491. }
  492. /**
  493. * Tests deleting a leaf in the tree
  494. *
  495. * @return void
  496. */
  497. public function testDeleteLeaf() {
  498. $table = TableRegistry::get('NumberTrees');
  499. $table->addBehavior('Tree');
  500. $entity = $table->get(4);
  501. $this->assertTrue($table->delete($entity));
  502. $result = $table->find()->order('lft')->hydrate(false)->toArray();
  503. $table->recover();
  504. $expected = $table->find()->order('lft')->hydrate(false)->toArray();
  505. $this->assertEquals($expected, $result);
  506. }
  507. /**
  508. * Tests deleting a subtree
  509. *
  510. * @return void
  511. */
  512. public function testDeleteSubTree() {
  513. $table = TableRegistry::get('NumberTrees');
  514. $table->addBehavior('Tree');
  515. $entity = $table->get(6);
  516. $this->assertTrue($table->delete($entity));
  517. $result = $table->find()->order('lft')->hydrate(false)->toArray();
  518. $table->recover();
  519. $expected = $table->find()->order('lft')->hydrate(false)->toArray();
  520. $this->assertEquals($expected, $result);
  521. }
  522. /**
  523. * Test deleting a root node
  524. *
  525. * @return void
  526. */
  527. public function testDeleteRoot() {
  528. $table = TableRegistry::get('NumberTrees');
  529. $table->addBehavior('Tree');
  530. $entity = $table->get(1);
  531. $this->assertTrue($table->delete($entity));
  532. $result = $table->find()->order('lft')->hydrate(false)->toArray();
  533. $table->recover();
  534. $expected = $table->find()->order('lft')->hydrate(false)->toArray();
  535. $this->assertEquals($expected, $result);
  536. }
  537. /**
  538. * Tests that a leaf can be taken out of the tree and put in as a root
  539. *
  540. * @return void
  541. */
  542. public function testRemoveFromLeafFromTree() {
  543. $table = TableRegistry::get('NumberTrees');
  544. $table->addBehavior('Tree');
  545. $entity = $table->get(10);
  546. $this->assertSame($entity, $table->removeFromTree($entity));
  547. $this->assertEquals(21, $entity->lft);
  548. $this->assertEquals(22, $entity->rght);
  549. $this->assertEquals(null, $entity->parent_id);
  550. $result = $table->find()->order('lft')->hydrate(false);
  551. $expected = [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 10];
  552. $this->assertTreeNumbers($expected, $table);
  553. }
  554. /**
  555. * Test removing a middle node from a tree
  556. *
  557. * @return void
  558. */
  559. public function testRemoveMiddleNodeFromTree() {
  560. $table = TableRegistry::get('NumberTrees');
  561. $table->addBehavior('Tree');
  562. $entity = $table->get(6);
  563. $this->assertSame($entity, $table->removeFromTree($entity));
  564. $result = $table->find('threaded')->order('lft')->hydrate(false)->toArray();
  565. $this->assertEquals(21, $entity->lft);
  566. $this->assertEquals(22, $entity->rght);
  567. $this->assertEquals(null, $entity->parent_id);
  568. $result = $table->find()->order('lft')->hydrate(false);
  569. $expected = [1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 6];
  570. $this->assertTreeNumbers($expected, $table);
  571. }
  572. /**
  573. * Tests removing the root of a tree
  574. *
  575. * @return void
  576. */
  577. public function testRemoveRootFromTree() {
  578. $table = TableRegistry::get('NumberTrees');
  579. $table->addBehavior('Tree');
  580. $entity = $table->get(1);
  581. $this->assertSame($entity, $table->removeFromTree($entity));
  582. $result = $table->find('threaded')->order('lft')->hydrate(false)->toArray();
  583. $this->assertEquals(21, $entity->lft);
  584. $this->assertEquals(22, $entity->rght);
  585. $this->assertEquals(null, $entity->parent_id);
  586. $expected = [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 1];
  587. $this->assertTreeNumbers($expected, $table);
  588. }
  589. /**
  590. * Custom assertion use to verify tha a tree is returned in the expected order
  591. * and that it is still valid
  592. *
  593. * @param array $expected The list of ids in the order they are expected
  594. * @param \Cake\ORM\Table the table instance to use for comparing
  595. * @return void
  596. */
  597. public function assertTreeNumbers($expected, $table) {
  598. $result = $table->find()->order('lft')->hydrate(false);
  599. $this->assertEquals($expected, $result->extract('id')->toArray());
  600. $numbers = [];
  601. $result->each(function($v) use (&$numbers) {
  602. $numbers[] = $v['lft'];
  603. $numbers[] = $v['rght'];
  604. });
  605. sort($numbers);
  606. $this->assertEquals(range(1, 22), $numbers);
  607. }
  608. }