AbstractMaterializedPath.php 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. <?php
  2. namespace Gedmo\Tree\Strategy;
  3. use Gedmo\Tree\Strategy;
  4. use Doctrine\ORM\EntityManager;
  5. use Gedmo\Tree\TreeListener;
  6. use Doctrine\ORM\Mapping\ClassMetadataInfo;
  7. use Doctrine\ORM\Query;
  8. use Doctrine\Common\Persistence\ObjectManager;
  9. /**
  10. * This strategy makes tree using materialized path strategy
  11. *
  12. * @author Gustavo Falco <comfortablynumb84@gmail.com>
  13. * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
  14. * @package Gedmo.Tree.Strategy
  15. * @subpackage AbstractMaterializedPath
  16. * @link http://www.gediminasm.org
  17. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  18. */
  19. abstract class AbstractMaterializedPath implements Strategy
  20. {
  21. /**
  22. * TreeListener
  23. *
  24. * @var AbstractTreeListener
  25. */
  26. protected $listener = null;
  27. /**
  28. * Array of objects which were scheduled for path processes
  29. *
  30. * @var array
  31. */
  32. protected $scheduledForPathProcess = array();
  33. /**
  34. * {@inheritdoc}
  35. */
  36. public function __construct(TreeListener $listener)
  37. {
  38. $this->listener = $listener;
  39. }
  40. /**
  41. * {@inheritdoc}
  42. */
  43. public function getName()
  44. {
  45. return Strategy::MATERIALIZED_PATH;
  46. }
  47. /**
  48. * {@inheritdoc}
  49. */
  50. public function processScheduledInsertion($om, $node, $ea)
  51. {
  52. $meta = $om->getClassMetadata(get_class($node));
  53. $config = $this->listener->getConfiguration($om, $meta->name);
  54. if ($meta->isIdentifier($config['path_source'])) {
  55. $this->scheduledForPathProcess[spl_object_hash($node)] = node;
  56. } else {
  57. $this->updateNode($om, $node, $ea);
  58. }
  59. }
  60. /**
  61. * {@inheritdoc}
  62. */
  63. public function processScheduledUpdate($om, $node, $ea)
  64. {
  65. $meta = $om->getClassMetadata(get_class($node));
  66. $config = $this->listener->getConfiguration($om, $meta->name);
  67. $uow = $om->getUnitOfWork();
  68. $changeSet = $ea->getObjectChangeSet($uow, $node);
  69. if (isset($changeSet[$config['parent']]) || isset($changeSet[$config['path_source']])) {
  70. if (isset($changeSet[$config['path']])) {
  71. $originalPath = $changeSet[$config['path']][0];
  72. } else {
  73. $pathProp = $meta->getReflectionProperty($config['path']);
  74. $pathProp->setAccessible(true);
  75. $originalPath = $pathProp->getValue($node);
  76. }
  77. $this->updateNode($om, $node, $ea);
  78. $this->updateNodesChildren($om, $node, $ea, $originalPath);
  79. }
  80. }
  81. /**
  82. * {@inheritdoc}
  83. */
  84. public function processPostPersist($om, $node, $ea)
  85. {
  86. foreach ($this->scheduledForPathProcess as $node) {
  87. $this->updateNode($om, $node, $ea);
  88. unset($this->scheduledForPathProcess[spl_object_hash($node)]);
  89. }
  90. }
  91. /**
  92. * {@inheritdoc}
  93. */
  94. public function onFlushEnd($om)
  95. {
  96. $this->scheduledForPathProcess = array();
  97. }
  98. /**
  99. * {@inheritdoc}
  100. */
  101. public function processPreRemove($om, $node)
  102. {}
  103. /**
  104. * {@inheritdoc}
  105. */
  106. public function processPrePersist($om, $node)
  107. {}
  108. /**
  109. * {@inheritdoc}
  110. */
  111. public function processMetadataLoad($om, $meta)
  112. {}
  113. /**
  114. * {@inheritdoc}
  115. */
  116. public function processScheduledDelete($om, $node)
  117. {
  118. $meta = $om->getClassMetadata(get_class($node));
  119. $config = $this->listener->getConfiguration($om, $meta->name);
  120. $this->removeNode($om, $meta, $config, $node);
  121. }
  122. /**
  123. * Update the $node
  124. *
  125. * @param ObjectManager $om
  126. * @param object $node - target node
  127. * @param object $ea - event adapter
  128. * @return void
  129. */
  130. public function updateNode(ObjectManager $om, $node, $ea)
  131. {
  132. $oid = spl_object_hash($node);
  133. $meta = $om->getClassMetadata(get_class($node));
  134. $config = $this->listener->getConfiguration($om, $meta->name);
  135. $uow = $om->getUnitOfWork();
  136. $parentProp = $meta->getReflectionProperty($config['parent']);
  137. $parentProp->setAccessible(true);
  138. $parent = $parentProp->getValue($node);
  139. $pathProp = $meta->getReflectionProperty($config['path']);
  140. $pathProp->setAccessible(true);
  141. $pathSourceProp = $meta->getReflectionProperty($config['path_source']);
  142. $pathSourceProp->setAccessible(true);
  143. $path = $pathSourceProp->getValue($node).$config['path_separator'];
  144. if ($parent) {
  145. $changeSet = $uow->isScheduledForUpdate($parent) ? $ea->getObjectChangeSet($uow, $parent) : false;
  146. $pathOrPathSourceHasChanged = $changeSet && (isset($changeSet[$config['path_source']]) || isset($changeSet[$config['path']]));
  147. if ($pathOrPathSourceHasChanged || !$pathProp->getValue($parent)) {
  148. $this->updateNode($om, $parent, $ea);
  149. }
  150. $path = $pathProp->getValue($parent).$path;
  151. }
  152. $pathProp->setValue($node, $path);
  153. $changes = array(
  154. $config['path'] => array(null, $path)
  155. );
  156. if (isset($config['level'])) {
  157. $level = substr_count($path, $config['path_separator']);
  158. $levelProp = $meta->getReflectionProperty($config['level']);
  159. $levelProp->setAccessible(true);
  160. $levelProp->setValue($node, $level);
  161. $changes[$config['level']] = array(null, $level);
  162. }
  163. $uow->scheduleExtraUpdate($node, $changes);
  164. $ea->setOriginalObjectProperty($uow, $oid, $config['path'], $path);
  165. }
  166. /**
  167. * Update $node 's children
  168. *
  169. * @param ObjectManager $om
  170. * @param object $node - target node
  171. * @param object $ea - event adapter
  172. * @param string $originalPath - original path of object
  173. * @return void
  174. */
  175. public function updateNodesChildren(ObjectManager $om, $node, $ea, $originalPath)
  176. {
  177. $meta = $om->getClassMetadata(get_class($node));
  178. $config = $this->listener->getConfiguration($om, $meta->name);
  179. $children = $this->getChildren($om, $meta, $config, $originalPath);
  180. foreach ($children as $child) {
  181. $this->updateNode($om, $child, $ea);
  182. }
  183. }
  184. /**
  185. * Remove node and its children
  186. *
  187. * @param ObjectManager $om
  188. * @param object $meta - Metadata
  189. * @param object $config - config
  190. * @param object $node - node to remove
  191. * @return void
  192. */
  193. abstract public function removeNode($om, $meta, $config, $node);
  194. /**
  195. * Returns children of the node with its original path
  196. *
  197. * @param ObjectManager $om
  198. * @param object $meta - Metadata
  199. * @param object $config - config
  200. * @param mixed $originalPath - original path of object
  201. * @return Doctrine\ODM\MongoDB\Cursor
  202. */
  203. abstract public function getChildren($om, $meta, $config, $originalPath);
  204. }