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

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