vendor/easycorp/easyadmin-bundle/src/Orm/EntityPaginator.php line 97

Open in your IDE?
  1. <?php
  2. namespace EasyCorp\Bundle\EasyAdminBundle\Orm;
  3. use Doctrine\ORM\Query;
  4. use Doctrine\ORM\QueryBuilder;
  5. use Doctrine\ORM\Tools\Pagination\CountWalker;
  6. use Doctrine\ORM\Tools\Pagination\Paginator;
  7. use EasyCorp\Bundle\EasyAdminBundle\Config\Option\EA;
  8. use EasyCorp\Bundle\EasyAdminBundle\Contracts\Orm\EntityPaginatorInterface;
  9. use EasyCorp\Bundle\EasyAdminBundle\Dto\PaginatorDto;
  10. use EasyCorp\Bundle\EasyAdminBundle\Factory\EntityFactory;
  11. use EasyCorp\Bundle\EasyAdminBundle\Router\AdminUrlGenerator;
  12. /**
  13. * @author Javier Eguiluz <javier.eguiluz@gmail.com>
  14. */
  15. final class EntityPaginator implements EntityPaginatorInterface
  16. {
  17. private $adminUrlGenerator;
  18. private $entityFactory;
  19. private $currentPage;
  20. private $pageSize;
  21. private $rangeSize;
  22. private $rangeEdgeSize;
  23. private $results;
  24. private $numResults;
  25. public function __construct(AdminUrlGenerator $adminUrlGenerator, EntityFactory $entityFactory)
  26. {
  27. $this->adminUrlGenerator = $adminUrlGenerator;
  28. $this->entityFactory = $entityFactory;
  29. }
  30. public function paginate(PaginatorDto $paginatorDto, QueryBuilder $queryBuilder): EntityPaginatorInterface
  31. {
  32. $this->pageSize = $paginatorDto->getPageSize();
  33. $this->rangeSize = $paginatorDto->getRangeSize();
  34. $this->rangeEdgeSize = $paginatorDto->getRangeEdgeSize();
  35. $this->currentPage = max(1, $paginatorDto->getPageNumber());
  36. $firstResult = ($this->currentPage - 1) * $this->pageSize;
  37. /** @var Query $query */
  38. $query = $queryBuilder
  39. ->setFirstResult($firstResult)
  40. ->setMaxResults($this->pageSize)
  41. ->getQuery();
  42. if (0 === \count($queryBuilder->getDQLPart('join'))) {
  43. $query->setHint(CountWalker::HINT_DISTINCT, false);
  44. }
  45. $paginator = new Paginator($query, $paginatorDto->fetchJoinCollection());
  46. if (null === $useOutputWalkers = $paginatorDto->useOutputWalkers()) {
  47. $havingPart = $queryBuilder->getDQLPart('having');
  48. if (\is_array($havingPart)) {
  49. $useOutputWalkers = \count($havingPart) > 0;
  50. } else {
  51. $useOutputWalkers = null !== $havingPart;
  52. }
  53. }
  54. $paginator->setUseOutputWalkers($useOutputWalkers);
  55. $this->results = $paginator->getIterator();
  56. $this->numResults = $paginator->count();
  57. return $this;
  58. }
  59. public function generateUrlForPage(int $page): string
  60. {
  61. return $this->adminUrlGenerator->set(EA::PAGE, $page)->includeReferrer()->generateUrl();
  62. }
  63. public function getCurrentPage(): int
  64. {
  65. return $this->currentPage;
  66. }
  67. public function getLastPage(): int
  68. {
  69. return (int) ceil($this->numResults / $this->pageSize);
  70. }
  71. /**
  72. * It returns the closest available pages around the current page.
  73. * E.g. a paginator with 35 pages, if current page = 1, returns [1, 2, 3, 4, null, 35]
  74. * if current page = 18, returns [1, null, 15, 16, 17, 18, 19, 20, 21, null, 35]
  75. * NULL values mean a gap in the pagination (they can be represented as ellipsis in the templates).
  76. *
  77. * This code was inspired by https://github.com/django/django/blob/55fabc53373a8c7ef31d8c4cffd2a07be0a88c2e/django/core/paginator.py#L134
  78. * (c) Django Project
  79. *
  80. * @return int[]
  81. */
  82. public function getPageRange(int $pagesOnEachSide = null, int $pagesOnEdges = null): iterable
  83. {
  84. $pagesOnEachSide = $pagesOnEachSide ?? $this->rangeSize;
  85. $pagesOnEdges = $pagesOnEdges ?? $this->rangeEdgeSize;
  86. if (0 === $pagesOnEachSide) {
  87. return null;
  88. }
  89. if ($this->getLastPage() <= ($pagesOnEachSide + $pagesOnEdges) * 2) {
  90. return yield from range(1, $this->getLastPage());
  91. }
  92. if ($this->getCurrentPage() > ($pagesOnEachSide + $pagesOnEdges + 1)) {
  93. yield from range(1, $pagesOnEdges);
  94. yield null;
  95. yield from range($this->getCurrentPage() - $pagesOnEachSide, $this->getCurrentPage());
  96. } else {
  97. yield from range(1, $this->getCurrentPage());
  98. }
  99. if ($this->getCurrentPage() < ($this->getLastPage() - $pagesOnEachSide - $pagesOnEdges - 1)) {
  100. yield from range($this->getCurrentPage() + 1, $this->getCurrentPage() + $pagesOnEachSide);
  101. yield null;
  102. yield from range($this->getLastPage() - $pagesOnEdges + 1, $this->getLastPage());
  103. } elseif ($this->getCurrentPage() + 1 <= $this->getLastPage()) {
  104. yield from range($this->getCurrentPage() + 1, $this->getLastPage());
  105. }
  106. }
  107. public function getPageSize(): int
  108. {
  109. return $this->pageSize;
  110. }
  111. public function hasPreviousPage(): bool
  112. {
  113. return $this->currentPage > 1;
  114. }
  115. public function getPreviousPage(): int
  116. {
  117. return max(1, $this->currentPage - 1);
  118. }
  119. public function hasNextPage(): bool
  120. {
  121. return $this->currentPage < $this->getLastPage();
  122. }
  123. public function getNextPage(): int
  124. {
  125. return min($this->getLastPage(), $this->currentPage + 1);
  126. }
  127. public function hasToPaginate(): bool
  128. {
  129. return $this->numResults > $this->pageSize;
  130. }
  131. public function isOutOfRange(): bool
  132. {
  133. if (1 === $this->currentPage) {
  134. return false;
  135. }
  136. return $this->currentPage < 1 || $this->currentPage > $this->getLastPage();
  137. }
  138. public function getNumResults(): int
  139. {
  140. return $this->numResults;
  141. }
  142. public function getResults(): ?iterable
  143. {
  144. return $this->results;
  145. }
  146. public function getResultsAsJson(): string
  147. {
  148. foreach ($this->getResults() ?? [] as $entityInstance) {
  149. $entityDto = $this->entityFactory->createForEntityInstance($entityInstance);
  150. $jsonResult['results'][] = [
  151. EA::ENTITY_ID => $entityDto->getPrimaryKeyValueAsString(),
  152. 'entityAsString' => $entityDto->toString(),
  153. ];
  154. }
  155. $nextPageUrl = !$this->hasNextPage() ? null : $this->adminUrlGenerator->set(EA::PAGE, $this->getNextPage())->removeReferrer()->generateUrl();
  156. $jsonResult['next_page'] = $nextPageUrl;
  157. return json_encode($jsonResult);
  158. }
  159. }