ClosureTreeTest.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. <?php
  2. namespace Gedmo\Tree;
  3. use Doctrine\Common\Util\Debug;
  4. use Tree\Fixture\Closure\Category;
  5. use Tool\Logging\DBAL\QueryAnalyzer;
  6. /**
  7. * These are tests for Tree behavior
  8. *
  9. * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
  10. * @package Gedmo.Tree
  11. * @link http://www.gediminasm.org
  12. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  13. */
  14. class ClosureTreeTest extends \PHPUnit_Framework_TestCase
  15. {
  16. const TEST_ENTITY_CLASS = "Tree\Fixture\Closure\Category";
  17. const TEST_CLOSURE_CLASS = "Tree\Fixture\Closure\CategoryClosure";
  18. const TEST_BASE_CLOSURE_CLASS = "Gedmo\Tree\Entity\AbstractClosure";
  19. /**
  20. * @var EntityManager
  21. */
  22. private $em;
  23. /**
  24. * @var QueryAnalyzer
  25. */
  26. private $analyzer;
  27. private $food;
  28. private $sports;
  29. private $fruits;
  30. private $vegitables;
  31. private $carrots;
  32. private $potatoes;
  33. public function setUp()
  34. {
  35. $config = new \Doctrine\ORM\Configuration();
  36. $config->setMetadataCacheImpl(new \Doctrine\Common\Cache\ArrayCache);
  37. $config->setQueryCacheImpl(new \Doctrine\Common\Cache\ArrayCache);
  38. $config->setProxyDir(__DIR__ . '/Proxy');
  39. $config->setProxyNamespace('Gedmo\Tree\Proxies');
  40. $config->setMetadataDriverImpl($config->newDefaultAnnotationDriver());
  41. /*$conn = array(
  42. 'driver' => 'pdo_sqlite',
  43. 'memory' => true,
  44. );*/
  45. $conn = array(
  46. 'driver' => 'pdo_mysql',
  47. 'host' => '127.0.0.1',
  48. 'dbname' => 'closure',
  49. 'user' => 'root',
  50. 'password' => 'B9jt12h0'
  51. );
  52. $evm = new \Doctrine\Common\EventManager();
  53. $treeListener = new TreeListener(TreeListener::TYPE_CLOSURE);
  54. $evm->addEventSubscriber($treeListener);
  55. $this->em = \Doctrine\ORM\EntityManager::create($conn, $config, $evm);
  56. $schema = array(
  57. $this->em->getClassMetadata(self::TEST_BASE_CLOSURE_CLASS),
  58. $this->em->getClassMetadata(self::TEST_CLOSURE_CLASS),
  59. $this->em->getClassMetadata(self::TEST_ENTITY_CLASS),
  60. );
  61. $schemaTool = new \Doctrine\ORM\Tools\SchemaTool($this->em);
  62. $schemaTool->dropSchema( $schema );
  63. $schemaTool->createSchema( $schema );
  64. $this->analyzer = new QueryAnalyzer(
  65. $this->em->getConnection()->getDatabasePlatform()
  66. );
  67. $config->setSQLLogger($this->analyzer);
  68. $this->populate();
  69. }
  70. public function tearDown()
  71. {
  72. $this->analyzer->dumpResult();
  73. $this->em->clear();
  74. }
  75. public function test_insertNodes_verifyClosurePaths()
  76. {
  77. // We check the inserted nodes fields from the closure table
  78. $repo = $this->em->getRepository(self::TEST_CLOSURE_CLASS);
  79. $rows = $this->em->createQuery( sprintf( 'SELECT c FROM %s c ORDER BY c.ancestor ASC, c.descendant ASC, c.depth ASC', self::TEST_CLOSURE_CLASS ) )
  80. ->getArrayResult();
  81. // Root self referencing row and descendants
  82. $this->assertEquals( $rows[ 0 ][ 'ancestor' ], $this->food->getId() );
  83. $this->assertEquals( $rows[ 0 ][ 'descendant' ], $this->food->getId() );
  84. $this->assertEquals( $rows[ 0 ][ 'depth' ], 0 );
  85. $this->assertEquals( $rows[ 1 ][ 'ancestor' ], $this->food->getId() );
  86. $this->assertEquals( $rows[ 1 ][ 'descendant' ], $this->fruits->getId() );
  87. $this->assertEquals( $rows[ 1 ][ 'depth' ], 1 );
  88. $this->assertEquals( $rows[ 2 ][ 'ancestor' ], $this->food->getId() );
  89. $this->assertEquals( $rows[ 2 ][ 'descendant' ], $this->vegitables->getId() );
  90. $this->assertEquals( $rows[ 2 ][ 'depth' ], 1 );
  91. $this->assertEquals( $rows[ 3 ][ 'ancestor' ], $this->food->getId() );
  92. $this->assertEquals( $rows[ 3 ][ 'descendant' ], $this->carrots->getId() );
  93. $this->assertEquals( $rows[ 3 ][ 'depth' ], 2 );
  94. $this->assertEquals( $rows[ 4 ][ 'ancestor' ], $this->food->getId() );
  95. $this->assertEquals( $rows[ 4 ][ 'descendant' ], $this->potatoes->getId() );
  96. $this->assertEquals( $rows[ 4 ][ 'depth' ], 2 );
  97. // Sports self referencing row
  98. $this->assertEquals( $rows[ 5 ][ 'ancestor' ], $this->sports->getId() );
  99. $this->assertEquals( $rows[ 5 ][ 'descendant' ], $this->sports->getId() );
  100. $this->assertEquals( $rows[ 5 ][ 'depth' ], 0 );
  101. // Fruits self referencing row
  102. $this->assertEquals( $rows[ 6 ][ 'ancestor' ], $this->fruits->getId() );
  103. $this->assertEquals( $rows[ 6 ][ 'descendant' ], $this->fruits->getId() );
  104. $this->assertEquals( $rows[ 6 ][ 'depth' ], 0 );
  105. // Vegitables self referencing row and descendants
  106. $this->assertEquals( $rows[ 7 ][ 'ancestor' ], $this->vegitables->getId() );
  107. $this->assertEquals( $rows[ 7 ][ 'descendant' ], $this->vegitables->getId() );
  108. $this->assertEquals( $rows[ 7 ][ 'depth' ], 0 );
  109. $this->assertEquals( $rows[ 8 ][ 'ancestor' ], $this->vegitables->getId() );
  110. $this->assertEquals( $rows[ 8 ][ 'descendant' ], $this->carrots->getId() );
  111. $this->assertEquals( $rows[ 8 ][ 'depth' ], 1 );
  112. $this->assertEquals( $rows[ 9 ][ 'ancestor' ], $this->vegitables->getId() );
  113. $this->assertEquals( $rows[ 9 ][ 'descendant' ], $this->potatoes->getId() );
  114. $this->assertEquals( $rows[ 9 ][ 'depth' ], 1 );
  115. // Carrots self referencing row
  116. $this->assertEquals( $rows[ 10 ][ 'ancestor' ], $this->carrots->getId() );
  117. $this->assertEquals( $rows[ 10 ][ 'descendant' ], $this->carrots->getId() );
  118. $this->assertEquals( $rows[ 10 ][ 'depth' ], 0 );
  119. // Potatoes self referencing row
  120. $this->assertEquals( $rows[ 11 ][ 'ancestor' ], $this->potatoes->getId() );
  121. $this->assertEquals( $rows[ 11 ][ 'descendant' ], $this->potatoes->getId() );
  122. $this->assertEquals( $rows[ 11 ][ 'depth' ], 0 );
  123. }
  124. public function test_updateNodes_moveASubtreeAndVerifyTreeClosurePaths()
  125. {
  126. // We change a subtree's location
  127. $vegitables = $this->em->getRepository( self::TEST_ENTITY_CLASS )
  128. ->findOneByTitle( 'Vegitables' );
  129. $sports = $this->em->getRepository( self::TEST_ENTITY_CLASS )
  130. ->findOneByTitle( 'Sports' );
  131. $vegitables->setParent( $sports );
  132. $this->em->persist( $vegitables );
  133. $this->em->flush();
  134. // We then verify the closure paths
  135. $repo = $this->em->getRepository(self::TEST_CLOSURE_CLASS);
  136. $rows = $this->em->createQuery( sprintf( 'SELECT c FROM %s c ORDER BY c.ancestor ASC, c.descendant ASC, c.depth ASC', self::TEST_CLOSURE_CLASS ) )
  137. ->getArrayResult();
  138. // Food self referencing row and descendants
  139. $this->assertEquals( $rows[ 0 ][ 'ancestor' ], $this->food->getId() );
  140. $this->assertEquals( $rows[ 0 ][ 'descendant' ], $this->food->getId() );
  141. $this->assertEquals( $rows[ 0 ][ 'depth' ], 0 );
  142. $this->assertEquals( $rows[ 1 ][ 'ancestor' ], $this->food->getId() );
  143. $this->assertEquals( $rows[ 1 ][ 'descendant' ], $this->fruits->getId() );
  144. $this->assertEquals( $rows[ 1 ][ 'depth' ], 1 );
  145. // Sports self referencing row and descendants
  146. $this->assertEquals( $rows[ 2 ][ 'ancestor' ], $this->sports->getId() );
  147. $this->assertEquals( $rows[ 2 ][ 'descendant' ], $this->sports->getId() );
  148. $this->assertEquals( $rows[ 2 ][ 'depth' ], 0 );
  149. $this->assertEquals( $rows[ 3 ][ 'ancestor' ], $this->sports->getId() );
  150. $this->assertEquals( $rows[ 3 ][ 'descendant' ], $this->vegitables->getId() );
  151. $this->assertEquals( $rows[ 3 ][ 'depth' ], 1 );
  152. $this->assertEquals( $rows[ 4 ][ 'ancestor' ], $this->sports->getId() );
  153. $this->assertEquals( $rows[ 4 ][ 'descendant' ], $this->carrots->getId() );
  154. $this->assertEquals( $rows[ 4 ][ 'depth' ], 2 );
  155. $this->assertEquals( $rows[ 5 ][ 'ancestor' ], $this->sports->getId() );
  156. $this->assertEquals( $rows[ 5 ][ 'descendant' ], $this->potatoes->getId() );
  157. $this->assertEquals( $rows[ 5 ][ 'depth' ], 2 );
  158. // Fruits self referencing row
  159. $this->assertEquals( $rows[ 6 ][ 'ancestor' ], $this->fruits->getId() );
  160. $this->assertEquals( $rows[ 6 ][ 'descendant' ], $this->fruits->getId() );
  161. $this->assertEquals( $rows[ 6 ][ 'depth' ], 0 );
  162. // Vegitables self referencing row and descendants
  163. $this->assertEquals( $rows[ 7 ][ 'ancestor' ], $this->vegitables->getId() );
  164. $this->assertEquals( $rows[ 7 ][ 'descendant' ], $this->vegitables->getId() );
  165. $this->assertEquals( $rows[ 7 ][ 'depth' ], 0 );
  166. $this->assertEquals( $rows[ 8 ][ 'ancestor' ], $this->vegitables->getId() );
  167. $this->assertEquals( $rows[ 8 ][ 'descendant' ], $this->carrots->getId() );
  168. $this->assertEquals( $rows[ 8 ][ 'depth' ], 1 );
  169. $this->assertEquals( $rows[ 9 ][ 'ancestor' ], $this->vegitables->getId() );
  170. $this->assertEquals( $rows[ 9 ][ 'descendant' ], $this->potatoes->getId() );
  171. $this->assertEquals( $rows[ 9 ][ 'depth' ], 1 );
  172. // Carrots self referencing row
  173. $this->assertEquals( $rows[ 10 ][ 'ancestor' ], $this->carrots->getId() );
  174. $this->assertEquals( $rows[ 10 ][ 'descendant' ], $this->carrots->getId() );
  175. $this->assertEquals( $rows[ 10 ][ 'depth' ], 0 );
  176. // Potatoes self referencing row
  177. $this->assertEquals( $rows[ 11 ][ 'ancestor' ], $this->potatoes->getId() );
  178. $this->assertEquals( $rows[ 11 ][ 'descendant' ], $this->potatoes->getId() );
  179. $this->assertEquals( $rows[ 11 ][ 'depth' ], 0 );
  180. }
  181. public function test_removeNode_removesClosurePathsOfNodeAndVerifyTree()
  182. {
  183. // We remove a subtree
  184. $vegitables = $this->em->getRepository( self::TEST_ENTITY_CLASS )
  185. ->findOneByTitle( 'Vegitables' );
  186. $this->em->remove( $vegitables );
  187. $this->em->flush();
  188. // We then verify the closure paths
  189. $repo = $this->em->getRepository(self::TEST_CLOSURE_CLASS);
  190. $rows = $this->em->createQuery( sprintf( 'SELECT c FROM %s c ORDER BY c.ancestor ASC, c.descendant ASC, c.depth ASC', self::TEST_CLOSURE_CLASS ) )
  191. ->getArrayResult();
  192. // Food self referencing row and descendants
  193. $this->assertEquals( $rows[ 0 ][ 'ancestor' ], $this->food->getId() );
  194. $this->assertEquals( $rows[ 0 ][ 'descendant' ], $this->food->getId() );
  195. $this->assertEquals( $rows[ 0 ][ 'depth' ], 0 );
  196. $this->assertEquals( $rows[ 1 ][ 'ancestor' ], $this->food->getId() );
  197. $this->assertEquals( $rows[ 1 ][ 'descendant' ], $this->fruits->getId() );
  198. $this->assertEquals( $rows[ 1 ][ 'depth' ], 1 );
  199. // Sports self referencing row
  200. $this->assertEquals( $rows[ 2 ][ 'ancestor' ], $this->sports->getId() );
  201. $this->assertEquals( $rows[ 2 ][ 'descendant' ], $this->sports->getId() );
  202. $this->assertEquals( $rows[ 2 ][ 'depth' ], 0 );
  203. // Fruits self referencing row
  204. $this->assertEquals( $rows[ 3 ][ 'ancestor' ], $this->fruits->getId() );
  205. $this->assertEquals( $rows[ 3 ][ 'descendant' ], $this->fruits->getId() );
  206. $this->assertEquals( $rows[ 3 ][ 'depth' ], 0 );
  207. }
  208. private function populate()
  209. {
  210. $root = new Category();
  211. $root->setTitle("Food");
  212. $this->food = $root;
  213. $root2 = new Category();
  214. $root2->setTitle("Sports");
  215. $this->sports = $root2;
  216. $child = new Category();
  217. $child->setTitle("Fruits");
  218. $child->setParent($root);
  219. $this->fruits = $child;
  220. $child2 = new Category();
  221. $child2->setTitle("Vegitables");
  222. $child2->setParent($root);
  223. $this->vegitables = $child2;
  224. $childsChild = new Category();
  225. $childsChild->setTitle("Carrots");
  226. $childsChild->setParent($child2);
  227. $this->carrots = $childsChild;
  228. $potatoes = new Category();
  229. $potatoes->setTitle("Potatoes");
  230. $potatoes->setParent($child2);
  231. $this->potatoes = $potatoes;
  232. $this->em->persist($this->food);
  233. $this->em->persist($this->sports);
  234. $this->em->persist($this->fruits);
  235. $this->em->persist($this->vegitables);
  236. $this->em->persist($this->carrots);
  237. $this->em->persist($this->potatoes);
  238. $this->em->flush();
  239. $this->em->clear();
  240. }
  241. }