1<?php 2 3/** 4 * This file is part of the Nette Framework (https://nette.org) 5 * Copyright (c) 2004 David Grudl (https://davidgrudl.com) 6 */ 7 8declare(strict_types=1); 9 10namespace Nette\Utils; 11 12use Nette; 13 14 15/** 16 * Paginating math. 17 * 18 * @property int $page 19 * @property-read int $firstPage 20 * @property-read int|null $lastPage 21 * @property-read int<0,max> $firstItemOnPage 22 * @property-read int<0,max> $lastItemOnPage 23 * @property int $base 24 * @property-read bool $first 25 * @property-read bool $last 26 * @property-read int<0,max>|null $pageCount 27 * @property positive-int $itemsPerPage 28 * @property int<0,max>|null $itemCount 29 * @property-read int<0,max> $offset 30 * @property-read int<0,max>|null $countdownOffset 31 * @property-read int<0,max> $length 32 */ 33class Paginator 34{ 35 use Nette\SmartObject; 36 37 private int $base = 1; 38 39 /** @var positive-int */ 40 private int $itemsPerPage = 1; 41 42 private int $page = 1; 43 44 /** @var int<0, max>|null */ 45 private ?int $itemCount = null; 46 47 48 /** 49 * Sets current page number. 50 */ 51 public function setPage(int $page): static 52 { 53 $this->page = $page; 54 return $this; 55 } 56 57 58 /** 59 * Returns current page number. 60 */ 61 public function getPage(): int 62 { 63 return $this->base + $this->getPageIndex(); 64 } 65 66 67 /** 68 * Returns first page number. 69 */ 70 public function getFirstPage(): int 71 { 72 return $this->base; 73 } 74 75 76 /** 77 * Returns last page number. 78 */ 79 public function getLastPage(): ?int 80 { 81 return $this->itemCount === null 82 ? null 83 : $this->base + max(0, $this->getPageCount() - 1); 84 } 85 86 87 /** 88 * Returns the sequence number of the first element on the page 89 * @return int<0, max> 90 */ 91 public function getFirstItemOnPage(): int 92 { 93 return $this->itemCount !== 0 94 ? $this->offset + 1 95 : 0; 96 } 97 98 99 /** 100 * Returns the sequence number of the last element on the page 101 * @return int<0, max> 102 */ 103 public function getLastItemOnPage(): int 104 { 105 return $this->offset + $this->length; 106 } 107 108 109 /** 110 * Sets first page (base) number. 111 */ 112 public function setBase(int $base): static 113 { 114 $this->base = $base; 115 return $this; 116 } 117 118 119 /** 120 * Returns first page (base) number. 121 */ 122 public function getBase(): int 123 { 124 return $this->base; 125 } 126 127 128 /** 129 * Returns zero-based page number. 130 * @return int<0, max> 131 */ 132 protected function getPageIndex(): int 133 { 134 $index = max(0, $this->page - $this->base); 135 return $this->itemCount === null 136 ? $index 137 : min($index, max(0, $this->getPageCount() - 1)); 138 } 139 140 141 /** 142 * Is the current page the first one? 143 */ 144 public function isFirst(): bool 145 { 146 return $this->getPageIndex() === 0; 147 } 148 149 150 /** 151 * Is the current page the last one? 152 */ 153 public function isLast(): bool 154 { 155 return $this->itemCount === null 156 ? false 157 : $this->getPageIndex() >= $this->getPageCount() - 1; 158 } 159 160 161 /** 162 * Returns the total number of pages. 163 * @return int<0, max>|null 164 */ 165 public function getPageCount(): ?int 166 { 167 return $this->itemCount === null 168 ? null 169 : (int) ceil($this->itemCount / $this->itemsPerPage); 170 } 171 172 173 /** 174 * Sets the number of items to display on a single page. 175 */ 176 public function setItemsPerPage(int $itemsPerPage): static 177 { 178 $this->itemsPerPage = max(1, $itemsPerPage); 179 return $this; 180 } 181 182 183 /** 184 * Returns the number of items to display on a single page. 185 * @return positive-int 186 */ 187 public function getItemsPerPage(): int 188 { 189 return $this->itemsPerPage; 190 } 191 192 193 /** 194 * Sets the total number of items. 195 */ 196 public function setItemCount(?int $itemCount = null): static 197 { 198 $this->itemCount = $itemCount === null ? null : max(0, $itemCount); 199 return $this; 200 } 201 202 203 /** 204 * Returns the total number of items. 205 * @return int<0, max>|null 206 */ 207 public function getItemCount(): ?int 208 { 209 return $this->itemCount; 210 } 211 212 213 /** 214 * Returns the absolute index of the first item on current page. 215 * @return int<0, max> 216 */ 217 public function getOffset(): int 218 { 219 return $this->getPageIndex() * $this->itemsPerPage; 220 } 221 222 223 /** 224 * Returns the absolute index of the first item on current page in countdown paging. 225 * @return int<0, max>|null 226 */ 227 public function getCountdownOffset(): ?int 228 { 229 return $this->itemCount === null 230 ? null 231 : max(0, $this->itemCount - ($this->getPageIndex() + 1) * $this->itemsPerPage); 232 } 233 234 235 /** 236 * Returns the number of items on current page. 237 * @return int<0, max> 238 */ 239 public function getLength(): int 240 { 241 return $this->itemCount === null 242 ? $this->itemsPerPage 243 : min($this->itemsPerPage, $this->itemCount - $this->getPageIndex() * $this->itemsPerPage); 244 } 245} 246