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

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