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

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