page($params['page'] ?? 1); $this->limit($params['limit'] ?? 20); $this->total($params['total'] ?? 0); } /** * Creates a pagination instance for the given * collection with a flexible argument api * * @param Collection $collection * @param ...mixed $arguments * @return self */ public static function for(Collection $collection, ...$arguments) { $a = $arguments[0] ?? null; $b = $arguments[1] ?? null; $params = []; if (is_array($a) === true) { /** * First argument is an option array * * $collection->paginate([...]) */ $params = $a; } elseif (is_int($a) === true && $b === null) { /** * First argument is the limit * * $collection->paginate(10) */ $params['limit'] = $a; } elseif (is_int($a) === true && is_int($b) === true) { /** * First argument is the limit, * second argument is the page * * $collection->paginate(10, 2) */ $params['limit'] = $a; $params['page'] = $b; } elseif (is_int($a) === true && is_array($b) === true) { /** * First argument is the limit, * second argument are options * * $collection->paginate(10, [...]) */ $params = $b; $params['limit'] = $a; } // add the total count from the collection $params['total'] = $collection->count(); // remove null values to make later merges work properly $params = array_filter($params); // create the pagination instance return new static($params); } /** * Getter and setter for the current page * * @param int|null $page * @return int|Pagination */ public function page(int $page = null) { if ($page === null) { if ($this->page > $this->pages()) { $this->page = $this->lastPage(); } if ($this->page < 1) { $this->page = $this->firstPage(); } return $this->page; } $this->page = $page; return $this; } /** * Getter and setter for the total number of items * * @param int|null $total * @return int|Pagination */ public function total(int $total = null) { if ($total === null) { return $this->total; } if ($total < 0) { throw new Exception('Invalid total number of items: ' . $total); } $this->total = $total; return $this; } /** * Getter and setter for the number of items per page * * @param int|null $limit * @return int|Pagination */ public function limit(int $limit = null) { if ($limit === null) { return $this->limit; } if ($limit < 1) { throw new Exception('Invalid pagination limit: ' . $limit); } $this->limit = $limit; return $this; } /** * Returns the index of the first item on the page * * @return int */ public function start(): int { $index = $this->page() - 1; if ($index < 0) { $index = 0; } return $index * $this->limit() + 1; } /** * Returns the index of the last item on the page * * @return int */ public function end(): int { $value = ($this->start() - 1) + $this->limit(); if ($value <= $this->total()) { return $value; } return $this->total(); } /** * Returns the total number of pages * * @return int */ public function pages(): int { if ($this->total() === 0) { return 0; } return ceil($this->total() / $this->limit()); } /** * Returns the first page * * @return int */ public function firstPage(): int { return $this->total() === 0 ? 0 : 1; } /** * Returns the last page * * @return int */ public function lastPage(): int { return $this->pages(); } /** * Returns the offset (i.e. for db queries) * * @return int */ public function offset(): int { return $this->start() - 1; } /** * Checks if the given page exists * * @return boolean */ public function hasPage(int $page): bool { if ($page <= 0) { return false; } if ($page > $this->pages()) { return false; } return true; } /** * Checks if there are any pages at all * * @return boolean */ public function hasPages(): bool { return $this->total() > $this->limit(); } /** * Checks if there's a previous page * * @return boolean */ public function hasPrevPage(): bool { return $this->page() > 1; } /** * Returns the previous page * * @return int|null */ public function prevPage() { return $this->hasPrevPage() ? $this->page() - 1 : null; } /** * Checks if there's a next page * * @return boolean */ public function hasNextPage(): bool { return $this->end() < $this->total(); } /** * Returns the next page * * @return int|null */ public function nextPage() { return $this->hasNextPage() ? $this->page() + 1 : null; } /** * Checks if the current page is the first page * * @return boolean */ public function isFirstPage(): bool { return $this->page() === $this->firstPage(); } /** * Checks if the current page is the last page * * @return boolean */ public function isLastPage(): bool { return $this->page() === $this->lastPage(); } /** * Creates a range of page numbers for Google-like pagination * * @return array */ public function range(int $range = 5): array { $page = $this->page(); $pages = $this->pages(); $start = 1; $end = $pages; if ($pages <= $range) { return range($start, $end); } $start = $page - (int)floor($range/2); $end = $page + (int)floor($range/2); if ($start <= 0) { $end += abs($start); $start = 1; } if ($end > $pages) { $start -= $end - $pages; $end = $pages; } return range($start, $end); } /** * Returns the first page of the created range * * @return int */ public function rangeStart(int $range = 5): int { return $this->range($range)[0]; } /** * Returns the last page of the created range * * @return int */ public function rangeEnd(int $range = 5): int { $range = $this->range($range); return array_pop($range); } /** * Returns an array with all properties * * @return array */ public function toArray(): array { return [ 'page' => $this->page(), 'firstPage' => $this->firstPage(), 'lastPage' => $this->lastPage(), 'pages' => $this->pages(), 'offset' => $this->offset(), 'limit' => $this->limit(), 'total' => $this->total(), 'start' => $this->start(), 'end' => $this->end(), ]; } }