TreeBehaviorTest.php 19 KB

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