TreeBehaviorTest.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651
  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 moveUp() method
  140. *
  141. * @return void
  142. */
  143. public function testMoveUp() {
  144. $table = TableRegistry::get('MenuLinkTrees');
  145. $table->addBehavior('Tree', ['scope' => ['menu' => 'main-menu']]);
  146. // top level, wont move
  147. $node = $this->table->moveUp($table->get(1), 10);
  148. $this->assertEquals(['lft' => 1, 'rght' => 10], $node->extract(['lft', 'rght']));
  149. // edge cases
  150. $this->assertFalse($this->table->moveUp($table->get(1), 0));
  151. $node = $this->table->moveUp($table->get(1), -10);
  152. $this->assertEquals(['lft' => 1, 'rght' => 10], $node->extract(['lft', 'rght']));
  153. // move inner node
  154. $node = $table->moveUp($table->get(3), 1);
  155. $nodes = $table->find('children', ['for' => 1])->all();
  156. $this->assertEquals([3, 4, 5, 2], $nodes->extract('id')->toArray());
  157. $this->assertEquals(['lft' => 2, 'rght' => 7], $node->extract(['lft', 'rght']));
  158. return;
  159. }
  160. /**
  161. * Tests moving a node with no siblings
  162. *
  163. * @return void
  164. */
  165. public function testMoveLeaf() {
  166. $table = TableRegistry::get('MenuLinkTrees');
  167. $table->addBehavior('Tree', ['scope' => ['menu' => 'main-menu']]);
  168. $node = $table->moveUp($table->get(5), 1);
  169. $this->assertEquals(['lft' => 6, 'rght' => 7], $node->extract(['lft', 'rght']));
  170. }
  171. /**
  172. * Tests moving a node to the top
  173. *
  174. * @return void
  175. */
  176. public function testMoveTop() {
  177. $table = TableRegistry::get('MenuLinkTrees');
  178. $table->addBehavior('Tree', ['scope' => ['menu' => 'main-menu']]);
  179. $node = $table->moveUp($table->get(8), true);
  180. $this->assertEquals(['lft' => 1, 'rght' => 2], $node->extract(['lft', 'rght']));
  181. $nodes = $table->find()
  182. ->select(['id'])
  183. ->where(function($exp) {
  184. return $exp->isNull('parent_id');
  185. })
  186. ->where(['menu' => 'main-menu'])
  187. ->order(['lft' => 'ASC'])
  188. ->all();
  189. $this->assertEquals([8, 1, 6], $nodes->extract('id')->toArray());
  190. }
  191. /**
  192. * Tests the moveDown() method
  193. *
  194. * @return void
  195. */
  196. public function testMoveDown() {
  197. $table = TableRegistry::get('MenuLinkTrees');
  198. $table->addBehavior('Tree', ['scope' => ['menu' => 'main-menu']]);
  199. // latest node, wont move
  200. $node = $this->table->moveDown($table->get(8), 10);
  201. $this->assertEquals(['lft' => 21, 'rght' => 22], $node->extract(['lft', 'rght']));
  202. // edge cases
  203. $this->assertFalse($this->table->moveDown($table->get(8), 0));
  204. // move inner node
  205. $node = $table->moveDown($table->get(2), 1);
  206. $nodes = $table->find('children', ['for' => 1])->all();
  207. $this->assertEquals([3, 4, 5, 2], $nodes->extract('id')->toArray());
  208. $this->assertEquals(['lft' => 11, 'rght' => 12], $node->extract(['lft', 'rght']));
  209. }
  210. /**
  211. * Tests moving a node that has no siblings
  212. *
  213. * @return void
  214. */
  215. public function testMoveLeafDown() {
  216. $table = TableRegistry::get('MenuLinkTrees');
  217. $table->addBehavior('Tree', ['scope' => ['menu' => 'main-menu']]);
  218. $node = $table->moveDown($table->get(5), 1);
  219. $this->assertEquals(['lft' => 6, 'rght' => 7], $node->extract(['lft', 'rght']));
  220. }
  221. /**
  222. * Tests moving a node to the bottom
  223. *
  224. * @return void
  225. */
  226. public function testMoveToBottom() {
  227. $table = TableRegistry::get('MenuLinkTrees');
  228. $table->addBehavior('Tree', ['scope' => ['menu' => 'main-menu']]);
  229. $node = $table->moveDown($table->get(1), true);
  230. $this->assertEquals(['lft' => 7, 'rght' => 16], $node->extract(['lft', 'rght']));
  231. $nodes = $table->find()
  232. ->select(['id'])
  233. ->where(function($exp) {
  234. return $exp->isNull('parent_id');
  235. })
  236. ->where(['menu' => 'main-menu'])
  237. ->order(['lft' => 'ASC'])
  238. ->all();
  239. $this->assertEquals([6, 8, 1], $nodes->extract('id')->toArray());
  240. }
  241. /**
  242. * Tests the recover function
  243. *
  244. * @return void
  245. */
  246. public function testRecover() {
  247. $table = TableRegistry::get('NumberTrees');
  248. $table->addBehavior('Tree');
  249. $expected = $table->find()->order('lft')->hydrate(false)->toArray();
  250. $table->updateAll(['lft' => null, 'rght' => null], []);
  251. $table->recover();
  252. $result = $table->find()->order('lft')->hydrate(false)->toArray();
  253. $this->assertEquals($expected, $result);
  254. }
  255. /**
  256. * Tests the recover function with a custom scope
  257. *
  258. * @return void
  259. */
  260. public function testRecoverScoped() {
  261. $table = TableRegistry::get('MenuLinkTrees');
  262. $table->addBehavior('Tree', ['scope' => ['menu' => 'main-menu']]);
  263. $expected = $table->find()
  264. ->where(['menu' => 'main-menu'])
  265. ->order('lft')
  266. ->hydrate(false)
  267. ->toArray();
  268. $expected2 = $table->find()
  269. ->where(['menu' => 'categories'])
  270. ->order('lft')
  271. ->hydrate(false)
  272. ->toArray();
  273. $table->updateAll(['lft' => null, 'rght' => null], ['menu' => 'main-menu']);
  274. $table->recover();
  275. $result = $table->find()
  276. ->where(['menu' => 'main-menu'])
  277. ->order('lft')
  278. ->hydrate(false)
  279. ->toArray();
  280. $this->assertEquals($expected, $result);
  281. $result2 = $table->find()
  282. ->where(['menu' => 'categories'])
  283. ->order('lft')
  284. ->hydrate(false)
  285. ->toArray();
  286. $this->assertEquals($expected2, $result2);
  287. }
  288. /**
  289. * Tests adding a new orphan node
  290. *
  291. * @return void
  292. */
  293. public function testAddOrphan() {
  294. $table = TableRegistry::get('NumberTrees');
  295. $table->addBehavior('Tree');
  296. $entity = new Entity(
  297. ['name' => 'New Orphan', 'parent_id' => null],
  298. ['markNew' => true]
  299. );
  300. $expected = $table->find()->order('lft')->hydrate(false)->toArray();
  301. $this->assertSame($entity, $table->save($entity));
  302. $this->assertEquals(23, $entity->lft);
  303. $this->assertEquals(24, $entity->rght);
  304. $expected[] = $entity->toArray();
  305. $results = $table->find()->order('lft')->hydrate(false)->toArray();
  306. $this->assertEquals($expected, $results);
  307. }
  308. /**
  309. * Tests that adding a child node as a decendant of one of the roots works
  310. *
  311. * @return void
  312. */
  313. public function testAddMiddle() {
  314. $table = TableRegistry::get('NumberTrees');
  315. $table->addBehavior('Tree');
  316. $entity = new Entity(
  317. ['name' => 'laptops', 'parent_id' => 1],
  318. ['markNew' => true]
  319. );
  320. $this->assertSame($entity, $table->save($entity));
  321. $this->assertEquals(20, $entity->lft);
  322. $this->assertEquals(21, $entity->rght);
  323. $result = $table->find()->order('lft')->hydrate(false)->toArray();
  324. $table->recover();
  325. $expected = $table->find()->order('lft')->hydrate(false)->toArray();
  326. $this->assertEquals($expected, $result);
  327. }
  328. /**
  329. * Tests adding a leaf to the tree
  330. *
  331. * @return void
  332. */
  333. public function testAddLeaf() {
  334. $table = TableRegistry::get('NumberTrees');
  335. $table->addBehavior('Tree');
  336. $entity = new Entity(
  337. ['name' => 'laptops', 'parent_id' => 2],
  338. ['markNew' => true]
  339. );
  340. $this->assertSame($entity, $table->save($entity));
  341. $this->assertEquals(9, $entity->lft);
  342. $this->assertEquals(10, $entity->rght);
  343. $results = $table->find()->order('lft')->hydrate(false)->toArray();
  344. $table->recover();
  345. $expected = $table->find()->order('lft')->hydrate(false)->toArray();
  346. $this->assertEquals($expected, $results);
  347. }
  348. /**
  349. * Tests moving a subtree to the right
  350. *
  351. * @return void
  352. */
  353. public function testReParentSubTreeRight() {
  354. $table = TableRegistry::get('NumberTrees');
  355. $table->addBehavior('Tree');
  356. $entity = $table->get(2);
  357. $entity->parent_id = 6;
  358. $this->assertSame($entity, $table->save($entity));
  359. $this->assertEquals(11, $entity->lft);
  360. $this->assertEquals(18, $entity->rght);
  361. $result = $table->find()->order('lft')->hydrate(false);
  362. $expected = [1, 6, 7, 8, 9, 10, 2, 3, 4, 5, 11];
  363. $this->assertEquals($expected, $result->extract('id')->toArray());
  364. $numbers = [];
  365. $result->each(function($v) use (&$numbers) {
  366. $numbers[] = $v['lft'];
  367. $numbers[] = $v['rght'];
  368. });
  369. sort($numbers);
  370. $this->assertEquals(range(1, 22), $numbers);
  371. }
  372. /**
  373. * Tests moving a subtree to the left
  374. *
  375. * @return void
  376. */
  377. public function testReParentSubTreeLeft() {
  378. $table = TableRegistry::get('NumberTrees');
  379. $table->addBehavior('Tree');
  380. $entity = $table->get(6);
  381. $entity->parent_id = 2;
  382. $this->assertSame($entity, $table->save($entity));
  383. $this->assertEquals(9, $entity->lft);
  384. $this->assertEquals(18, $entity->rght);
  385. $result = $table->find()->order('lft')->hydrate(false)->toArray();
  386. $table->recover();
  387. $expected = $table->find()->order('lft')->hydrate(false)->toArray();
  388. $this->assertEquals($expected, $result);
  389. }
  390. /**
  391. * Test moving a leaft to the left
  392. *
  393. * @return void
  394. */
  395. public function testReParentLeafLeft() {
  396. $table = TableRegistry::get('NumberTrees');
  397. $table->addBehavior('Tree');
  398. $entity = $table->get(10);
  399. $entity->parent_id = 2;
  400. $this->assertSame($entity, $table->save($entity));
  401. $this->assertEquals(9, $entity->lft);
  402. $this->assertEquals(10, $entity->rght);
  403. $result = $table->find()->order('lft')->hydrate(false)->toArray();
  404. $table->recover();
  405. $expected = $table->find()->order('lft')->hydrate(false)->toArray();
  406. $this->assertEquals($expected, $result);
  407. }
  408. /**
  409. * Test moving a leaft to the left
  410. *
  411. * @return void
  412. */
  413. public function testReParentLeafRight() {
  414. $table = TableRegistry::get('NumberTrees');
  415. $table->addBehavior('Tree');
  416. $entity = $table->get(5);
  417. $entity->parent_id = 6;
  418. $this->assertSame($entity, $table->save($entity));
  419. $this->assertEquals(17, $entity->lft);
  420. $this->assertEquals(18, $entity->rght);
  421. $result = $table->find()->order('lft')->hydrate(false);
  422. $expected = [1, 2, 3, 4, 6, 7, 8, 9, 10, 5, 11];
  423. $this->assertEquals($expected, $result->extract('id')->toArray());
  424. $numbers = [];
  425. $result->each(function($v) use (&$numbers) {
  426. $numbers[] = $v['lft'];
  427. $numbers[] = $v['rght'];
  428. });
  429. sort($numbers);
  430. $this->assertEquals(range(1, 22), $numbers);
  431. }
  432. /**
  433. * Tests moving a subtree as a new root
  434. *
  435. * @return void
  436. */
  437. public function testRootingSubTree() {
  438. $table = TableRegistry::get('NumberTrees');
  439. $table->addBehavior('Tree');
  440. $entity = $table->get(2);
  441. $entity->parent_id = null;
  442. $this->assertSame($entity, $table->save($entity));
  443. $this->assertEquals(15, $entity->lft);
  444. $this->assertEquals(22, $entity->rght);
  445. $result = $table->find()->order('lft')->hydrate(false);
  446. $expected = [1, 6, 7, 8, 9, 10, 11, 2, 3, 4, 5];
  447. $this->assertEquals($expected, $result->extract('id')->toArray());
  448. $numbers = [];
  449. $result->each(function($v) use (&$numbers) {
  450. $numbers[] = $v['lft'];
  451. $numbers[] = $v['rght'];
  452. });
  453. sort($numbers);
  454. $this->assertEquals(range(1, 22), $numbers);
  455. }
  456. /**
  457. * Tests that trying to create a cycle throws an exception
  458. *
  459. * @expectedException RuntimeException
  460. * @expectedExceptionMessage Cannot use node "5" as parent for entity "2"
  461. * @return void
  462. */
  463. public function testReparentCycle() {
  464. $table = TableRegistry::get('NumberTrees');
  465. $table->addBehavior('Tree');
  466. $entity = $table->get(2);
  467. $entity->parent_id = 5;
  468. $table->save($entity);
  469. }
  470. /**
  471. * Tests deleting a leaf in the tree
  472. *
  473. * @return void
  474. */
  475. public function testDeleteLeaf() {
  476. $table = TableRegistry::get('NumberTrees');
  477. $table->addBehavior('Tree');
  478. $entity = $table->get(4);
  479. $this->assertTrue($table->delete($entity));
  480. $result = $table->find()->order('lft')->hydrate(false)->toArray();
  481. $table->recover();
  482. $expected = $table->find()->order('lft')->hydrate(false)->toArray();
  483. $this->assertEquals($expected, $result);
  484. }
  485. /**
  486. * Tests deleting a subtree
  487. *
  488. * @return void
  489. */
  490. public function testDeleteSubTree() {
  491. $table = TableRegistry::get('NumberTrees');
  492. $table->addBehavior('Tree');
  493. $entity = $table->get(6);
  494. $this->assertTrue($table->delete($entity));
  495. $result = $table->find()->order('lft')->hydrate(false)->toArray();
  496. $table->recover();
  497. $expected = $table->find()->order('lft')->hydrate(false)->toArray();
  498. $this->assertEquals($expected, $result);
  499. }
  500. /**
  501. * Test deleting a root node
  502. *
  503. * @return void
  504. */
  505. public function testDeleteRoot() {
  506. $table = TableRegistry::get('NumberTrees');
  507. $table->addBehavior('Tree');
  508. $entity = $table->get(1);
  509. $this->assertTrue($table->delete($entity));
  510. $result = $table->find()->order('lft')->hydrate(false)->toArray();
  511. $table->recover();
  512. $expected = $table->find()->order('lft')->hydrate(false)->toArray();
  513. $this->assertEquals($expected, $result);
  514. }
  515. /**
  516. * Tests that a leaf can be taken out of the tree and put in as a root
  517. *
  518. * @return void
  519. */
  520. public function testRemoveFromLeafFromTree() {
  521. $table = TableRegistry::get('NumberTrees');
  522. $table->addBehavior('Tree');
  523. $entity = $table->get(10);
  524. $this->assertSame($entity, $table->removeFromTree($entity));
  525. $this->assertEquals(21, $entity->lft);
  526. $this->assertEquals(22, $entity->rght);
  527. $this->assertEquals(null, $entity->parent_id);
  528. $result = $table->find()->order('lft')->hydrate(false);
  529. $expected = [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 10];
  530. $this->assertEquals($expected, $result->extract('id')->toArray());
  531. $numbers = [];
  532. $result->each(function($v) use (&$numbers) {
  533. $numbers[] = $v['lft'];
  534. $numbers[] = $v['rght'];
  535. });
  536. sort($numbers);
  537. $this->assertEquals(range(1, 22), $numbers);
  538. }
  539. /**
  540. * Test removing a middle node from a tree
  541. *
  542. * @return void
  543. */
  544. public function testRemoveMiddleNodeFromTree() {
  545. $table = TableRegistry::get('NumberTrees');
  546. $table->addBehavior('Tree');
  547. $entity = $table->get(6);
  548. $this->assertSame($entity, $table->removeFromTree($entity));
  549. $result = $table->find('threaded')->order('lft')->hydrate(false)->toArray();
  550. $this->assertEquals(21, $entity->lft);
  551. $this->assertEquals(22, $entity->rght);
  552. $this->assertEquals(null, $entity->parent_id);
  553. $result = $table->find()->order('lft')->hydrate(false);
  554. $expected = [1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 6];
  555. $this->assertEquals($expected, $result->extract('id')->toArray());
  556. $numbers = [];
  557. $result->each(function($v) use (&$numbers) {
  558. $numbers[] = $v['lft'];
  559. $numbers[] = $v['rght'];
  560. });
  561. sort($numbers);
  562. $this->assertEquals(range(1, 22), $numbers);
  563. }
  564. /**
  565. * Tests removing the root of a tree
  566. *
  567. * @return void
  568. */
  569. public function testRemoveRootFromTree() {
  570. $table = TableRegistry::get('NumberTrees');
  571. $table->addBehavior('Tree');
  572. $entity = $table->get(1);
  573. $this->assertSame($entity, $table->removeFromTree($entity));
  574. $result = $table->find('threaded')->order('lft')->hydrate(false)->toArray();
  575. $this->assertEquals(21, $entity->lft);
  576. $this->assertEquals(22, $entity->rght);
  577. $this->assertEquals(null, $entity->parent_id);
  578. $result = $table->find()->order('lft')->hydrate(false);
  579. $expected = [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 1];
  580. $this->assertEquals($expected, $result->extract('id')->toArray());
  581. $numbers = [];
  582. $result->each(function($v) use (&$numbers) {
  583. $numbers[] = $v['lft'];
  584. $numbers[] = $v['rght'];
  585. });
  586. sort($numbers);
  587. $this->assertEquals(range(1, 22), $numbers);
  588. }
  589. }