TreeBehaviorTest.php 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411
  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 3.0.0
  13. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Test\TestCase\ORM\Behavior;
  16. use Cake\Collection\Collection;
  17. use Cake\Event\Event;
  18. use Cake\ORM\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. /**
  28. * fixtures
  29. *
  30. * @var array
  31. */
  32. public $fixtures = [
  33. 'core.menu_link_trees',
  34. 'core.number_trees'
  35. ];
  36. public function setUp()
  37. {
  38. parent::setUp();
  39. $this->table = TableRegistry::get('NumberTrees');
  40. $this->table->primaryKey(['id']);
  41. $this->table->addBehavior('Tree');
  42. }
  43. public function tearDown()
  44. {
  45. parent::tearDown();
  46. TableRegistry::clear();
  47. }
  48. /**
  49. * Sanity test
  50. *
  51. * Make sure the assert method acts as you'd expect, this is the expected
  52. * initial db state
  53. *
  54. * @return void
  55. */
  56. public function testAssertMpttValues()
  57. {
  58. $expected = [
  59. ' 1:20 - 1:electronics',
  60. '_ 2: 9 - 2:televisions',
  61. '__ 3: 4 - 3:tube',
  62. '__ 5: 6 - 4:lcd',
  63. '__ 7: 8 - 5:plasma',
  64. '_10:19 - 6:portable',
  65. '__11:14 - 7:mp3',
  66. '___12:13 - 8:flash',
  67. '__15:16 - 9:cd',
  68. '__17:18 - 10:radios',
  69. '21:22 - 11:alien hardware'
  70. ];
  71. $this->assertMpttValues($expected, $this->table);
  72. $table = TableRegistry::get('MenuLinkTrees');
  73. $table->addBehavior('Tree', ['scope' => ['menu' => 'main-menu']]);
  74. $expected = [
  75. ' 1:10 - 1:Link 1',
  76. '_ 2: 3 - 2:Link 2',
  77. '_ 4: 9 - 3:Link 3',
  78. '__ 5: 8 - 4:Link 4',
  79. '___ 6: 7 - 5:Link 5',
  80. '11:14 - 6:Link 6',
  81. '_12:13 - 7:Link 7',
  82. '15:16 - 8:Link 8'
  83. ];
  84. $this->assertMpttValues($expected, $table);
  85. $table->removeBehavior('Tree');
  86. $table->addBehavior('Tree', ['scope' => ['menu' => 'categories']]);
  87. $expected = [
  88. ' 1:10 - 9:electronics',
  89. '_ 2: 9 - 10:televisions',
  90. '__ 3: 4 - 11:tube',
  91. '__ 5: 8 - 12:lcd',
  92. '___ 6: 7 - 13:plasma',
  93. '11:20 - 14:portable',
  94. '_12:15 - 15:mp3',
  95. '__13:14 - 16:flash',
  96. '_16:17 - 17:cd',
  97. '_18:19 - 18:radios'
  98. ];
  99. $this->assertMpttValues($expected, $table);
  100. }
  101. /**
  102. * Tests the find('path') method
  103. *
  104. * @return void
  105. */
  106. public function testFindPath()
  107. {
  108. $nodes = $this->table->find('path', ['for' => 9]);
  109. $this->assertEquals([1, 6, 9], $nodes->extract('id')->toArray());
  110. $nodes = $this->table->find('path', ['for' => 10]);
  111. $this->assertSame([1, 6, 10], $nodes->extract('id')->toArray());
  112. $nodes = $this->table->find('path', ['for' => 5]);
  113. $this->assertSame([1, 2, 5], $nodes->extract('id')->toArray());
  114. $nodes = $this->table->find('path', ['for' => 1]);
  115. $this->assertSame([1], $nodes->extract('id')->toArray());
  116. $entity = $this->table->newEntity(['name' => 'odd one', 'parent_id' => 1]);
  117. $entity = $this->table->save($entity);
  118. $newId = $entity->id;
  119. $entity = $this->table->get(2);
  120. $entity->parent_id = $newId;
  121. $this->table->save($entity);
  122. $nodes = $this->table->find('path', ['for' => 4]);
  123. $this->assertSame([1, $newId, 2, 4], $nodes->extract('id')->toArray());
  124. // find path with scope
  125. $table = TableRegistry::get('MenuLinkTrees');
  126. $table->addBehavior('Tree', ['scope' => ['menu' => 'main-menu']]);
  127. $nodes = $table->find('path', ['for' => 5]);
  128. $this->assertSame([1, 3, 4, 5], $nodes->extract('id')->toArray());
  129. }
  130. /**
  131. * Tests the childCount() method
  132. *
  133. * @return void
  134. */
  135. public function testChildCount()
  136. {
  137. // direct children for the root node
  138. $table = $this->table;
  139. $countDirect = $this->table->childCount($table->get(1), true);
  140. $this->assertEquals(2, $countDirect);
  141. // counts all the children of root
  142. $count = $this->table->childCount($table->get(1), false);
  143. $this->assertEquals(9, $count);
  144. // counts direct children
  145. $count = $this->table->childCount($table->get(2), false);
  146. $this->assertEquals(3, $count);
  147. // count children for a middle-node
  148. $count = $this->table->childCount($table->get(6), false);
  149. $this->assertEquals(4, $count);
  150. // count leaf children
  151. $count = $this->table->childCount($table->get(10), false);
  152. $this->assertEquals(0, $count);
  153. // test scoping
  154. $table = TableRegistry::get('MenuLinkTrees');
  155. $table->addBehavior('Tree', ['scope' => ['menu' => 'main-menu']]);
  156. $count = $table->childCount($table->get(3), false);
  157. $this->assertEquals(2, $count);
  158. }
  159. /**
  160. * Tests that childCount will provide the correct lft and rght values
  161. *
  162. * @return void
  163. */
  164. public function testChildCountNoTreeColumns()
  165. {
  166. $table = $this->table;
  167. $node = $table->get(6);
  168. $node->unsetProperty('lft');
  169. $node->unsetProperty('rght');
  170. $count = $this->table->childCount($node, false);
  171. $this->assertEquals(4, $count);
  172. }
  173. /**
  174. * Tests the childCount() plus callable scoping
  175. *
  176. * @return void
  177. */
  178. public function testCallableScoping()
  179. {
  180. $table = TableRegistry::get('MenuLinkTrees');
  181. $table->addBehavior('Tree', [
  182. 'scope' => function ($query) {
  183. return $query->where(['menu' => 'main-menu']);
  184. }
  185. ]);
  186. $count = $table->childCount($table->get(1), false);
  187. $this->assertEquals(4, $count);
  188. }
  189. /**
  190. * Tests the find('children') method
  191. *
  192. * @return void
  193. */
  194. public function testFindChildren()
  195. {
  196. $table = TableRegistry::get('MenuLinkTrees');
  197. $table->addBehavior('Tree', ['scope' => ['menu' => 'main-menu']]);
  198. // root
  199. $nodeIds = [];
  200. $nodes = $table->find('children', ['for' => 1])->all();
  201. $this->assertEquals([2, 3, 4, 5], $nodes->extract('id')->toArray());
  202. // leaf
  203. $nodeIds = [];
  204. $nodes = $table->find('children', ['for' => 5])->all();
  205. $this->assertEquals(0, count($nodes->extract('id')->toArray()));
  206. // direct children
  207. $nodes = $table->find('children', ['for' => 1, 'direct' => true])->all();
  208. $this->assertEquals([2, 3], $nodes->extract('id')->toArray());
  209. }
  210. /**
  211. * Tests that find('children') will throw an exception if the node was not found
  212. *
  213. * @expectedException \Cake\Datasource\Exception\RecordNotFoundException
  214. * @return void
  215. */
  216. public function testFindChildrenException()
  217. {
  218. $table = TableRegistry::get('MenuLinkTrees');
  219. $table->addBehavior('Tree', ['scope' => ['menu' => 'main-menu']]);
  220. $query = $table->find('children', ['for' => 500]);
  221. }
  222. /**
  223. * Tests the find('treeList') method
  224. *
  225. * @return void
  226. */
  227. public function testFindTreeList()
  228. {
  229. $table = TableRegistry::get('MenuLinkTrees');
  230. $table->addBehavior('Tree', ['scope' => ['menu' => 'main-menu']]);
  231. $result = $table->find('treeList')->toArray();
  232. $expected = [
  233. 1 => 'Link 1',
  234. 2 => '_Link 2',
  235. 3 => '_Link 3',
  236. 4 => '__Link 4',
  237. 5 => '___Link 5',
  238. 6 => 'Link 6',
  239. 7 => '_Link 7',
  240. 8 => 'Link 8'
  241. ];
  242. $this->assertEquals($expected, $result);
  243. }
  244. /**
  245. * Tests the find('treeList') method after moveUp, moveDown
  246. *
  247. * @return void
  248. */
  249. public function testFindTreeListAfterMove()
  250. {
  251. $table = TableRegistry::get('MenuLinkTrees');
  252. $table->addBehavior('Tree', ['scope' => ['menu' => 'main-menu']]);
  253. // moveUp
  254. $table->moveUp($table->get(3), 1);
  255. $expected = [
  256. ' 1:10 - 1:Link 1',
  257. '_ 2: 7 - 3:Link 3',
  258. '__ 3: 6 - 4:Link 4',
  259. '___ 4: 5 - 5:Link 5',
  260. '_ 8: 9 - 2:Link 2',
  261. '11:14 - 6:Link 6',
  262. '_12:13 - 7:Link 7',
  263. '15:16 - 8:Link 8'
  264. ];
  265. $this->assertMpttValues($expected, $table);
  266. // moveDown
  267. $table->moveDown($table->get(6), 1);
  268. $expected = [
  269. ' 1:10 - 1:Link 1',
  270. '_ 2: 7 - 3:Link 3',
  271. '__ 3: 6 - 4:Link 4',
  272. '___ 4: 5 - 5:Link 5',
  273. '_ 8: 9 - 2:Link 2',
  274. '11:12 - 8:Link 8',
  275. '13:16 - 6:Link 6',
  276. '_14:15 - 7:Link 7'
  277. ];
  278. $this->assertMpttValues($expected, $table);
  279. }
  280. /**
  281. * Tests the find('treeList') method with custom options
  282. *
  283. * @return void
  284. */
  285. public function testFindTreeListCustom()
  286. {
  287. $table = TableRegistry::get('MenuLinkTrees');
  288. $table->addBehavior('Tree', ['scope' => ['menu' => 'main-menu']]);
  289. $result = $table
  290. ->find('treeList', ['keyPath' => 'url', 'valuePath' => 'id', 'spacer' => ' '])
  291. ->toArray();
  292. $expected = [
  293. '/link1.html' => '1',
  294. 'http://example.com' => ' 2',
  295. '/what/even-more-links.html' => ' 3',
  296. '/lorem/ipsum.html' => ' 4',
  297. '/what/the.html' => ' 5',
  298. '/yeah/another-link.html' => '6',
  299. 'http://cakephp.org' => ' 7',
  300. '/page/who-we-are.html' => '8'
  301. ];
  302. $this->assertEquals($expected, $result);
  303. }
  304. /**
  305. * Tests the testFormatTreeListCustom() method.
  306. *
  307. * @return void
  308. */
  309. public function testFormatTreeListCustom()
  310. {
  311. $table = TableRegistry::get('MenuLinkTrees');
  312. $table->addBehavior('Tree');
  313. $query = $table
  314. ->find('threaded')
  315. ->where(['menu' => 'main-menu']);
  316. $options = ['keyPath' => 'url', 'valuePath' => 'id', 'spacer' => ' '];
  317. $result = $table->formatTreeList($query, $options)->toArray();
  318. $expected = [
  319. '/link1.html' => '1',
  320. 'http://example.com' => ' 2',
  321. '/what/even-more-links.html' => ' 3',
  322. '/lorem/ipsum.html' => ' 4',
  323. '/what/the.html' => ' 5',
  324. '/yeah/another-link.html' => '6',
  325. 'http://cakephp.org' => ' 7',
  326. '/page/who-we-are.html' => '8'
  327. ];
  328. $this->assertEquals($expected, $result);
  329. }
  330. /**
  331. * Tests the moveUp() method
  332. *
  333. * @return void
  334. */
  335. public function testMoveUp()
  336. {
  337. $table = TableRegistry::get('MenuLinkTrees');
  338. $table->addBehavior('Tree', ['scope' => ['menu' => 'main-menu']]);
  339. // top level, won't move
  340. $node = $this->table->moveUp($table->get(1), 10);
  341. $this->assertEquals(['lft' => 1, 'rght' => 10], $node->extract(['lft', 'rght']));
  342. $expected = [
  343. ' 1:10 - 1:Link 1',
  344. '_ 2: 3 - 2:Link 2',
  345. '_ 4: 9 - 3:Link 3',
  346. '__ 5: 8 - 4:Link 4',
  347. '___ 6: 7 - 5:Link 5',
  348. '11:14 - 6:Link 6',
  349. '_12:13 - 7:Link 7',
  350. '15:16 - 8:Link 8'
  351. ];
  352. $this->assertMpttValues($expected, $table);
  353. // edge cases
  354. $this->assertFalse($this->table->moveUp($table->get(1), 0));
  355. $this->assertFalse($this->table->moveUp($table->get(1), -10));
  356. $expected = [
  357. ' 1:10 - 1:Link 1',
  358. '_ 2: 3 - 2:Link 2',
  359. '_ 4: 9 - 3:Link 3',
  360. '__ 5: 8 - 4:Link 4',
  361. '___ 6: 7 - 5:Link 5',
  362. '11:14 - 6:Link 6',
  363. '_12:13 - 7:Link 7',
  364. '15:16 - 8:Link 8'
  365. ];
  366. $this->assertMpttValues($expected, $table);
  367. // move inner node
  368. $node = $table->moveUp($table->get(3), 1);
  369. $nodes = $table->find('children', ['for' => 1])->all();
  370. $this->assertEquals(['lft' => 2, 'rght' => 7], $node->extract(['lft', 'rght']));
  371. $expected = [
  372. ' 1:10 - 1:Link 1',
  373. '_ 2: 7 - 3:Link 3',
  374. '__ 3: 6 - 4:Link 4',
  375. '___ 4: 5 - 5:Link 5',
  376. '_ 8: 9 - 2:Link 2',
  377. '11:14 - 6:Link 6',
  378. '_12:13 - 7:Link 7',
  379. '15:16 - 8:Link 8'
  380. ];
  381. $this->assertMpttValues($expected, $table);
  382. }
  383. /**
  384. * Tests moving a node with no siblings
  385. *
  386. * @return void
  387. */
  388. public function testMoveLeaf()
  389. {
  390. $table = TableRegistry::get('MenuLinkTrees');
  391. $table->addBehavior('Tree', ['scope' => ['menu' => 'main-menu']]);
  392. $node = $table->moveUp($table->get(5), 1);
  393. $this->assertEquals(['lft' => 6, 'rght' => 7], $node->extract(['lft', 'rght']));
  394. $expected = [
  395. ' 1:10 - 1:Link 1',
  396. '_ 2: 3 - 2:Link 2',
  397. '_ 4: 9 - 3:Link 3',
  398. '__ 5: 8 - 4:Link 4',
  399. '___ 6: 7 - 5:Link 5',
  400. '11:14 - 6:Link 6',
  401. '_12:13 - 7:Link 7',
  402. '15:16 - 8:Link 8'
  403. ];
  404. $this->assertMpttValues($expected, $table);
  405. }
  406. /**
  407. * Tests moving a node to the top
  408. *
  409. * @return void
  410. */
  411. public function testMoveTop()
  412. {
  413. $table = TableRegistry::get('MenuLinkTrees');
  414. $table->addBehavior('Tree', ['scope' => ['menu' => 'main-menu']]);
  415. $node = $table->moveUp($table->get(8), true);
  416. $expected = [
  417. ' 1: 2 - 8:Link 8',
  418. ' 3:12 - 1:Link 1',
  419. '_ 4: 5 - 2:Link 2',
  420. '_ 6:11 - 3:Link 3',
  421. '__ 7:10 - 4:Link 4',
  422. '___ 8: 9 - 5:Link 5',
  423. '13:16 - 6:Link 6',
  424. '_14:15 - 7:Link 7'
  425. ];
  426. $this->assertMpttValues($expected, $table);
  427. }
  428. /**
  429. * Tests moving a node with no lft and rght
  430. *
  431. * @return void
  432. */
  433. public function testMoveNoTreeColumns()
  434. {
  435. $table = TableRegistry::get('MenuLinkTrees');
  436. $table->addBehavior('Tree', ['scope' => ['menu' => 'main-menu']]);
  437. $node = $table->get(8);
  438. $node->unsetProperty('lft');
  439. $node->unsetProperty('rght');
  440. $node = $table->moveUp($node, true);
  441. $this->assertEquals(['lft' => 1, 'rght' => 2], $node->extract(['lft', 'rght']));
  442. $expected = [
  443. ' 1: 2 - 8:Link 8',
  444. ' 3:12 - 1:Link 1',
  445. '_ 4: 5 - 2:Link 2',
  446. '_ 6:11 - 3:Link 3',
  447. '__ 7:10 - 4:Link 4',
  448. '___ 8: 9 - 5:Link 5',
  449. '13:16 - 6:Link 6',
  450. '_14:15 - 7:Link 7'
  451. ];
  452. $this->assertMpttValues($expected, $table);
  453. }
  454. /**
  455. * Tests the moveDown() method
  456. *
  457. * @return void
  458. */
  459. public function testMoveDown()
  460. {
  461. $table = TableRegistry::get('MenuLinkTrees');
  462. $table->addBehavior('Tree', ['scope' => ['menu' => 'main-menu']]);
  463. // latest node, won't move
  464. $node = $table->moveDown($table->get(8), 10);
  465. $this->assertEquals(['lft' => 15, 'rght' => 16], $node->extract(['lft', 'rght']));
  466. $expected = [
  467. ' 1:10 - 1:Link 1',
  468. '_ 2: 3 - 2:Link 2',
  469. '_ 4: 9 - 3:Link 3',
  470. '__ 5: 8 - 4:Link 4',
  471. '___ 6: 7 - 5:Link 5',
  472. '11:14 - 6:Link 6',
  473. '_12:13 - 7:Link 7',
  474. '15:16 - 8:Link 8'
  475. ];
  476. $this->assertMpttValues($expected, $table);
  477. // edge cases
  478. $this->assertFalse($this->table->moveDown($table->get(8), 0));
  479. $this->assertFalse($this->table->moveDown($table->get(8), -10));
  480. $expected = [
  481. ' 1:10 - 1:Link 1',
  482. '_ 2: 3 - 2:Link 2',
  483. '_ 4: 9 - 3:Link 3',
  484. '__ 5: 8 - 4:Link 4',
  485. '___ 6: 7 - 5:Link 5',
  486. '11:14 - 6:Link 6',
  487. '_12:13 - 7:Link 7',
  488. '15:16 - 8:Link 8'
  489. ];
  490. $this->assertMpttValues($expected, $table);
  491. // move inner node
  492. $node = $table->moveDown($table->get(2), 1);
  493. $this->assertEquals(['lft' => 8, 'rght' => 9], $node->extract(['lft', 'rght']));
  494. $expected = [
  495. ' 1:10 - 1:Link 1',
  496. '_ 2: 7 - 3:Link 3',
  497. '__ 3: 6 - 4:Link 4',
  498. '___ 4: 5 - 5:Link 5',
  499. '_ 8: 9 - 2:Link 2',
  500. '11:14 - 6:Link 6',
  501. '_12:13 - 7:Link 7',
  502. '15:16 - 8:Link 8'
  503. ];
  504. $this->assertMpttValues($expected, $table);
  505. }
  506. /**
  507. * Tests moving a node that has no siblings
  508. *
  509. * @return void
  510. */
  511. public function testMoveLeafDown()
  512. {
  513. $table = TableRegistry::get('MenuLinkTrees');
  514. $table->addBehavior('Tree', ['scope' => ['menu' => 'main-menu']]);
  515. $node = $table->moveDown($table->get(5), 1);
  516. $this->assertEquals(['lft' => 6, 'rght' => 7], $node->extract(['lft', 'rght']));
  517. $expected = [
  518. ' 1:10 - 1:Link 1',
  519. '_ 2: 3 - 2:Link 2',
  520. '_ 4: 9 - 3:Link 3',
  521. '__ 5: 8 - 4:Link 4',
  522. '___ 6: 7 - 5:Link 5',
  523. '11:14 - 6:Link 6',
  524. '_12:13 - 7:Link 7',
  525. '15:16 - 8:Link 8'
  526. ];
  527. $this->assertMpttValues($expected, $table);
  528. }
  529. /**
  530. * Tests moving a node to the bottom
  531. *
  532. * @return void
  533. */
  534. public function testMoveToBottom()
  535. {
  536. $table = TableRegistry::get('MenuLinkTrees');
  537. $table->addBehavior('Tree', ['scope' => ['menu' => 'main-menu']]);
  538. $node = $table->moveDown($table->get(1), true);
  539. $this->assertEquals(['lft' => 7, 'rght' => 16], $node->extract(['lft', 'rght']));
  540. $expected = [
  541. ' 1: 4 - 6:Link 6',
  542. '_ 2: 3 - 7:Link 7',
  543. ' 5: 6 - 8:Link 8',
  544. ' 7:16 - 1:Link 1',
  545. '_ 8: 9 - 2:Link 2',
  546. '_10:15 - 3:Link 3',
  547. '__11:14 - 4:Link 4',
  548. '___12:13 - 5:Link 5'
  549. ];
  550. $this->assertMpttValues($expected, $table);
  551. }
  552. /**
  553. * Tests moving a node with no lft and rght columns
  554. *
  555. * @return void
  556. */
  557. public function testMoveDownNoTreeColumns()
  558. {
  559. $table = TableRegistry::get('MenuLinkTrees');
  560. $table->addBehavior('Tree', ['scope' => ['menu' => 'main-menu']]);
  561. $node = $table->get(1);
  562. $node->unsetProperty('lft');
  563. $node->unsetProperty('rght');
  564. $node = $table->moveDown($node, true);
  565. $this->assertEquals(['lft' => 7, 'rght' => 16], $node->extract(['lft', 'rght']));
  566. $expected = [
  567. ' 1: 4 - 6:Link 6',
  568. '_ 2: 3 - 7:Link 7',
  569. ' 5: 6 - 8:Link 8',
  570. ' 7:16 - 1:Link 1',
  571. '_ 8: 9 - 2:Link 2',
  572. '_10:15 - 3:Link 3',
  573. '__11:14 - 4:Link 4',
  574. '___12:13 - 5:Link 5'
  575. ];
  576. $this->assertMpttValues($expected, $table);
  577. }
  578. public function testMoveDownMultiplePositions()
  579. {
  580. $node = $this->table->moveDown($this->table->get(3), 2);
  581. $this->assertEquals(['lft' => 7, 'rght' => 8], $node->extract(['lft', 'rght']));
  582. $expected = [
  583. ' 1:20 - 1:electronics',
  584. '_ 2: 9 - 2:televisions',
  585. '__ 3: 4 - 4:lcd',
  586. '__ 5: 6 - 5:plasma',
  587. '__ 7: 8 - 3:tube',
  588. '_10:19 - 6:portable',
  589. '__11:14 - 7:mp3',
  590. '___12:13 - 8:flash',
  591. '__15:16 - 9:cd',
  592. '__17:18 - 10:radios',
  593. '21:22 - 11:alien hardware'
  594. ];
  595. $this->assertMpttValues($expected, $this->table);
  596. }
  597. /**
  598. * Tests the recover function
  599. *
  600. * @return void
  601. */
  602. public function testRecover()
  603. {
  604. $table = $this->table;
  605. $expectedLevels = $table
  606. ->find('list', ['valueField' => 'depth'])
  607. ->order('lft')
  608. ->toArray();
  609. $table->updateAll(['lft' => null, 'rght' => null, 'depth' => null], []);
  610. $table->behaviors()->Tree->config('level', 'depth');
  611. $table->recover();
  612. $expected = [
  613. ' 1:20 - 1:electronics',
  614. '_ 2: 9 - 2:televisions',
  615. '__ 3: 4 - 3:tube',
  616. '__ 5: 6 - 4:lcd',
  617. '__ 7: 8 - 5:plasma',
  618. '_10:19 - 6:portable',
  619. '__11:14 - 7:mp3',
  620. '___12:13 - 8:flash',
  621. '__15:16 - 9:cd',
  622. '__17:18 - 10:radios',
  623. '21:22 - 11:alien hardware'
  624. ];
  625. $this->assertMpttValues($expected, $table);
  626. $result = $table
  627. ->find('list', ['valueField' => 'depth'])
  628. ->order('lft')
  629. ->toArray();
  630. $this->assertSame($expectedLevels, $result);
  631. }
  632. /**
  633. * Tests the recover function with a custom scope
  634. *
  635. * @return void
  636. */
  637. public function testRecoverScoped()
  638. {
  639. $table = TableRegistry::get('MenuLinkTrees');
  640. $table->addBehavior('Tree', ['scope' => ['menu' => 'main-menu']]);
  641. $table->updateAll(['lft' => null, 'rght' => null], ['menu' => 'main-menu']);
  642. $table->recover();
  643. $expected = [
  644. ' 1:10 - 1:Link 1',
  645. '_ 2: 3 - 2:Link 2',
  646. '_ 4: 9 - 3:Link 3',
  647. '__ 5: 8 - 4:Link 4',
  648. '___ 6: 7 - 5:Link 5',
  649. '11:14 - 6:Link 6',
  650. '_12:13 - 7:Link 7',
  651. '15:16 - 8:Link 8'
  652. ];
  653. $this->assertMpttValues($expected, $table);
  654. $table->removeBehavior('Tree');
  655. $table->addBehavior('Tree', ['scope' => ['menu' => 'categories']]);
  656. $expected = [
  657. ' 1:10 - 9:electronics',
  658. '_ 2: 9 - 10:televisions',
  659. '__ 3: 4 - 11:tube',
  660. '__ 5: 8 - 12:lcd',
  661. '___ 6: 7 - 13:plasma',
  662. '11:20 - 14:portable',
  663. '_12:15 - 15:mp3',
  664. '__13:14 - 16:flash',
  665. '_16:17 - 17:cd',
  666. '_18:19 - 18:radios'
  667. ];
  668. $this->assertMpttValues($expected, $table);
  669. }
  670. /**
  671. * Test recover function with a custom order clause
  672. *
  673. * @return void
  674. */
  675. public function testRecoverWithCustomOrder()
  676. {
  677. $table = TableRegistry::get('MenuLinkTrees');
  678. $table->addBehavior('Tree', ['scope' => ['menu' => 'main-menu'], 'recoverOrder' => ['MenuLinkTrees.title' => 'desc']]);
  679. $table->updateAll(['lft' => null, 'rght' => null], ['menu' => 'main-menu']);
  680. $table->recover();
  681. $expected = [
  682. ' 1: 2 - 8:Link 8',
  683. ' 3: 6 - 6:Link 6',
  684. '_ 4: 5 - 7:Link 7',
  685. ' 7:16 - 1:Link 1',
  686. '_ 8:13 - 3:Link 3',
  687. '__ 9:12 - 4:Link 4',
  688. '___10:11 - 5:Link 5',
  689. '_14:15 - 2:Link 2'
  690. ];
  691. $this->assertMpttValues($expected, $table);
  692. }
  693. /**
  694. * Tests adding a new orphan node
  695. *
  696. * @return void
  697. */
  698. public function testAddOrphan()
  699. {
  700. $table = $this->table;
  701. $entity = new Entity(
  702. ['name' => 'New Orphan', 'parent_id' => null, 'level' => null],
  703. ['markNew' => true]
  704. );
  705. $this->assertSame($entity, $table->save($entity));
  706. $this->assertEquals(23, $entity->lft);
  707. $this->assertEquals(24, $entity->rght);
  708. $expected = [
  709. ' 1:20 - 1:electronics',
  710. '_ 2: 9 - 2:televisions',
  711. '__ 3: 4 - 3:tube',
  712. '__ 5: 6 - 4:lcd',
  713. '__ 7: 8 - 5:plasma',
  714. '_10:19 - 6:portable',
  715. '__11:14 - 7:mp3',
  716. '___12:13 - 8:flash',
  717. '__15:16 - 9:cd',
  718. '__17:18 - 10:radios',
  719. '21:22 - 11:alien hardware',
  720. '23:24 - 12:New Orphan',
  721. ];
  722. $this->assertMpttValues($expected, $this->table);
  723. }
  724. /**
  725. * Tests that adding a child node as a decendant of one of the roots works
  726. *
  727. * @return void
  728. */
  729. public function testAddMiddle()
  730. {
  731. $table = $this->table;
  732. $entity = new Entity(
  733. ['name' => 'laptops', 'parent_id' => 1],
  734. ['markNew' => true]
  735. );
  736. $this->assertSame($entity, $table->save($entity));
  737. $this->assertEquals(20, $entity->lft);
  738. $this->assertEquals(21, $entity->rght);
  739. $expected = [
  740. ' 1:22 - 1:electronics',
  741. '_ 2: 9 - 2:televisions',
  742. '__ 3: 4 - 3:tube',
  743. '__ 5: 6 - 4:lcd',
  744. '__ 7: 8 - 5:plasma',
  745. '_10:19 - 6:portable',
  746. '__11:14 - 7:mp3',
  747. '___12:13 - 8:flash',
  748. '__15:16 - 9:cd',
  749. '__17:18 - 10:radios',
  750. '_20:21 - 12:laptops',
  751. '23:24 - 11:alien hardware',
  752. ];
  753. $this->assertMpttValues($expected, $this->table);
  754. }
  755. /**
  756. * Tests adding a leaf to the tree
  757. *
  758. * @return void
  759. */
  760. public function testAddLeaf()
  761. {
  762. $table = $this->table;
  763. $entity = new Entity(
  764. ['name' => 'laptops', 'parent_id' => 2],
  765. ['markNew' => true]
  766. );
  767. $this->assertSame($entity, $table->save($entity));
  768. $this->assertEquals(9, $entity->lft);
  769. $this->assertEquals(10, $entity->rght);
  770. $expected = [
  771. ' 1:22 - 1:electronics',
  772. '_ 2:11 - 2:televisions',
  773. '__ 3: 4 - 3:tube',
  774. '__ 5: 6 - 4:lcd',
  775. '__ 7: 8 - 5:plasma',
  776. '__ 9:10 - 12:laptops',
  777. '_12:21 - 6:portable',
  778. '__13:16 - 7:mp3',
  779. '___14:15 - 8:flash',
  780. '__17:18 - 9:cd',
  781. '__19:20 - 10:radios',
  782. '23:24 - 11:alien hardware'
  783. ];
  784. $this->assertMpttValues($expected, $this->table);
  785. }
  786. /**
  787. * Tests moving a subtree to the right
  788. *
  789. * @return void
  790. */
  791. public function testReParentSubTreeRight()
  792. {
  793. $table = $this->table;
  794. $entity = $table->get(2);
  795. $entity->parent_id = 6;
  796. $this->assertSame($entity, $table->save($entity));
  797. $this->assertEquals(11, $entity->lft);
  798. $this->assertEquals(18, $entity->rght);
  799. $expected = [
  800. ' 1:20 - 1:electronics',
  801. '_ 2:19 - 6:portable',
  802. '__ 3: 6 - 7:mp3',
  803. '___ 4: 5 - 8:flash',
  804. '__ 7: 8 - 9:cd',
  805. '__ 9:10 - 10:radios',
  806. '__11:18 - 2:televisions',
  807. '___12:13 - 3:tube',
  808. '___14:15 - 4:lcd',
  809. '___16:17 - 5:plasma',
  810. '21:22 - 11:alien hardware'
  811. ];
  812. $this->assertMpttValues($expected, $table);
  813. }
  814. /**
  815. * Tests moving a subtree to the left
  816. *
  817. * @return void
  818. */
  819. public function testReParentSubTreeLeft()
  820. {
  821. $table = $this->table;
  822. $entity = $table->get(6);
  823. $entity->parent_id = 2;
  824. $this->assertSame($entity, $table->save($entity));
  825. $this->assertEquals(9, $entity->lft);
  826. $this->assertEquals(18, $entity->rght);
  827. $expected = [
  828. ' 1:20 - 1:electronics',
  829. '_ 2:19 - 2:televisions',
  830. '__ 3: 4 - 3:tube',
  831. '__ 5: 6 - 4:lcd',
  832. '__ 7: 8 - 5:plasma',
  833. '__ 9:18 - 6:portable',
  834. '___10:13 - 7:mp3',
  835. '____11:12 - 8:flash',
  836. '___14:15 - 9:cd',
  837. '___16:17 - 10:radios',
  838. '21:22 - 11:alien hardware'
  839. ];
  840. $this->assertMpttValues($expected, $this->table);
  841. }
  842. /**
  843. * Test moving a leaft to the left
  844. *
  845. * @return void
  846. */
  847. public function testReParentLeafLeft()
  848. {
  849. $table = $this->table;
  850. $entity = $table->get(10);
  851. $entity->parent_id = 2;
  852. $this->assertSame($entity, $table->save($entity));
  853. $this->assertEquals(9, $entity->lft);
  854. $this->assertEquals(10, $entity->rght);
  855. $expected = [
  856. ' 1:20 - 1:electronics',
  857. '_ 2:11 - 2:televisions',
  858. '__ 3: 4 - 3:tube',
  859. '__ 5: 6 - 4:lcd',
  860. '__ 7: 8 - 5:plasma',
  861. '__ 9:10 - 10:radios',
  862. '_12:19 - 6:portable',
  863. '__13:16 - 7:mp3',
  864. '___14:15 - 8:flash',
  865. '__17:18 - 9:cd',
  866. '21:22 - 11:alien hardware'
  867. ];
  868. $this->assertMpttValues($expected, $this->table);
  869. }
  870. /**
  871. * Test moving a leaf to the left
  872. *
  873. * @return void
  874. */
  875. public function testReParentLeafRight()
  876. {
  877. $table = $this->table;
  878. $entity = $table->get(5);
  879. $entity->parent_id = 6;
  880. $this->assertSame($entity, $table->save($entity));
  881. $this->assertEquals(17, $entity->lft);
  882. $this->assertEquals(18, $entity->rght);
  883. $result = $table->find()->order('lft')->hydrate(false);
  884. $expected = [
  885. ' 1:20 - 1:electronics',
  886. '_ 2: 7 - 2:televisions',
  887. '__ 3: 4 - 3:tube',
  888. '__ 5: 6 - 4:lcd',
  889. '_ 8:19 - 6:portable',
  890. '__ 9:12 - 7:mp3',
  891. '___10:11 - 8:flash',
  892. '__13:14 - 9:cd',
  893. '__15:16 - 10:radios',
  894. '__17:18 - 5:plasma',
  895. '21:22 - 11:alien hardware'
  896. ];
  897. $this->assertMpttValues($expected, $table);
  898. }
  899. /**
  900. * Tests moving a subtree with a node having no lft and rght columns
  901. *
  902. * @return void
  903. */
  904. public function testReParentNoTreeColumns()
  905. {
  906. $table = $this->table;
  907. $entity = $table->get(6);
  908. $entity->unsetProperty('lft');
  909. $entity->unsetProperty('rght');
  910. $entity->parent_id = 2;
  911. $this->assertSame($entity, $table->save($entity));
  912. $this->assertEquals(9, $entity->lft);
  913. $this->assertEquals(18, $entity->rght);
  914. $expected = [
  915. ' 1:20 - 1:electronics',
  916. '_ 2:19 - 2:televisions',
  917. '__ 3: 4 - 3:tube',
  918. '__ 5: 6 - 4:lcd',
  919. '__ 7: 8 - 5:plasma',
  920. '__ 9:18 - 6:portable',
  921. '___10:13 - 7:mp3',
  922. '____11:12 - 8:flash',
  923. '___14:15 - 9:cd',
  924. '___16:17 - 10:radios',
  925. '21:22 - 11:alien hardware'
  926. ];
  927. $this->assertMpttValues($expected, $this->table);
  928. }
  929. /**
  930. * Tests moving a subtree as a new root
  931. *
  932. * @return void
  933. */
  934. public function testRootingSubTree()
  935. {
  936. $table = $this->table;
  937. $entity = $table->get(2);
  938. $entity->parent_id = null;
  939. $this->assertSame($entity, $table->save($entity));
  940. $this->assertEquals(15, $entity->lft);
  941. $this->assertEquals(22, $entity->rght);
  942. $expected = [
  943. ' 1:12 - 1:electronics',
  944. '_ 2:11 - 6:portable',
  945. '__ 3: 6 - 7:mp3',
  946. '___ 4: 5 - 8:flash',
  947. '__ 7: 8 - 9:cd',
  948. '__ 9:10 - 10:radios',
  949. '13:14 - 11:alien hardware',
  950. '15:22 - 2:televisions',
  951. '_16:17 - 3:tube',
  952. '_18:19 - 4:lcd',
  953. '_20:21 - 5:plasma'
  954. ];
  955. $this->assertMpttValues($expected, $table);
  956. }
  957. /**
  958. * Tests moving a subtree with no tree columns
  959. *
  960. * @return void
  961. */
  962. public function testRootingNoTreeColumns()
  963. {
  964. $table = $this->table;
  965. $entity = $table->get(2);
  966. $entity->unsetProperty('lft');
  967. $entity->unsetProperty('rght');
  968. $entity->parent_id = null;
  969. $this->assertSame($entity, $table->save($entity));
  970. $this->assertEquals(15, $entity->lft);
  971. $this->assertEquals(22, $entity->rght);
  972. $expected = [
  973. ' 1:12 - 1:electronics',
  974. '_ 2:11 - 6:portable',
  975. '__ 3: 6 - 7:mp3',
  976. '___ 4: 5 - 8:flash',
  977. '__ 7: 8 - 9:cd',
  978. '__ 9:10 - 10:radios',
  979. '13:14 - 11:alien hardware',
  980. '15:22 - 2:televisions',
  981. '_16:17 - 3:tube',
  982. '_18:19 - 4:lcd',
  983. '_20:21 - 5:plasma'
  984. ];
  985. $this->assertMpttValues($expected, $table);
  986. }
  987. /**
  988. * Tests that trying to create a cycle throws an exception
  989. *
  990. * @expectedException \RuntimeException
  991. * @expectedExceptionMessage Cannot use node "5" as parent for entity "2"
  992. * @return void
  993. */
  994. public function testReparentCycle()
  995. {
  996. $table = $this->table;
  997. $entity = $table->get(2);
  998. $entity->parent_id = 5;
  999. $table->save($entity);
  1000. }
  1001. /**
  1002. * Tests deleting a leaf in the tree
  1003. *
  1004. * @return void
  1005. */
  1006. public function testDeleteLeaf()
  1007. {
  1008. $table = $this->table;
  1009. $entity = $table->get(4);
  1010. $this->assertTrue($table->delete($entity));
  1011. $expected = [
  1012. ' 1:18 - 1:electronics',
  1013. '_ 2: 7 - 2:televisions',
  1014. '__ 3: 4 - 3:tube',
  1015. '__ 5: 6 - 5:plasma',
  1016. '_ 8:17 - 6:portable',
  1017. '__ 9:12 - 7:mp3',
  1018. '___10:11 - 8:flash',
  1019. '__13:14 - 9:cd',
  1020. '__15:16 - 10:radios',
  1021. '19:20 - 11:alien hardware'
  1022. ];
  1023. $this->assertMpttValues($expected, $this->table);
  1024. }
  1025. /**
  1026. * Tests deleting a subtree
  1027. *
  1028. * @return void
  1029. */
  1030. public function testDeleteSubTree()
  1031. {
  1032. $table = $this->table;
  1033. $entity = $table->get(6);
  1034. $this->assertTrue($table->delete($entity));
  1035. $expected = [
  1036. ' 1:10 - 1:electronics',
  1037. '_ 2: 9 - 2:televisions',
  1038. '__ 3: 4 - 3:tube',
  1039. '__ 5: 6 - 4:lcd',
  1040. '__ 7: 8 - 5:plasma',
  1041. '11:12 - 11:alien hardware'
  1042. ];
  1043. $this->assertMpttValues($expected, $this->table);
  1044. }
  1045. /**
  1046. * Test deleting a root node
  1047. *
  1048. * @return void
  1049. */
  1050. public function testDeleteRoot()
  1051. {
  1052. $table = $this->table;
  1053. $entity = $table->get(1);
  1054. $this->assertTrue($table->delete($entity));
  1055. $expected = [
  1056. ' 1: 2 - 11:alien hardware'
  1057. ];
  1058. $this->assertMpttValues($expected, $this->table);
  1059. }
  1060. /**
  1061. * Test deleting a node with no tree columns
  1062. *
  1063. * @return void
  1064. */
  1065. public function testDeleteRootNoTreeColumns()
  1066. {
  1067. $table = $this->table;
  1068. $entity = $table->get(1);
  1069. $entity->unsetProperty('lft');
  1070. $entity->unsetProperty('rght');
  1071. $this->assertTrue($table->delete($entity));
  1072. $expected = [
  1073. ' 1: 2 - 11:alien hardware'
  1074. ];
  1075. $this->assertMpttValues($expected, $this->table);
  1076. }
  1077. /**
  1078. * Tests that a leaf can be taken out of the tree and put in as a root
  1079. *
  1080. * @return void
  1081. */
  1082. public function testRemoveFromLeafFromTree()
  1083. {
  1084. $table = $this->table;
  1085. $entity = $table->get(10);
  1086. $this->assertSame($entity, $table->removeFromTree($entity));
  1087. $this->assertEquals(21, $entity->lft);
  1088. $this->assertEquals(22, $entity->rght);
  1089. $this->assertEquals(null, $entity->parent_id);
  1090. $result = $table->find()->order('lft')->hydrate(false);
  1091. $expected = [
  1092. ' 1:18 - 1:electronics',
  1093. '_ 2: 9 - 2:televisions',
  1094. '__ 3: 4 - 3:tube',
  1095. '__ 5: 6 - 4:lcd',
  1096. '__ 7: 8 - 5:plasma',
  1097. '_10:17 - 6:portable',
  1098. '__11:14 - 7:mp3',
  1099. '___12:13 - 8:flash',
  1100. '__15:16 - 9:cd',
  1101. '19:20 - 11:alien hardware',
  1102. '21:22 - 10:radios'
  1103. ];
  1104. $this->assertMpttValues($expected, $table);
  1105. }
  1106. /**
  1107. * Test removing a middle node from a tree
  1108. *
  1109. * @return void
  1110. */
  1111. public function testRemoveMiddleNodeFromTree()
  1112. {
  1113. $table = $this->table;
  1114. $entity = $table->get(6);
  1115. $this->assertSame($entity, $table->removeFromTree($entity));
  1116. $result = $table->find('threaded')->order('lft')->hydrate(false)->toArray();
  1117. $this->assertEquals(21, $entity->lft);
  1118. $this->assertEquals(22, $entity->rght);
  1119. $this->assertEquals(null, $entity->parent_id);
  1120. $result = $table->find()->order('lft')->hydrate(false);
  1121. $expected = [
  1122. ' 1:18 - 1:electronics',
  1123. '_ 2: 9 - 2:televisions',
  1124. '__ 3: 4 - 3:tube',
  1125. '__ 5: 6 - 4:lcd',
  1126. '__ 7: 8 - 5:plasma',
  1127. '_10:13 - 7:mp3',
  1128. '__11:12 - 8:flash',
  1129. '_14:15 - 9:cd',
  1130. '_16:17 - 10:radios',
  1131. '19:20 - 11:alien hardware',
  1132. '21:22 - 6:portable'
  1133. ];
  1134. $this->assertMpttValues($expected, $table);
  1135. }
  1136. /**
  1137. * Tests removing the root of a tree
  1138. *
  1139. * @return void
  1140. */
  1141. public function testRemoveRootFromTree()
  1142. {
  1143. $table = $this->table;
  1144. $entity = $table->get(1);
  1145. $this->assertSame($entity, $table->removeFromTree($entity));
  1146. $result = $table->find('threaded')->order('lft')->hydrate(false)->toArray();
  1147. $this->assertEquals(21, $entity->lft);
  1148. $this->assertEquals(22, $entity->rght);
  1149. $this->assertEquals(null, $entity->parent_id);
  1150. $expected = [
  1151. ' 1: 8 - 2:televisions',
  1152. '_ 2: 3 - 3:tube',
  1153. '_ 4: 5 - 4:lcd',
  1154. '_ 6: 7 - 5:plasma',
  1155. ' 9:18 - 6:portable',
  1156. '_10:13 - 7:mp3',
  1157. '__11:12 - 8:flash',
  1158. '_14:15 - 9:cd',
  1159. '_16:17 - 10:radios',
  1160. '19:20 - 11:alien hardware',
  1161. '21:22 - 1:electronics'
  1162. ];
  1163. $this->assertMpttValues($expected, $table);
  1164. }
  1165. /**
  1166. * Tests that using associations having tree fields in the schema
  1167. * does not generate SQL errors
  1168. *
  1169. * @return void
  1170. */
  1171. public function testFindPathWithAssociation()
  1172. {
  1173. $table = $this->table;
  1174. $other = TableRegistry::get('FriendlyTrees', [
  1175. 'table' => $table->table()
  1176. ]);
  1177. $table->hasOne('FriendlyTrees', [
  1178. 'foreignKey' => 'id'
  1179. ]);
  1180. $result = $table
  1181. ->find('children', ['for' => 1])
  1182. ->contain('FriendlyTrees')
  1183. ->toArray();
  1184. $this->assertCount(9, $result);
  1185. }
  1186. /**
  1187. * Tests getting the depth level of a node in the tree.
  1188. *
  1189. * @return void
  1190. */
  1191. public function testGetLevel()
  1192. {
  1193. $entity = $this->table->get(8);
  1194. $result = $this->table->getLevel($entity);
  1195. $this->assertEquals(3, $result);
  1196. $result = $this->table->getLevel($entity->id);
  1197. $this->assertEquals(3, $result);
  1198. $result = $this->table->getLevel(5);
  1199. $this->assertEquals(2, $result);
  1200. $result = $this->table->getLevel(99999);
  1201. $this->assertFalse($result);
  1202. }
  1203. /**
  1204. * Test setting level for new nodes
  1205. *
  1206. * @return void
  1207. */
  1208. public function testSetLevelNewNode()
  1209. {
  1210. $this->table->behaviors()->Tree->config('level', 'depth');
  1211. $entity = new Entity(['parent_id' => null, 'name' => 'Depth 0']);
  1212. $this->table->save($entity);
  1213. $entity = $this->table->get(12);
  1214. $this->assertEquals(0, $entity->depth);
  1215. $entity = new Entity(['parent_id' => 1, 'name' => 'Depth 1']);
  1216. $this->table->save($entity);
  1217. $entity = $this->table->get(13);
  1218. $this->assertEquals(1, $entity->depth);
  1219. $entity = new Entity(['parent_id' => 8, 'name' => 'Depth 4']);
  1220. $this->table->save($entity);
  1221. $entity = $this->table->get(14);
  1222. $this->assertEquals(4, $entity->depth);
  1223. }
  1224. /**
  1225. * Test setting level for existing nodes
  1226. *
  1227. * @return void
  1228. */
  1229. public function testSetLevelExistingNode()
  1230. {
  1231. $this->table->behaviors()->Tree->config('level', 'depth');
  1232. // Leaf node
  1233. $entity = $this->table->get(4);
  1234. $this->assertEquals(2, $entity->depth);
  1235. $this->table->save($entity);
  1236. $entity = $this->table->get(4);
  1237. $this->assertEquals(2, $entity->depth);
  1238. // Non leaf node so depth of descendents will also change
  1239. $entity = $this->table->get(6);
  1240. $this->assertEquals(1, $entity->depth);
  1241. $entity->parent_id = null;
  1242. $this->table->save($entity);
  1243. $entity = $this->table->get(6);
  1244. $this->assertEquals(0, $entity->depth);
  1245. $entity = $this->table->get(7);
  1246. $this->assertEquals(1, $entity->depth);
  1247. $entity = $this->table->get(8);
  1248. $this->assertEquals(2, $entity->depth);
  1249. $entity->parent_id = 6;
  1250. $this->table->save($entity);
  1251. $entity = $this->table->get(8);
  1252. $this->assertEquals(1, $entity->depth);
  1253. }
  1254. /**
  1255. * Assert MPTT values
  1256. *
  1257. * Custom assert method to make identifying the differences between expected
  1258. * and actual db state easier to identify.
  1259. *
  1260. * @param array $expected tree state to be expected
  1261. * @param \Cake\ORM\Table $table Table instance
  1262. * @param \Cake\ORM\Query $query Optional query object
  1263. * @return void
  1264. */
  1265. public function assertMpttValues($expected, $table, $query = null)
  1266. {
  1267. $query = $query ?: $table->find();
  1268. $primaryKey = $table->primaryKey();
  1269. if (is_array($primaryKey)) {
  1270. $primaryKey = $primaryKey[0];
  1271. }
  1272. $displayField = $table->displayField();
  1273. $options = [
  1274. 'valuePath' => function ($item, $key, $iterator) use ($primaryKey, $displayField) {
  1275. return sprintf(
  1276. '%s:%s - %s:%s',
  1277. str_pad($item->lft, 2, ' ', STR_PAD_LEFT),
  1278. str_pad($item->rght, 2, ' ', STR_PAD_LEFT),
  1279. str_pad($item->$primaryKey, 2, ' ', STR_PAD_LEFT),
  1280. $item->{$displayField}
  1281. );
  1282. }
  1283. ];
  1284. $result = array_values($query->find('treeList', $options)->toArray());
  1285. if (count($result) === count($expected)) {
  1286. $subExpected = array_diff($expected, $result);
  1287. if ($subExpected) {
  1288. $subResult = array_intersect_key($result, $subExpected);
  1289. $this->assertSame($subExpected, $subResult, 'Differences in the tree were found (lft:rght id:display-name)');
  1290. }
  1291. }
  1292. $this->assertSame($expected, $result, 'The tree is not the same (lft:rght id:display-name)');
  1293. }
  1294. }