0
0

Paginator.php 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | ThinkPHP [ WE CAN DO IT JUST THINK ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8. // +----------------------------------------------------------------------
  9. // | Author: zhangyajun <448901948@qq.com>
  10. // +----------------------------------------------------------------------
  11. namespace think;
  12. use ArrayAccess;
  13. use ArrayIterator;
  14. use Countable;
  15. use IteratorAggregate;
  16. use JsonSerializable;
  17. use Traversable;
  18. abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable
  19. {
  20. /**
  21. * 是否简洁模式
  22. * @var bool
  23. */
  24. protected $simple = false;
  25. /**
  26. * 数据集
  27. * @var Collection
  28. */
  29. protected $items;
  30. /**
  31. * 当前页
  32. * @var integer
  33. */
  34. protected $currentPage;
  35. /**
  36. * 最后一页
  37. * @var integer
  38. */
  39. protected $lastPage;
  40. /**
  41. * 数据总数
  42. * @var integer|null
  43. */
  44. protected $total;
  45. /**
  46. * 每页数量
  47. * @var integer
  48. */
  49. protected $listRows;
  50. /**
  51. * 是否有下一页
  52. * @var bool
  53. */
  54. protected $hasMore;
  55. /**
  56. * 分页配置
  57. * @var array
  58. */
  59. protected $options = [
  60. 'var_page' => 'page',
  61. 'path' => '/',
  62. 'query' => [],
  63. 'fragment' => '',
  64. ];
  65. public function __construct($items, $listRows, $currentPage = null, $total = null, $simple = false, $options = [])
  66. {
  67. $this->options = array_merge($this->options, $options);
  68. $this->options['path'] = '/' != $this->options['path'] ? rtrim($this->options['path'], '/') : $this->options['path'];
  69. $this->simple = $simple;
  70. $this->listRows = $listRows;
  71. if (!$items instanceof Collection) {
  72. $items = Collection::make($items);
  73. }
  74. if ($simple) {
  75. $this->currentPage = $this->setCurrentPage($currentPage);
  76. $this->hasMore = count($items) > ($this->listRows);
  77. $items = $items->slice(0, $this->listRows);
  78. } else {
  79. $this->total = $total;
  80. $this->lastPage = (int) ceil($total / $listRows);
  81. $this->currentPage = $this->setCurrentPage($currentPage);
  82. $this->hasMore = $this->currentPage < $this->lastPage;
  83. }
  84. $this->items = $items;
  85. }
  86. /**
  87. * @access public
  88. * @param $items
  89. * @param $listRows
  90. * @param null $currentPage
  91. * @param null $total
  92. * @param bool $simple
  93. * @param array $options
  94. * @return Paginator
  95. */
  96. public static function make($items, $listRows, $currentPage = null, $total = null, $simple = false, $options = [])
  97. {
  98. return new static($items, $listRows, $currentPage, $total, $simple, $options);
  99. }
  100. protected function setCurrentPage($currentPage)
  101. {
  102. if (!$this->simple && $currentPage > $this->lastPage) {
  103. return $this->lastPage > 0 ? $this->lastPage : 1;
  104. }
  105. return $currentPage;
  106. }
  107. /**
  108. * 获取页码对应的链接
  109. *
  110. * @access protected
  111. * @param $page
  112. * @return string
  113. */
  114. protected function url($page)
  115. {
  116. if ($page <= 0) {
  117. $page = 1;
  118. }
  119. if (strpos($this->options['path'], '[PAGE]') === false) {
  120. $parameters = [$this->options['var_page'] => $page];
  121. $path = $this->options['path'];
  122. } else {
  123. $parameters = [];
  124. $path = str_replace('[PAGE]', $page, $this->options['path']);
  125. }
  126. if (count($this->options['query']) > 0) {
  127. $parameters = array_merge($this->options['query'], $parameters);
  128. }
  129. $url = $path;
  130. if (!empty($parameters)) {
  131. $url .= '?' . http_build_query($parameters, null, '&');
  132. }
  133. return $url . $this->buildFragment();
  134. }
  135. /**
  136. * 自动获取当前页码
  137. * @access public
  138. * @param string $varPage
  139. * @param int $default
  140. * @return int
  141. */
  142. public static function getCurrentPage($varPage = 'page', $default = 1)
  143. {
  144. $page = Container::get('request')->param($varPage);
  145. if (filter_var($page, FILTER_VALIDATE_INT) !== false && (int) $page >= 1) {
  146. return $page;
  147. }
  148. return $default;
  149. }
  150. /**
  151. * 自动获取当前的path
  152. * @access public
  153. * @return string
  154. */
  155. public static function getCurrentPath()
  156. {
  157. return Container::get('request')->baseUrl();
  158. }
  159. public function total()
  160. {
  161. if ($this->simple) {
  162. throw new \DomainException('not support total');
  163. }
  164. return $this->total;
  165. }
  166. public function listRows()
  167. {
  168. return $this->listRows;
  169. }
  170. public function currentPage()
  171. {
  172. return $this->currentPage;
  173. }
  174. public function lastPage()
  175. {
  176. if ($this->simple) {
  177. throw new \DomainException('not support last');
  178. }
  179. return $this->lastPage;
  180. }
  181. /**
  182. * 数据是否足够分页
  183. * @access public
  184. * @return boolean
  185. */
  186. public function hasPages()
  187. {
  188. return !(1 == $this->currentPage && !$this->hasMore);
  189. }
  190. /**
  191. * 创建一组分页链接
  192. *
  193. * @access public
  194. * @param int $start
  195. * @param int $end
  196. * @return array
  197. */
  198. public function getUrlRange($start, $end)
  199. {
  200. $urls = [];
  201. for ($page = $start; $page <= $end; $page++) {
  202. $urls[$page] = $this->url($page);
  203. }
  204. return $urls;
  205. }
  206. /**
  207. * 设置URL锚点
  208. *
  209. * @access public
  210. * @param string|null $fragment
  211. * @return $this
  212. */
  213. public function fragment($fragment)
  214. {
  215. $this->options['fragment'] = $fragment;
  216. return $this;
  217. }
  218. /**
  219. * 添加URL参数
  220. *
  221. * @access public
  222. * @param array|string $key
  223. * @param string|null $value
  224. * @return $this
  225. */
  226. public function appends($key, $value = null)
  227. {
  228. if (!is_array($key)) {
  229. $queries = [$key => $value];
  230. } else {
  231. $queries = $key;
  232. }
  233. foreach ($queries as $k => $v) {
  234. if ($k !== $this->options['var_page']) {
  235. $this->options['query'][$k] = $v;
  236. }
  237. }
  238. return $this;
  239. }
  240. /**
  241. * 构造锚点字符串
  242. *
  243. * @access public
  244. * @return string
  245. */
  246. protected function buildFragment()
  247. {
  248. return $this->options['fragment'] ? '#' . $this->options['fragment'] : '';
  249. }
  250. /**
  251. * 渲染分页html
  252. * @access public
  253. * @return mixed
  254. */
  255. abstract public function render();
  256. public function items()
  257. {
  258. return $this->items->all();
  259. }
  260. public function getCollection()
  261. {
  262. return $this->items;
  263. }
  264. public function isEmpty()
  265. {
  266. return $this->items->isEmpty();
  267. }
  268. /**
  269. * 给每个元素执行个回调
  270. *
  271. * @access public
  272. * @param callable $callback
  273. * @return $this
  274. */
  275. public function each(callable $callback)
  276. {
  277. foreach ($this->items as $key => $item) {
  278. $result = $callback($item, $key);
  279. if (false === $result) {
  280. break;
  281. } elseif (!is_object($item)) {
  282. $this->items[$key] = $result;
  283. }
  284. }
  285. return $this;
  286. }
  287. /**
  288. * Retrieve an external iterator
  289. * @access public
  290. * @return Traversable An instance of an object implementing <b>Iterator</b> or
  291. * <b>Traversable</b>
  292. */
  293. public function getIterator()
  294. {
  295. return new ArrayIterator($this->items->all());
  296. }
  297. /**
  298. * Whether a offset exists
  299. * @access public
  300. * @param mixed $offset
  301. * @return bool
  302. */
  303. public function offsetExists($offset)
  304. {
  305. return $this->items->offsetExists($offset);
  306. }
  307. /**
  308. * Offset to retrieve
  309. * @access public
  310. * @param mixed $offset
  311. * @return mixed
  312. */
  313. public function offsetGet($offset)
  314. {
  315. return $this->items->offsetGet($offset);
  316. }
  317. /**
  318. * Offset to set
  319. * @access public
  320. * @param mixed $offset
  321. * @param mixed $value
  322. */
  323. public function offsetSet($offset, $value)
  324. {
  325. $this->items->offsetSet($offset, $value);
  326. }
  327. /**
  328. * Offset to unset
  329. * @access public
  330. * @param mixed $offset
  331. * @return void
  332. * @since 5.0.0
  333. */
  334. public function offsetUnset($offset)
  335. {
  336. $this->items->offsetUnset($offset);
  337. }
  338. /**
  339. * Count elements of an object
  340. */
  341. public function count()
  342. {
  343. return $this->items->count();
  344. }
  345. public function __toString()
  346. {
  347. return (string) $this->render();
  348. }
  349. public function toArray()
  350. {
  351. try {
  352. $total = $this->total();
  353. } catch (\DomainException $e) {
  354. $total = null;
  355. }
  356. return [
  357. 'total' => $total,
  358. 'per_page' => $this->listRows(),
  359. 'current_page' => $this->currentPage(),
  360. 'last_page' => $this->lastPage,
  361. 'data' => $this->items->toArray(),
  362. ];
  363. }
  364. /**
  365. * Specify data which should be serialized to JSON
  366. */
  367. public function jsonSerialize()
  368. {
  369. return $this->toArray();
  370. }
  371. public function __call($name, $arguments)
  372. {
  373. $collection = $this->getCollection();
  374. $result = call_user_func_array([$collection, $name], $arguments);
  375. if ($result === $collection) {
  376. return $this;
  377. }
  378. return $result;
  379. }
  380. }