AbstractPaginator.php 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615
  1. <?php
  2. namespace Illuminate\Pagination;
  3. use Closure;
  4. use Illuminate\Support\Str;
  5. use Illuminate\Support\Collection;
  6. use Illuminate\Contracts\Support\Htmlable;
  7. /**
  8. * @mixin \Illuminate\Support\Collection
  9. */
  10. abstract class AbstractPaginator implements Htmlable
  11. {
  12. /**
  13. * All of the items being paginated.
  14. *
  15. * @var \Illuminate\Support\Collection
  16. */
  17. protected $items;
  18. /**
  19. * The number of items to be shown per page.
  20. *
  21. * @var int
  22. */
  23. protected $perPage;
  24. /**
  25. * The current page being "viewed".
  26. *
  27. * @var int
  28. */
  29. protected $currentPage;
  30. /**
  31. * The base path to assign to all URLs.
  32. *
  33. * @var string
  34. */
  35. protected $path = '/';
  36. /**
  37. * The query parameters to add to all URLs.
  38. *
  39. * @var array
  40. */
  41. protected $query = [];
  42. /**
  43. * The URL fragment to add to all URLs.
  44. *
  45. * @var string|null
  46. */
  47. protected $fragment;
  48. /**
  49. * The query string variable used to store the page.
  50. *
  51. * @var string
  52. */
  53. protected $pageName = 'page';
  54. /**
  55. * The current path resolver callback.
  56. *
  57. * @var \Closure
  58. */
  59. protected static $currentPathResolver;
  60. /**
  61. * The current page resolver callback.
  62. *
  63. * @var \Closure
  64. */
  65. protected static $currentPageResolver;
  66. /**
  67. * The view factory resolver callback.
  68. *
  69. * @var \Closure
  70. */
  71. protected static $viewFactoryResolver;
  72. /**
  73. * The default pagination view.
  74. *
  75. * @var string
  76. */
  77. public static $defaultView = 'pagination::bootstrap-4';
  78. /**
  79. * The default "simple" pagination view.
  80. *
  81. * @var string
  82. */
  83. public static $defaultSimpleView = 'pagination::simple-bootstrap-4';
  84. /**
  85. * Determine if the given value is a valid page number.
  86. *
  87. * @param int $page
  88. * @return bool
  89. */
  90. protected function isValidPageNumber($page)
  91. {
  92. return $page >= 1 && filter_var($page, FILTER_VALIDATE_INT) !== false;
  93. }
  94. /**
  95. * Get the URL for the previous page.
  96. *
  97. * @return string|null
  98. */
  99. public function previousPageUrl()
  100. {
  101. if ($this->currentPage() > 1) {
  102. return $this->url($this->currentPage() - 1);
  103. }
  104. }
  105. /**
  106. * Create a range of pagination URLs.
  107. *
  108. * @param int $start
  109. * @param int $end
  110. * @return array
  111. */
  112. public function getUrlRange($start, $end)
  113. {
  114. return collect(range($start, $end))->mapWithKeys(function ($page) {
  115. return [$page => $this->url($page)];
  116. })->all();
  117. }
  118. /**
  119. * Get the URL for a given page number.
  120. *
  121. * @param int $page
  122. * @return string
  123. */
  124. public function url($page)
  125. {
  126. if ($page <= 0) {
  127. $page = 1;
  128. }
  129. // If we have any extra query string key / value pairs that need to be added
  130. // onto the URL, we will put them in query string form and then attach it
  131. // to the URL. This allows for extra information like sortings storage.
  132. $parameters = [$this->pageName => $page];
  133. if (count($this->query) > 0) {
  134. $parameters = array_merge($this->query, $parameters);
  135. }
  136. return $this->path
  137. .(Str::contains($this->path, '?') ? '&' : '?')
  138. .http_build_query($parameters, '', '&')
  139. .$this->buildFragment();
  140. }
  141. /**
  142. * Get / set the URL fragment to be appended to URLs.
  143. *
  144. * @param string|null $fragment
  145. * @return $this|string|null
  146. */
  147. public function fragment($fragment = null)
  148. {
  149. if (is_null($fragment)) {
  150. return $this->fragment;
  151. }
  152. $this->fragment = $fragment;
  153. return $this;
  154. }
  155. /**
  156. * Add a set of query string values to the paginator.
  157. *
  158. * @param array|string $key
  159. * @param string|null $value
  160. * @return $this
  161. */
  162. public function appends($key, $value = null)
  163. {
  164. if (is_array($key)) {
  165. return $this->appendArray($key);
  166. }
  167. return $this->addQuery($key, $value);
  168. }
  169. /**
  170. * Add an array of query string values.
  171. *
  172. * @param array $keys
  173. * @return $this
  174. */
  175. protected function appendArray(array $keys)
  176. {
  177. foreach ($keys as $key => $value) {
  178. $this->addQuery($key, $value);
  179. }
  180. return $this;
  181. }
  182. /**
  183. * Add a query string value to the paginator.
  184. *
  185. * @param string $key
  186. * @param string $value
  187. * @return $this
  188. */
  189. protected function addQuery($key, $value)
  190. {
  191. if ($key !== $this->pageName) {
  192. $this->query[$key] = $value;
  193. }
  194. return $this;
  195. }
  196. /**
  197. * Build the full fragment portion of a URL.
  198. *
  199. * @return string
  200. */
  201. protected function buildFragment()
  202. {
  203. return $this->fragment ? '#'.$this->fragment : '';
  204. }
  205. /**
  206. * Load a set of relationships onto the mixed relationship collection.
  207. *
  208. * @param string $relation
  209. * @param array $relations
  210. * @return $this
  211. */
  212. public function loadMorph($relation, $relations)
  213. {
  214. $this->getCollection()->loadMorph($relation, $relations);
  215. return $this;
  216. }
  217. /**
  218. * Get the slice of items being paginated.
  219. *
  220. * @return array
  221. */
  222. public function items()
  223. {
  224. return $this->items->all();
  225. }
  226. /**
  227. * Get the number of the first item in the slice.
  228. *
  229. * @return int
  230. */
  231. public function firstItem()
  232. {
  233. return count($this->items) > 0 ? ($this->currentPage - 1) * $this->perPage + 1 : null;
  234. }
  235. /**
  236. * Get the number of the last item in the slice.
  237. *
  238. * @return int
  239. */
  240. public function lastItem()
  241. {
  242. return count($this->items) > 0 ? $this->firstItem() + $this->count() - 1 : null;
  243. }
  244. /**
  245. * Get the number of items shown per page.
  246. *
  247. * @return int
  248. */
  249. public function perPage()
  250. {
  251. return $this->perPage;
  252. }
  253. /**
  254. * Determine if there are enough items to split into multiple pages.
  255. *
  256. * @return bool
  257. */
  258. public function hasPages()
  259. {
  260. return $this->currentPage() != 1 || $this->hasMorePages();
  261. }
  262. /**
  263. * Determine if the paginator is on the first page.
  264. *
  265. * @return bool
  266. */
  267. public function onFirstPage()
  268. {
  269. return $this->currentPage() <= 1;
  270. }
  271. /**
  272. * Get the current page.
  273. *
  274. * @return int
  275. */
  276. public function currentPage()
  277. {
  278. return $this->currentPage;
  279. }
  280. /**
  281. * Get the query string variable used to store the page.
  282. *
  283. * @return string
  284. */
  285. public function getPageName()
  286. {
  287. return $this->pageName;
  288. }
  289. /**
  290. * Set the query string variable used to store the page.
  291. *
  292. * @param string $name
  293. * @return $this
  294. */
  295. public function setPageName($name)
  296. {
  297. $this->pageName = $name;
  298. return $this;
  299. }
  300. /**
  301. * Set the base path to assign to all URLs.
  302. *
  303. * @param string $path
  304. * @return $this
  305. */
  306. public function withPath($path)
  307. {
  308. return $this->setPath($path);
  309. }
  310. /**
  311. * Set the base path to assign to all URLs.
  312. *
  313. * @param string $path
  314. * @return $this
  315. */
  316. public function setPath($path)
  317. {
  318. $this->path = $path;
  319. return $this;
  320. }
  321. /**
  322. * Resolve the current request path or return the default value.
  323. *
  324. * @param string $default
  325. * @return string
  326. */
  327. public static function resolveCurrentPath($default = '/')
  328. {
  329. if (isset(static::$currentPathResolver)) {
  330. return call_user_func(static::$currentPathResolver);
  331. }
  332. return $default;
  333. }
  334. /**
  335. * Set the current request path resolver callback.
  336. *
  337. * @param \Closure $resolver
  338. * @return void
  339. */
  340. public static function currentPathResolver(Closure $resolver)
  341. {
  342. static::$currentPathResolver = $resolver;
  343. }
  344. /**
  345. * Resolve the current page or return the default value.
  346. *
  347. * @param string $pageName
  348. * @param int $default
  349. * @return int
  350. */
  351. public static function resolveCurrentPage($pageName = 'page', $default = 1)
  352. {
  353. if (isset(static::$currentPageResolver)) {
  354. return call_user_func(static::$currentPageResolver, $pageName);
  355. }
  356. return $default;
  357. }
  358. /**
  359. * Set the current page resolver callback.
  360. *
  361. * @param \Closure $resolver
  362. * @return void
  363. */
  364. public static function currentPageResolver(Closure $resolver)
  365. {
  366. static::$currentPageResolver = $resolver;
  367. }
  368. /**
  369. * Get an instance of the view factory from the resolver.
  370. *
  371. * @return \Illuminate\Contracts\View\Factory
  372. */
  373. public static function viewFactory()
  374. {
  375. return call_user_func(static::$viewFactoryResolver);
  376. }
  377. /**
  378. * Set the view factory resolver callback.
  379. *
  380. * @param \Closure $resolver
  381. * @return void
  382. */
  383. public static function viewFactoryResolver(Closure $resolver)
  384. {
  385. static::$viewFactoryResolver = $resolver;
  386. }
  387. /**
  388. * Set the default pagination view.
  389. *
  390. * @param string $view
  391. * @return void
  392. */
  393. public static function defaultView($view)
  394. {
  395. static::$defaultView = $view;
  396. }
  397. /**
  398. * Set the default "simple" pagination view.
  399. *
  400. * @param string $view
  401. * @return void
  402. */
  403. public static function defaultSimpleView($view)
  404. {
  405. static::$defaultSimpleView = $view;
  406. }
  407. /**
  408. * Indicate that Bootstrap 3 styling should be used for generated links.
  409. *
  410. * @return void
  411. */
  412. public static function useBootstrapThree()
  413. {
  414. static::defaultView('pagination::default');
  415. static::defaultSimpleView('pagination::simple-default');
  416. }
  417. /**
  418. * Get an iterator for the items.
  419. *
  420. * @return \ArrayIterator
  421. */
  422. public function getIterator()
  423. {
  424. return $this->items->getIterator();
  425. }
  426. /**
  427. * Determine if the list of items is empty.
  428. *
  429. * @return bool
  430. */
  431. public function isEmpty()
  432. {
  433. return $this->items->isEmpty();
  434. }
  435. /**
  436. * Determine if the list of items is not empty.
  437. *
  438. * @return bool
  439. */
  440. public function isNotEmpty()
  441. {
  442. return $this->items->isNotEmpty();
  443. }
  444. /**
  445. * Get the number of items for the current page.
  446. *
  447. * @return int
  448. */
  449. public function count()
  450. {
  451. return $this->items->count();
  452. }
  453. /**
  454. * Get the paginator's underlying collection.
  455. *
  456. * @return \Illuminate\Support\Collection
  457. */
  458. public function getCollection()
  459. {
  460. return $this->items;
  461. }
  462. /**
  463. * Set the paginator's underlying collection.
  464. *
  465. * @param \Illuminate\Support\Collection $collection
  466. * @return $this
  467. */
  468. public function setCollection(Collection $collection)
  469. {
  470. $this->items = $collection;
  471. return $this;
  472. }
  473. /**
  474. * Determine if the given item exists.
  475. *
  476. * @param mixed $key
  477. * @return bool
  478. */
  479. public function offsetExists($key)
  480. {
  481. return $this->items->has($key);
  482. }
  483. /**
  484. * Get the item at the given offset.
  485. *
  486. * @param mixed $key
  487. * @return mixed
  488. */
  489. public function offsetGet($key)
  490. {
  491. return $this->items->get($key);
  492. }
  493. /**
  494. * Set the item at the given offset.
  495. *
  496. * @param mixed $key
  497. * @param mixed $value
  498. * @return void
  499. */
  500. public function offsetSet($key, $value)
  501. {
  502. $this->items->put($key, $value);
  503. }
  504. /**
  505. * Unset the item at the given key.
  506. *
  507. * @param mixed $key
  508. * @return void
  509. */
  510. public function offsetUnset($key)
  511. {
  512. $this->items->forget($key);
  513. }
  514. /**
  515. * Render the contents of the paginator to HTML.
  516. *
  517. * @return string
  518. */
  519. public function toHtml()
  520. {
  521. return (string) $this->render();
  522. }
  523. /**
  524. * Make dynamic calls into the collection.
  525. *
  526. * @param string $method
  527. * @param array $parameters
  528. * @return mixed
  529. */
  530. public function __call($method, $parameters)
  531. {
  532. return $this->getCollection()->$method(...$parameters);
  533. }
  534. /**
  535. * Render the contents of the paginator when casting to string.
  536. *
  537. * @return string
  538. */
  539. public function __toString()
  540. {
  541. return (string) $this->render();
  542. }
  543. }