vendor/knplabs/knp-components/src/Knp/Component/Pager/Paginator.php line 59

Open in your IDE?
  1. <?php
  2. namespace Knp\Component\Pager;
  3. use Knp\Component\Pager\Exception\PageLimitInvalidException;
  4. use Knp\Component\Pager\Exception\PageNumberInvalidException;
  5. use Knp\Component\Pager\Exception\PageNumberOutOfRangeException;
  6. use Knp\Component\Pager\Pagination\PaginationInterface;
  7. use Symfony\Component\HttpFoundation\Request;
  8. use Symfony\Component\HttpFoundation\RequestStack;
  9. use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
  10. /**
  11.  * Paginator uses event dispatcher to trigger pagination
  12.  * lifecycle events. Subscribers are expected to paginate
  13.  * wanted target and finally it generates pagination view
  14.  * which is only the result of paginator
  15.  */
  16. final class Paginator implements PaginatorInterface
  17. {
  18.     private EventDispatcherInterface $eventDispatcher;
  19.     /**
  20.      * Default options of paginator
  21.      *
  22.      * @var array<string, scalar>
  23.      */
  24.     private array $defaultOptions = [
  25.         self::PAGE_PARAMETER_NAME => 'page',
  26.         self::SORT_FIELD_PARAMETER_NAME => 'sort',
  27.         self::SORT_DIRECTION_PARAMETER_NAME => 'direction',
  28.         self::FILTER_FIELD_PARAMETER_NAME => 'filterParam',
  29.         self::FILTER_VALUE_PARAMETER_NAME => 'filterValue',
  30.         self::DISTINCT => true,
  31.         self::PAGE_OUT_OF_RANGE => self::PAGE_OUT_OF_RANGE_IGNORE,
  32.         self::DEFAULT_LIMIT => self::DEFAULT_LIMIT_VALUE,
  33.     ];
  34.     private ?RequestStack $requestStack;
  35.     public function __construct(EventDispatcherInterface $eventDispatcherRequestStack $requestStack null)
  36.     {
  37.         $this->eventDispatcher $eventDispatcher;
  38.         $this->requestStack $requestStack;
  39.     }
  40.     /**
  41.      * Override the default paginator options
  42.      * to be reused for paginations
  43.      */ 
  44.     public function setDefaultPaginatorOptions(array $options): void
  45.     {
  46.         $this->defaultOptions \array_merge($this->defaultOptions$options);
  47.     }
  48.     public function paginate($targetint $page 1int $limit null, array $options = []): PaginationInterface
  49.     {
  50.         if ($page <= 0) {
  51.             throw PageNumberInvalidException::create($page);
  52.         }
  53.         $limit $limit ?? $this->defaultOptions[self::DEFAULT_LIMIT];
  54.         if ($limit <= 0) {
  55.             throw PageLimitInvalidException::create($limit);
  56.         }
  57.         $offset = ($page 1) * $limit;
  58.         $options \array_merge($this->defaultOptions$options);
  59.         // normalize default sort field
  60.         if (isset($options[PaginatorInterface::DEFAULT_SORT_FIELD_NAME]) && is_array($options[PaginatorInterface::DEFAULT_SORT_FIELD_NAME])) {
  61.             $options[PaginatorInterface::DEFAULT_SORT_FIELD_NAME] = implode('+'$options[PaginatorInterface::DEFAULT_SORT_FIELD_NAME]);
  62.         }
  63.         $request null === $this->requestStack Request::createFromGlobals() : $this->requestStack->getCurrentRequest();
  64.         // default sort field and direction are set based on options (if available)
  65.         if (isset($options[self::DEFAULT_SORT_FIELD_NAME]) && !$request->query->has($options[self::SORT_FIELD_PARAMETER_NAME])) {
  66.            $request->query->set($options[self::SORT_FIELD_PARAMETER_NAME], $options[self::DEFAULT_SORT_FIELD_NAME]);
  67.             if (!$request->query->has($options[PaginatorInterface::SORT_DIRECTION_PARAMETER_NAME])) {
  68.                 $request->query->set($options[PaginatorInterface::SORT_DIRECTION_PARAMETER_NAME], $options[PaginatorInterface::DEFAULT_SORT_DIRECTION] ?? 'asc');
  69.             }
  70.         }
  71.         // before pagination start
  72.         $beforeEvent = new Event\BeforeEvent($this->eventDispatcher$request);
  73.         $this->eventDispatcher->dispatch($beforeEvent'knp_pager.before');
  74.         // items
  75.         $itemsEvent = new Event\ItemsEvent($offset$limit);
  76.         $itemsEvent->options = &$options;
  77.         $itemsEvent->target = &$target;
  78.         $this->eventDispatcher->dispatch($itemsEvent'knp_pager.items');
  79.         if (!$itemsEvent->isPropagationStopped()) {
  80.             throw new \RuntimeException('One of listeners must count and slice given target');
  81.         }
  82.         if ($page ceil($itemsEvent->count $limit)) {
  83.             $pageOutOfRangeOption $options[PaginatorInterface::PAGE_OUT_OF_RANGE] ?? $this->defaultOptions[PaginatorInterface::PAGE_OUT_OF_RANGE];
  84.             if ($pageOutOfRangeOption === PaginatorInterface::PAGE_OUT_OF_RANGE_FIX && $itemsEvent->count 0) {
  85.                 // replace page number out of range with max page
  86.                 return $this->paginate($target, (int) ceil($itemsEvent->count $limit), $limit$options);
  87.             }
  88.             if ($pageOutOfRangeOption === self::PAGE_OUT_OF_RANGE_THROW_EXCEPTION && $page 1) {
  89.                 throw new PageNumberOutOfRangeException(
  90.                     sprintf('Page number: %d is out of range.'$page),
  91.                     (int) ceil($itemsEvent->count $limit)
  92.                 );
  93.             }
  94.         }
  95.         // pagination initialization event
  96.         $paginationEvent = new Event\PaginationEvent;
  97.         $paginationEvent->target = &$target;
  98.         $paginationEvent->options = &$options;
  99.         $this->eventDispatcher->dispatch($paginationEvent'knp_pager.pagination');
  100.         if (!$paginationEvent->isPropagationStopped()) {
  101.             throw new \RuntimeException('One of listeners must create pagination view');
  102.         }
  103.         // pagination class can be different, with different rendering methods
  104.         $paginationView $paginationEvent->getPagination();
  105.         $paginationView->setCustomParameters($itemsEvent->getCustomPaginationParameters());
  106.         $paginationView->setCurrentPageNumber($page);
  107.         $paginationView->setItemNumberPerPage($limit);
  108.         $paginationView->setTotalItemCount($itemsEvent->count);
  109.         $paginationView->setPaginatorOptions($options);
  110.         $paginationView->setItems($itemsEvent->items);
  111.         // after
  112.         $afterEvent = new Event\AfterEvent($paginationView);
  113.         $this->eventDispatcher->dispatch($afterEvent'knp_pager.after');
  114.         return $paginationView;
  115.     }
  116. }