vendor/gedmo/doctrine-extensions/src/Tree/Entity/Repository/AbstractTreeRepository.php line 45

  1. <?php
  2. /*
  3. * This file is part of the Doctrine Behavioral Extensions package.
  4. * (c) Gediminas Morkevicius <gediminas.morkevicius@gmail.com> http://www.gediminasm.org
  5. * For the full copyright and license information, please view the LICENSE
  6. * file that was distributed with this source code.
  7. */
  8. namespace Gedmo\Tree\Entity\Repository;
  9. use Doctrine\ORM\EntityManagerInterface;
  10. use Doctrine\ORM\EntityRepository;
  11. use Doctrine\ORM\Mapping\ClassMetadata;
  12. use Doctrine\ORM\Query;
  13. use Doctrine\ORM\QueryBuilder;
  14. use Gedmo\Exception\InvalidArgumentException;
  15. use Gedmo\Exception\InvalidMappingException;
  16. use Gedmo\Tool\Wrapper\EntityWrapper;
  17. use Gedmo\Tree\RepositoryInterface;
  18. use Gedmo\Tree\RepositoryUtils;
  19. use Gedmo\Tree\RepositoryUtilsInterface;
  20. use Gedmo\Tree\TreeListener;
  21. /**
  22. * @template T of object
  23. *
  24. * @template-extends EntityRepository<T>
  25. *
  26. * @template-implements RepositoryInterface<T>
  27. */
  28. abstract class AbstractTreeRepository extends EntityRepository implements RepositoryInterface
  29. {
  30. /**
  31. * Tree listener on event manager
  32. *
  33. * @var TreeListener
  34. */
  35. protected $listener;
  36. /**
  37. * Repository utils
  38. *
  39. * @var RepositoryUtilsInterface
  40. */
  41. protected $repoUtils;
  42. /** @param ClassMetadata<T> $class */
  43. public function __construct(EntityManagerInterface $em, ClassMetadata $class)
  44. {
  45. parent::__construct($em, $class);
  46. $treeListener = null;
  47. foreach ($em->getEventManager()->getAllListeners() as $listeners) {
  48. foreach ($listeners as $listener) {
  49. if ($listener instanceof TreeListener) {
  50. $treeListener = $listener;
  51. break 2;
  52. }
  53. }
  54. }
  55. if (null === $treeListener) {
  56. throw new InvalidMappingException('Tree listener was not found on your entity manager, it must be hooked into the event manager');
  57. }
  58. $this->listener = $treeListener;
  59. if (!$this->validate()) {
  60. throw new InvalidMappingException('This repository cannot be used for tree type: '.$treeListener->getStrategy($em, $class->getName())->getName());
  61. }
  62. $this->repoUtils = new RepositoryUtils($this->getEntityManager(), $this->getClassMetadata(), $this->listener, $this);
  63. }
  64. /**
  65. * Sets the RepositoryUtilsInterface instance
  66. *
  67. * @return static
  68. */
  69. public function setRepoUtils(RepositoryUtilsInterface $repoUtils)
  70. {
  71. $this->repoUtils = $repoUtils;
  72. return $this;
  73. }
  74. /**
  75. * Returns the RepositoryUtilsInterface instance
  76. *
  77. * @return RepositoryUtilsInterface|null
  78. */
  79. public function getRepoUtils()
  80. {
  81. return $this->repoUtils;
  82. }
  83. public function childCount($node = null, $direct = false)
  84. {
  85. $meta = $this->getClassMetadata();
  86. if (is_object($node)) {
  87. if (!is_a($node, $meta->getName())) {
  88. throw new InvalidArgumentException('Node is not related to this repository');
  89. }
  90. $wrapped = new EntityWrapper($node, $this->getEntityManager());
  91. if (!$wrapped->hasValidIdentifier()) {
  92. throw new InvalidArgumentException('Node is not managed by UnitOfWork');
  93. }
  94. }
  95. $qb = $this->getChildrenQueryBuilder($node, $direct);
  96. // We need to remove the ORDER BY DQL part since some vendors could throw an error
  97. // in count queries
  98. $dqlParts = $qb->getDQLParts();
  99. // We need to check first if there's an ORDER BY DQL part, because resetDQLPart doesn't
  100. // check if its internal array has an "orderby" index
  101. if (isset($dqlParts['orderBy'])) {
  102. $qb->resetDQLPart('orderBy');
  103. }
  104. $aliases = $qb->getRootAliases();
  105. $alias = $aliases[0];
  106. $qb->select('COUNT('.$alias.')');
  107. return (int) $qb->getQuery()->getSingleScalarResult();
  108. }
  109. /**
  110. * @see RepositoryUtilsInterface::childrenHierarchy
  111. */
  112. public function childrenHierarchy($node = null, $direct = false, array $options = [], $includeNode = false)
  113. {
  114. return $this->repoUtils->childrenHierarchy($node, $direct, $options, $includeNode);
  115. }
  116. /**
  117. * @see RepositoryUtilsInterface::buildTree
  118. */
  119. public function buildTree(array $nodes, array $options = [])
  120. {
  121. return $this->repoUtils->buildTree($nodes, $options);
  122. }
  123. /**
  124. * @see RepositoryUtilsInterface::buildTreeArray
  125. */
  126. public function buildTreeArray(array $nodes)
  127. {
  128. return $this->repoUtils->buildTreeArray($nodes);
  129. }
  130. /**
  131. * @see RepositoryUtilsInterface::setChildrenIndex
  132. */
  133. public function setChildrenIndex($childrenIndex)
  134. {
  135. $this->repoUtils->setChildrenIndex($childrenIndex);
  136. }
  137. /**
  138. * @see RepositoryUtilsInterface::getChildrenIndex
  139. */
  140. public function getChildrenIndex()
  141. {
  142. return $this->repoUtils->getChildrenIndex();
  143. }
  144. /**
  145. * Get all root nodes query builder
  146. *
  147. * @param string|string[]|null $sortByField Sort by field
  148. * @param string|string[] $direction Sort direction ("asc" or "desc")
  149. *
  150. * @return QueryBuilder QueryBuilder object
  151. */
  152. abstract public function getRootNodesQueryBuilder($sortByField = null, $direction = 'asc');
  153. /**
  154. * Get all root nodes query
  155. *
  156. * @param string|string[]|null $sortByField Sort by field
  157. * @param string|string[] $direction Sort direction ("asc" or "desc")
  158. *
  159. * @return Query Query object
  160. */
  161. abstract public function getRootNodesQuery($sortByField = null, $direction = 'asc');
  162. /**
  163. * Returns a QueryBuilder configured to return an array of nodes suitable for buildTree method
  164. *
  165. * @param object $node Root node
  166. * @param bool $direct Obtain direct children?
  167. * @param array<string, mixed> $options Options
  168. * @param bool $includeNode Include node in results?
  169. *
  170. * @return QueryBuilder QueryBuilder object
  171. */
  172. abstract public function getNodesHierarchyQueryBuilder($node = null, $direct = false, array $options = [], $includeNode = false);
  173. /**
  174. * Returns a Query configured to return an array of nodes suitable for buildTree method
  175. *
  176. * @param object $node Root node
  177. * @param bool $direct Obtain direct children?
  178. * @param array<string, mixed> $options Options
  179. * @param bool $includeNode Include node in results?
  180. *
  181. * @return Query Query object
  182. */
  183. abstract public function getNodesHierarchyQuery($node = null, $direct = false, array $options = [], $includeNode = false);
  184. /**
  185. * Get list of children followed by given $node. This returns a QueryBuilder object
  186. *
  187. * @param object|null $node If null, all tree nodes will be taken
  188. * @param bool $direct True to take only direct children
  189. * @param string|string[]|null $sortByField Field name or array of fields names to sort by
  190. * @param string|string[] $direction Sort order ('asc'|'desc'|'ASC'|'DESC'). If $sortByField is an array, this may also be an array with matching number of elements
  191. * @param bool $includeNode Include the root node in results?
  192. *
  193. * @return QueryBuilder QueryBuilder object
  194. *
  195. * @phpstan-param 'asc'|'desc'|'ASC'|'DESC'|array<int, 'asc'|'desc'|'ASC'|'DESC'> $direction
  196. */
  197. abstract public function getChildrenQueryBuilder($node = null, $direct = false, $sortByField = null, $direction = 'ASC', $includeNode = false);
  198. /**
  199. * Get list of children followed by given $node. This returns a Query
  200. *
  201. * @param object|null $node If null, all tree nodes will be taken
  202. * @param bool $direct True to take only direct children
  203. * @param string|string[]|null $sortByField Field name or array of fields names to sort by
  204. * @param string|string[] $direction Sort order ('asc'|'desc'|'ASC'|'DESC'). If $sortByField is an array, this may also be an array with matching number of elements
  205. * @param bool $includeNode Include the root node in results?
  206. *
  207. * @return Query Query object
  208. *
  209. * @phpstan-param 'asc'|'desc'|'ASC'|'DESC'|array<int, 'asc'|'desc'|'ASC'|'DESC'> $direction
  210. */
  211. abstract public function getChildrenQuery($node = null, $direct = false, $sortByField = null, $direction = 'ASC', $includeNode = false);
  212. /**
  213. * @return QueryBuilder
  214. */
  215. protected function getQueryBuilder()
  216. {
  217. return $this->getEntityManager()->createQueryBuilder();
  218. }
  219. /**
  220. * Checks if current repository is right
  221. * for currently used tree strategy
  222. *
  223. * @return bool
  224. */
  225. abstract protected function validate();
  226. }