TreeBehaviorScopedTest.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591
  1. <?php
  2. /**
  3. * TreeBehaviorScopedTest file
  4. *
  5. * A tree test using scope
  6. *
  7. * CakePHP(tm) Tests <http://book.cakephp.org/2.0/en/development/testing.html>
  8. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  9. *
  10. * Licensed under The MIT License
  11. * For full copyright and license information, please see the LICENSE.txt
  12. * Redistributions of files must retain the above copyright notice
  13. *
  14. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  15. * @link http://book.cakephp.org/2.0/en/development/testing.html CakePHP(tm) Tests
  16. * @package Cake.Test.Case.Model.Behavior
  17. * @since CakePHP(tm) v 1.2.0.5330
  18. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  19. */
  20. App::uses('Model', 'Model');
  21. App::uses('AppModel', 'Model');
  22. require_once dirname(dirname(__FILE__)) . DS . 'models.php';
  23. /**
  24. * TreeBehaviorScopedTest class
  25. *
  26. * @package Cake.Test.Case.Model.Behavior
  27. */
  28. class TreeBehaviorScopedTest extends CakeTestCase {
  29. /**
  30. * Whether backup global state for each test method or not
  31. *
  32. * @var bool
  33. */
  34. public $backupGlobals = false;
  35. /**
  36. * settings property
  37. *
  38. * @var array
  39. */
  40. public $settings = array(
  41. 'modelClass' => 'FlagTree',
  42. 'leftField' => 'lft',
  43. 'rightField' => 'rght',
  44. 'parentField' => 'parent_id'
  45. );
  46. /**
  47. * fixtures property
  48. *
  49. * @var array
  50. */
  51. public $fixtures = array('core.flag_tree', 'core.ad', 'core.campaign', 'core.translate', 'core.number_tree_two');
  52. /**
  53. * testStringScope method
  54. *
  55. * @return void
  56. */
  57. public function testStringScope() {
  58. $this->Tree = new FlagTree();
  59. $this->Tree->order = null;
  60. $this->Tree->initialize(2, 3);
  61. $this->Tree->id = 1;
  62. $this->Tree->saveField('flag', 1);
  63. $this->Tree->id = 2;
  64. $this->Tree->saveField('flag', 1);
  65. $result = $this->Tree->children();
  66. $expected = array(
  67. array('FlagTree' => array('id' => '3', 'name' => '1.1.1', 'parent_id' => '2', 'lft' => '3', 'rght' => '4', 'flag' => '0')),
  68. array('FlagTree' => array('id' => '4', 'name' => '1.1.2', 'parent_id' => '2', 'lft' => '5', 'rght' => '6', 'flag' => '0')),
  69. array('FlagTree' => array('id' => '5', 'name' => '1.1.3', 'parent_id' => '2', 'lft' => '7', 'rght' => '8', 'flag' => '0'))
  70. );
  71. $this->assertEquals($expected, $result);
  72. $this->Tree->Behaviors->load('Tree', array('scope' => 'FlagTree.flag = 1'));
  73. $this->assertEquals(array(), $this->Tree->children());
  74. $this->Tree->id = 1;
  75. $this->Tree->Behaviors->load('Tree', array('scope' => 'FlagTree.flag = 1'));
  76. $result = $this->Tree->children();
  77. $expected = array(array('FlagTree' => array('id' => '2', 'name' => '1.1', 'parent_id' => '1', 'lft' => '2', 'rght' => '9', 'flag' => '1')));
  78. $this->assertEquals($expected, $result);
  79. $this->assertTrue($this->Tree->delete());
  80. $this->assertEquals(11, $this->Tree->find('count'));
  81. }
  82. /**
  83. * testArrayScope method
  84. *
  85. * @return void
  86. */
  87. public function testArrayScope() {
  88. $this->Tree = new FlagTree();
  89. $this->Tree->order = null;
  90. $this->Tree->initialize(2, 3);
  91. $this->Tree->id = 1;
  92. $this->Tree->saveField('flag', 1);
  93. $this->Tree->id = 2;
  94. $this->Tree->saveField('flag', 1);
  95. $result = $this->Tree->children();
  96. $expected = array(
  97. array('FlagTree' => array('id' => '3', 'name' => '1.1.1', 'parent_id' => '2', 'lft' => '3', 'rght' => '4', 'flag' => '0')),
  98. array('FlagTree' => array('id' => '4', 'name' => '1.1.2', 'parent_id' => '2', 'lft' => '5', 'rght' => '6', 'flag' => '0')),
  99. array('FlagTree' => array('id' => '5', 'name' => '1.1.3', 'parent_id' => '2', 'lft' => '7', 'rght' => '8', 'flag' => '0'))
  100. );
  101. $this->assertEquals($expected, $result);
  102. $this->Tree->Behaviors->load('Tree', array('scope' => array('FlagTree.flag' => 1)));
  103. $this->assertEquals(array(), $this->Tree->children());
  104. $this->Tree->id = 1;
  105. $this->Tree->Behaviors->load('Tree', array('scope' => array('FlagTree.flag' => 1)));
  106. $result = $this->Tree->children();
  107. $expected = array(array('FlagTree' => array('id' => '2', 'name' => '1.1', 'parent_id' => '1', 'lft' => '2', 'rght' => '9', 'flag' => '1')));
  108. $this->assertEquals($expected, $result);
  109. $this->assertTrue($this->Tree->delete());
  110. $this->assertEquals(11, $this->Tree->find('count'));
  111. }
  112. /**
  113. * testSaveWithParentAndInvalidScope method
  114. *
  115. * Attempting to save an invalid data should not trigger an `Undefined offset`
  116. * error
  117. *
  118. * @return void
  119. */
  120. public function testSaveWithParentAndInvalidScope() {
  121. $this->Tree = new FlagTree();
  122. $this->Tree->order = null;
  123. $data = $this->Tree->create(array(
  124. 'name' => 'Flag',
  125. ));
  126. $tree = $this->Tree->save($data);
  127. $this->Tree->Behaviors->load('Tree', array(
  128. 'scope' => array('FlagTree.flag' => 100)
  129. ));
  130. $tree['FlagTree']['parent_id'] = 1;
  131. $result = $this->Tree->save($tree);
  132. $this->assertFalse($result);
  133. }
  134. /**
  135. * testMoveUpWithScope method
  136. *
  137. * @return void
  138. */
  139. public function testMoveUpWithScope() {
  140. $this->Ad = new Ad();
  141. $this->Ad->order = null;
  142. $this->Ad->Behaviors->load('Tree', array('scope' => 'Campaign'));
  143. $this->Ad->moveUp(6);
  144. $this->Ad->id = 4;
  145. $result = $this->Ad->children();
  146. $this->assertEquals(array(6, 5), Hash::extract($result, '{n}.Ad.id'));
  147. $this->assertEquals(array(2, 2), Hash::extract($result, '{n}.Campaign.id'));
  148. }
  149. /**
  150. * testMoveDownWithScope method
  151. *
  152. * @return void
  153. */
  154. public function testMoveDownWithScope() {
  155. $this->Ad = new Ad();
  156. $this->Ad->order = null;
  157. $this->Ad->Behaviors->load('Tree', array('scope' => 'Campaign'));
  158. $this->Ad->moveDown(6);
  159. $this->Ad->id = 4;
  160. $result = $this->Ad->children();
  161. $this->assertEquals(array(5, 6), Hash::extract($result, '{n}.Ad.id'));
  162. $this->assertEquals(array(2, 2), Hash::extract($result, '{n}.Campaign.id'));
  163. }
  164. /**
  165. * Tests the interaction (non-interference) between TreeBehavior and other behaviors with respect
  166. * to callback hooks
  167. *
  168. * @return void
  169. */
  170. public function testTranslatingTree() {
  171. $this->Tree = new FlagTree();
  172. $this->Tree->order = null;
  173. $this->Tree->cacheQueries = false;
  174. $this->Tree->Behaviors->load('Translate', array('title'));
  175. //Save
  176. $this->Tree->create();
  177. $this->Tree->locale = 'eng';
  178. $data = array('FlagTree' => array(
  179. 'title' => 'name #1',
  180. 'name' => 'test',
  181. 'locale' => 'eng',
  182. 'parent_id' => null,
  183. ));
  184. $this->Tree->save($data);
  185. $result = $this->Tree->find('all');
  186. $expected = array(array('FlagTree' => array(
  187. 'id' => 1,
  188. 'title' => 'name #1',
  189. 'name' => 'test',
  190. 'parent_id' => null,
  191. 'lft' => 1,
  192. 'rght' => 2,
  193. 'flag' => 0,
  194. 'locale' => 'eng',
  195. )));
  196. $this->assertEquals($expected, $result);
  197. // update existing record, same locale
  198. $this->Tree->create();
  199. $data['FlagTree']['title'] = 'Named 2';
  200. $this->Tree->id = 1;
  201. $this->Tree->save($data);
  202. $result = $this->Tree->find('all');
  203. $expected = array(array('FlagTree' => array(
  204. 'id' => 1,
  205. 'title' => 'Named 2',
  206. 'name' => 'test',
  207. 'parent_id' => null,
  208. 'lft' => 1,
  209. 'rght' => 2,
  210. 'flag' => 0,
  211. 'locale' => 'eng',
  212. )));
  213. $this->assertEquals($expected, $result);
  214. // update different locale, same record
  215. $this->Tree->create();
  216. $this->Tree->locale = 'deu';
  217. $this->Tree->id = 1;
  218. $data = array('FlagTree' => array(
  219. 'id' => 1,
  220. 'parent_id' => null,
  221. 'title' => 'namen #1',
  222. 'name' => 'test',
  223. 'locale' => 'deu',
  224. ));
  225. $this->Tree->save($data);
  226. $this->Tree->locale = 'deu';
  227. $result = $this->Tree->find('all');
  228. $expected = array(
  229. array(
  230. 'FlagTree' => array(
  231. 'id' => 1,
  232. 'title' => 'namen #1',
  233. 'name' => 'test',
  234. 'parent_id' => null,
  235. 'lft' => 1,
  236. 'rght' => 2,
  237. 'flag' => 0,
  238. 'locale' => 'deu',
  239. )
  240. )
  241. );
  242. $this->assertEquals($expected, $result);
  243. // Save with bindTranslation
  244. $this->Tree->locale = 'eng';
  245. $data = array(
  246. 'title' => array('eng' => 'New title', 'spa' => 'Nuevo leyenda'),
  247. 'name' => 'test',
  248. 'parent_id' => null
  249. );
  250. $this->Tree->create($data);
  251. $this->Tree->save();
  252. $this->Tree->unbindTranslation();
  253. $translations = array('title' => 'Title');
  254. $this->Tree->bindTranslation($translations, false);
  255. $this->Tree->locale = array('eng', 'spa');
  256. $result = $this->Tree->read();
  257. $expected = array(
  258. 'FlagTree' => array(
  259. 'id' => 2,
  260. 'parent_id' => null,
  261. 'locale' => 'eng',
  262. 'name' => 'test',
  263. 'title' => 'New title',
  264. 'flag' => 0,
  265. 'lft' => 3,
  266. 'rght' => 4
  267. ),
  268. 'Title' => array(
  269. array('id' => 21, 'locale' => 'eng', 'model' => 'FlagTree', 'foreign_key' => 2, 'field' => 'title', 'content' => 'New title'),
  270. array('id' => 22, 'locale' => 'spa', 'model' => 'FlagTree', 'foreign_key' => 2, 'field' => 'title', 'content' => 'Nuevo leyenda')
  271. ),
  272. );
  273. $this->assertEquals($expected, $result);
  274. }
  275. /**
  276. * testGenerateTreeListWithSelfJoin method
  277. *
  278. * @return void
  279. */
  280. public function testAliasesWithScopeInTwoTreeAssociations() {
  281. extract($this->settings);
  282. $this->Tree = new $modelClass();
  283. $this->Tree->order = null;
  284. $this->Tree->initialize(2, 2);
  285. $this->TreeTwo = new NumberTreeTwo();
  286. $this->TreeTwo->order = null;
  287. $record = $this->Tree->find('first');
  288. $this->Tree->bindModel(array(
  289. 'hasMany' => array(
  290. 'SecondTree' => array(
  291. 'className' => 'NumberTreeTwo',
  292. 'foreignKey' => 'number_tree_id'
  293. )
  294. )
  295. ));
  296. $this->TreeTwo->bindModel(array(
  297. 'belongsTo' => array(
  298. 'FirstTree' => array(
  299. 'className' => $modelClass,
  300. 'foreignKey' => 'number_tree_id'
  301. )
  302. )
  303. ));
  304. $this->TreeTwo->Behaviors->load('Tree', array(
  305. 'scope' => 'FirstTree'
  306. ));
  307. $data = array(
  308. 'NumberTreeTwo' => array(
  309. 'name' => 'First',
  310. 'number_tree_id' => $record['FlagTree']['id']
  311. )
  312. );
  313. $this->TreeTwo->create();
  314. $result = $this->TreeTwo->save($data);
  315. $this->assertFalse(empty($result));
  316. $result = $this->TreeTwo->find('first');
  317. $expected = array('NumberTreeTwo' => array(
  318. 'id' => 1,
  319. 'name' => 'First',
  320. 'number_tree_id' => $record['FlagTree']['id'],
  321. 'parent_id' => null,
  322. 'lft' => 1,
  323. 'rght' => 2
  324. ));
  325. $this->assertEquals($expected, $result);
  326. }
  327. /**
  328. * testGenerateTreeListWithScope method
  329. *
  330. * @return void
  331. */
  332. public function testGenerateTreeListWithScope() {
  333. extract($this->settings);
  334. $this->Tree = new $modelClass();
  335. $this->Tree->order = null;
  336. $this->Tree->initialize(2, 3);
  337. $this->Tree->id = 1;
  338. $this->Tree->saveField('flag', 1);
  339. $this->Tree->id = 2;
  340. $this->Tree->saveField('flag', 1);
  341. $this->Tree->Behaviors->load('Tree', array('scope' => array('FlagTree.flag' => 1)));
  342. $result = $this->Tree->generateTreeList();
  343. $expected = array(
  344. 1 => '1. Root',
  345. 2 => '_1.1'
  346. );
  347. $this->assertEquals($expected, $result);
  348. // As string.
  349. $this->Tree->Behaviors->load('Tree', array('scope' => 'FlagTree.flag = 1'));
  350. $result = $this->Tree->generateTreeList();
  351. $this->assertEquals($expected, $result);
  352. // Merging conditions.
  353. $result = $this->Tree->generateTreeList(array('FlagTree.id >' => 1));
  354. $expected = array(
  355. 2 => '1.1'
  356. );
  357. $this->assertEquals($expected, $result);
  358. }
  359. /**
  360. * testRecoverUsingParentMode method
  361. *
  362. * @return void
  363. */
  364. public function testRecoverUsingParentMode() {
  365. extract($this->settings);
  366. $this->Tree = new $modelClass();
  367. $this->Tree->order = null;
  368. $this->Tree->initialize(2, 3);
  369. $this->Tree->Behaviors->load('Tree', array('scope' => 'FlagTree.flag = 1'));
  370. $this->Tree->Behaviors->disable('Tree');
  371. $this->Tree->create();
  372. $this->Tree->save(array('name' => 'Main', $parentField => null, $leftField => 0, $rightField => 0, 'flag' => 1));
  373. $node1 = $this->Tree->id;
  374. $this->Tree->create();
  375. $this->Tree->save(array('name' => 'About Us', $parentField => $node1, $leftField => 0, $rightField => 0, 'flag' => 1));
  376. $node11 = $this->Tree->id;
  377. $this->Tree->create();
  378. $this->Tree->save(array('name' => 'Programs', $parentField => $node1, $leftField => 0, $rightField => 0, 'flag' => 1));
  379. $node12 = $this->Tree->id;
  380. $this->Tree->create();
  381. $this->Tree->save(array('name' => 'Mission and History', $parentField => $node11, $leftField => 0, $rightField => 0, 'flag' => 1));
  382. $this->Tree->create();
  383. $this->Tree->save(array('name' => 'Overview', $parentField => $node12, $leftField => 0, $rightField => 0, 'flag' => 1));
  384. $this->Tree->Behaviors->enable('Tree');
  385. $result = $this->Tree->verify();
  386. $this->assertNotSame($result, true);
  387. $result = $this->Tree->recover();
  388. $this->assertTrue($result);
  389. $result = $this->Tree->verify();
  390. $this->assertTrue($result);
  391. $result = $this->Tree->find('first', array(
  392. 'fields' => array('name', $parentField, $leftField, $rightField, 'flag'),
  393. 'conditions' => array('name' => 'Main'),
  394. 'recursive' => -1
  395. ));
  396. $expected = array(
  397. $modelClass => array(
  398. 'name' => 'Main',
  399. $parentField => null,
  400. $leftField => 1,
  401. $rightField => 10,
  402. 'flag' => 1
  403. )
  404. );
  405. $this->assertEquals($expected, $result);
  406. }
  407. /**
  408. * testRecoverFromMissingParent method
  409. *
  410. * @return void
  411. */
  412. public function testRecoverFromMissingParent() {
  413. extract($this->settings);
  414. $this->Tree = new $modelClass();
  415. $this->Tree->order = null;
  416. $this->Tree->initialize(2, 2);
  417. $this->Tree->id = 1;
  418. $this->Tree->saveField('flag', 1);
  419. $this->Tree->id = 2;
  420. $this->Tree->saveField('flag', 1);
  421. $this->Tree->Behaviors->load('Tree', array('scope' => array('FlagTree.flag' => 1)));
  422. $result = $this->Tree->findByName('1.1');
  423. $this->Tree->updateAll(array($parentField => 999999), array('id' => $result[$modelClass]['id']));
  424. $result = $this->Tree->verify();
  425. $this->assertNotSame($result, true);
  426. $result = $this->Tree->recover();
  427. $this->assertTrue($result);
  428. $result = $this->Tree->verify();
  429. $this->assertTrue($result);
  430. }
  431. /**
  432. * testDetectInvalidParents method
  433. *
  434. * @return void
  435. */
  436. public function testDetectInvalidParents() {
  437. extract($this->settings);
  438. $this->Tree = new $modelClass();
  439. $this->Tree->order = null;
  440. $this->Tree->initialize(2, 2);
  441. $this->Tree->id = 1;
  442. $this->Tree->saveField('flag', 1);
  443. $this->Tree->id = 2;
  444. $this->Tree->saveField('flag', 1);
  445. $this->Tree->Behaviors->load('Tree', array('scope' => array('FlagTree.flag' => 1)));
  446. $this->Tree->updateAll(array($parentField => null));
  447. $result = $this->Tree->verify();
  448. $this->assertNotSame($result, true);
  449. $result = $this->Tree->recover();
  450. $this->assertTrue($result);
  451. $result = $this->Tree->verify();
  452. $this->assertTrue($result);
  453. }
  454. /**
  455. * testDetectInvalidLftsRghts method
  456. *
  457. * @return void
  458. */
  459. public function testDetectInvalidLftsRghts() {
  460. extract($this->settings);
  461. $this->Tree = new $modelClass();
  462. $this->Tree->order = null;
  463. $this->Tree->initialize(2, 2);
  464. $this->Tree->id = 1;
  465. $this->Tree->saveField('flag', 1);
  466. $this->Tree->id = 2;
  467. $this->Tree->saveField('flag', 1);
  468. $this->Tree->Behaviors->load('Tree', array('scope' => array('FlagTree.flag' => 1)));
  469. $this->Tree->updateAll(array($leftField => 0, $rightField => 0));
  470. $result = $this->Tree->verify();
  471. $this->assertNotSame($result, true);
  472. $this->Tree->recover();
  473. $result = $this->Tree->verify();
  474. $this->assertTrue($result);
  475. }
  476. /**
  477. * Reproduces a situation where a single node has lft= rght, and all other lft and rght fields follow sequentially
  478. *
  479. * @return void
  480. */
  481. public function testDetectEqualLftsRghts() {
  482. extract($this->settings);
  483. $this->Tree = new $modelClass();
  484. $this->Tree->order = null;
  485. $this->Tree->initialize(1, 3);
  486. $this->Tree->id = 1;
  487. $this->Tree->saveField('flag', 1);
  488. $this->Tree->id = 2;
  489. $this->Tree->saveField('flag', 1);
  490. $this->Tree->Behaviors->load('Tree', array('scope' => array('FlagTree.flag' => 1)));
  491. $result = $this->Tree->findByName('1.1');
  492. $this->Tree->updateAll(array($rightField => $result[$modelClass][$leftField]), array('id' => $result[$modelClass]['id']));
  493. $this->Tree->updateAll(array($leftField => $this->Tree->escapeField($leftField) . ' -1'),
  494. array($leftField . ' >' => $result[$modelClass][$leftField]));
  495. $this->Tree->updateAll(array($rightField => $this->Tree->escapeField($rightField) . ' -1'),
  496. array($rightField . ' >' => $result[$modelClass][$leftField]));
  497. $result = $this->Tree->verify();
  498. $this->assertNotSame($result, true);
  499. $result = $this->Tree->recover();
  500. $this->assertTrue($result);
  501. $result = $this->Tree->verify();
  502. $this->assertTrue($result);
  503. }
  504. }