RepositoryUtils.php 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. <?php
  2. namespace Gedmo\Tree;
  3. use Doctrine\Common\Persistence\Mapping\ClassMetadata;
  4. use Doctrine\Common\Persistence\ObjectManager;
  5. use Gedmo\Exception\InvalidArgumentException;
  6. class RepositoryUtils implements RepositoryUtilsInterface
  7. {
  8. /** @var \Doctrine\Common\Persistence\Mapping\ClassMetadata */
  9. protected $meta;
  10. /** @var \Gedmo\Tree\TreeListener */
  11. protected $listener;
  12. /** @var \Doctrine\Common\Persistence\ObjectManager */
  13. protected $om;
  14. /** @var \Gedmo\Tree\RepositoryInterface */
  15. protected $repo;
  16. /**
  17. * This index is used to hold the children of a node
  18. * when using any of the buildTree related methods.
  19. *
  20. * @var string
  21. */
  22. protected $childrenIndex = '__children';
  23. public function __construct(ObjectManager $om, ClassMetadata $meta, $listener, $repo)
  24. {
  25. $this->om = $om;
  26. $this->meta = $meta;
  27. $this->listener = $listener;
  28. $this->repo = $repo;
  29. }
  30. public function getClassMetadata()
  31. {
  32. return $this->meta;
  33. }
  34. /**
  35. * {@inheritDoc}
  36. */
  37. public function childrenHierarchy($node = null, $direct = false, array $options = array(), $includeNode = false)
  38. {
  39. $meta = $this->getClassMetadata();
  40. if ($node !== null) {
  41. if ($node instanceof $meta->name) {
  42. $wrapperClass = $this->om instanceof \Doctrine\ORM\EntityManager ?
  43. '\Gedmo\Tool\Wrapper\EntityWrapper' :
  44. '\Gedmo\Tool\Wrapper\MongoDocumentWrapper';
  45. $wrapped = new $wrapperClass($node, $this->om);
  46. if (!$wrapped->hasValidIdentifier()) {
  47. throw new InvalidArgumentException("Node is not managed by UnitOfWork");
  48. }
  49. }
  50. } else {
  51. $includeNode = true;
  52. }
  53. // Gets the array of $node results. It must be ordered by depth
  54. $nodes = $this->repo->getNodesHierarchy($node, $direct, $options, $includeNode);
  55. return $this->repo->buildTree($nodes, $options);
  56. }
  57. /**
  58. * {@inheritDoc}
  59. */
  60. public function buildTree(array $nodes, array $options = array())
  61. {
  62. $meta = $this->getClassMetadata();
  63. $nestedTree = $this->repo->buildTreeArray($nodes);
  64. $default = array(
  65. 'decorate' => false,
  66. 'rootOpen' => '<ul>',
  67. 'rootClose' => '</ul>',
  68. 'childOpen' => '<li>',
  69. 'childClose' => '</li>',
  70. 'nodeDecorator' => function ($node) use ($meta) {
  71. // override and change it, guessing which field to use
  72. if ($meta->hasField('title')) {
  73. $field = 'title';
  74. } elseif ($meta->hasField('name')) {
  75. $field = 'name';
  76. } else {
  77. throw new InvalidArgumentException("Cannot find any representation field");
  78. }
  79. return $node[$field];
  80. }
  81. );
  82. $options = array_merge($default, $options);
  83. // If you don't want any html output it will return the nested array
  84. if (!$options['decorate']) {
  85. return $nestedTree;
  86. }
  87. if (!count($nestedTree)) {
  88. return '';
  89. }
  90. $childrenIndex = $this->childrenIndex;
  91. $build = function($tree) use (&$build, &$options, $childrenIndex) {
  92. $output = is_string($options['rootOpen']) ? $options['rootOpen'] : $options['rootOpen']($tree);
  93. foreach ($tree as $node) {
  94. $output .= is_string($options['childOpen']) ? $options['childOpen'] : $options['childOpen']($node);
  95. $output .= $options['nodeDecorator']($node);
  96. if (count($node[$childrenIndex]) > 0) {
  97. $output .= $build($node[$childrenIndex]);
  98. }
  99. $output .= is_string($options['childClose']) ? $options['childClose'] : $options['childClose']($node);
  100. }
  101. return $output . (is_string($options['rootClose']) ? $options['rootClose'] : $options['rootClose']($tree));
  102. };
  103. return $build($nestedTree);
  104. }
  105. /**
  106. * {@inheritDoc}
  107. */
  108. public function buildTreeArray(array $nodes)
  109. {
  110. $meta = $this->getClassMetadata();
  111. $config = $this->listener->getConfiguration($this->om, $meta->name);
  112. $nestedTree = array();
  113. $l = 0;
  114. if (count($nodes) > 0) {
  115. // Node Stack. Used to help building the hierarchy
  116. $stack = array();
  117. foreach ($nodes as $child) {
  118. $item = $child;
  119. $item[$this->childrenIndex] = array();
  120. // Number of stack items
  121. $l = count($stack);
  122. // Check if we're dealing with different levels
  123. while($l > 0 && $stack[$l - 1][$config['level']] >= $item[$config['level']]) {
  124. array_pop($stack);
  125. $l--;
  126. }
  127. // Stack is empty (we are inspecting the root)
  128. if ($l == 0) {
  129. // Assigning the root child
  130. $i = count($nestedTree);
  131. $nestedTree[$i] = $item;
  132. $stack[] = &$nestedTree[$i];
  133. } else {
  134. // Add child to parent
  135. $i = count($stack[$l - 1][$this->childrenIndex]);
  136. $stack[$l - 1][$this->childrenIndex][$i] = $item;
  137. $stack[] = &$stack[$l - 1][$this->childrenIndex][$i];
  138. }
  139. }
  140. }
  141. return $nestedTree;
  142. }
  143. /**
  144. * {@inheritDoc}
  145. */
  146. public function setChildrenIndex($childrenIndex)
  147. {
  148. $this->childrenIndex = $childrenIndex;
  149. }
  150. /**
  151. * {@inheritDoc}
  152. */
  153. public function getChildrenIndex()
  154. {
  155. return $this->childrenIndex;
  156. }
  157. }