'page', 'limit' => 10, 'display' => 5, 'opening' => 0, 'ending' => 0, 'url' => null ]; /** @var array */ private $items; public function isEnabled(): bool { return $this->count() > 1; } public function getOptions(): array { return $this->options; } public function getRoute(): ?Route { return $this->route; } public function getTotalPages(): int { return $this->pages; } public function getPageNumber(): int { return $this->page; } public function getPrevNumber(int $count = 1): ?int { $page = $this->page - $count; return $page >= 1 ? $page : null; } public function getNextNumber(int $count = 1): ?int { $page = $this->page + $count; return $page <= $this->pages ? $page : null; } public function getPage(int $page, string $label = null): ?PaginationPage { if ($page < 1 || $page > $this->pages) { return null; } $start = ($page - 1) * $this->limit; if ($this->getOptions()['type'] === 'page') { $name = 'page'; $offset = $page; } else { $name = 'start'; $offset = $start; } return new PaginationPage( [ 'label' => $label ?? (string)$page, 'number' => $page, 'offset_start' => $start, 'offset_end' => min($start + $this->limit, $this->total) - 1, 'enabled' => $page !== $this->page || $this->viewAll, 'active' => $page === $this->page, 'route' => $this->route->withGravParam($name, $offset) ] ); } public function getFirstPage(string $label = null, int $count = 0): ?PaginationPage { return $this->getPage(1 + $count, $label ?? $this->getOptions()['label_first'] ?? null); } public function getPrevPage(string $label = null, int $count = 1): ?PaginationPage { return $this->getPage($this->page - $count, $label ?? $this->getOptions()['label_prev'] ?? null); } public function getNextPage(string $label = null, int $count = 1): ?PaginationPage { return $this->getPage($this->page + $count, $label ?? $this->getOptions()['label_next'] ?? null); } public function getLastPage(string $label = null, int $count = 0): ?PaginationPage { return $this->getPage($this->pages - $count, $label ?? $this->getOptions()['label_last'] ?? null); } public function getStart(): int { return $this->start; } public function getLimit(): int { return $this->limit; } public function getTotal(): int { return $this->total; } public function count(): int { $this->loadItems(); return \count($this->items); } public function getIterator() { $this->loadItems(); return new \ArrayIterator($this->items); } public function getPages(): array { $this->loadItems(); return $this->items; } protected function loadItems() { $this->calculateRange(); // Make list like: 1 ... 4 5 6 ... 10 $range = range($this->pagesStart, $this->pagesStop); //$range[] = 1; //$range[] = $this->pages; natsort($range); $range = array_unique($range); $this->items = []; foreach ($range as $i) { $this->items[$i] = $this->getPage($i); } } protected function setRoute(Route $route) { $this->route = $route; return $this; } protected function setOptions(array $options = null) { $this->options = $options ? array_merge($this->defaultOptions, $options) : $this->defaultOptions; return $this; } protected function setPage(int $page = null) { $this->page = (int)max($page, 1); $this->start = null; return $this; } /** * @param int $start * @return $this */ protected function setStart(int $start = null) { $this->start = (int)max($start, 0); $this->page = null; return $this; } /** * @param int|null $limit * @return $this */ protected function setLimit(int $limit = null) { $this->limit = (int)max($limit ?? $this->getOptions()['limit'], 0); // No limit, display all records in a single page. $this->viewAll = !$limit; return $this; } /** * @param int $total * @return $this */ protected function setTotal(int $total) { $this->total = (int)max($total, 0); return $this; } protected function initialize(Route $route, int $total, int $pos = null, int $limit = null, array $options = null) { $this->setRoute($route); $this->setOptions($options); $this->setTotal($total); if ($this->getOptions()['type'] === 'start') { $this->setStart($pos); } else { $this->setPage($pos); } $this->setLimit($limit); $this->calculateLimits(); } protected function calculateLimits() { $limit = $this->limit; $total = $this->total; if (!$limit || $limit > $total) { // All records fit into a single page. $this->start = 0; $this->page = 1; $this->pages = 1; return; } if (null === $this->start) { // If we are using page, convert it to start. $this->start = (int)(($this->page - 1) * $limit); } if ($this->start > $total - $limit) { // If start is greater than total count (i.e. we are asked to display records that don't exist) // then set start to display the last natural page of results. $this->start = (int)max(0, (ceil($total / $limit) - 1) * $limit); } // Set the total pages and current page values. $this->page = (int)ceil(($this->start + 1) / $limit); $this->pages = (int)ceil($total / $limit); } protected function calculateRange() { $options = $this->getOptions(); $displayed = $options['display']; $opening = $options['opening']; $ending = $options['ending']; // Set the pagination iteration loop values. $this->pagesStart = $this->page - (int)($displayed / 2); if ($this->pagesStart < 1 + $opening) { $this->pagesStart = 1 + $opening; } if ($this->pagesStart + $displayed - $opening > $this->pages) { $this->pagesStop = $this->pages; if ($this->pages < $displayed) { $this->pagesStart = 1 + $opening; } else { $this->pagesStart = $this->pages - $displayed + 1 + $opening; } } else { $this->pagesStop = (int)max(1, $this->pagesStart + $displayed - 1 - $ending); } } }