PageRenderTime 45ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/Flux/Paginator.php

https://github.com/chokoleytdesignoper/fluxcp_choko
PHP | 434 lines | 247 code | 56 blank | 131 comment | 47 complexity | 47bf28dfb83e4e19dc7e9a91cccdfc73 MD5 | raw file
Possible License(s): LGPL-2.1, LGPL-3.0, BSD-3-Clause
  1. <?php
  2. /**
  3. * The paginator helps in creating pages for SQL-stored data.
  4. */
  5. class Flux_Paginator {
  6. /**
  7. * Number of records.
  8. *
  9. * @access public
  10. * @var int
  11. */
  12. public $total = 0;
  13. /**
  14. * Current page.
  15. *
  16. * @access public
  17. * @var int
  18. */
  19. public $currentPage = 1;
  20. /**
  21. * Total number of pages.
  22. *
  23. * @access public
  24. * @var int
  25. */
  26. public $numberOfPages = 1;
  27. /**
  28. * Whether or not to show the page numbers even if there's only one page.
  29. *
  30. * @access public
  31. * @var bool
  32. */
  33. public $showSinglePage;
  34. /**
  35. * Records per-age.
  36. *
  37. * @access public
  38. * @var int
  39. */
  40. public $perPage;
  41. /**
  42. * The number of pages to display at once in the HTML pages output.
  43. *
  44. * @access public
  45. * @var int
  46. */
  47. public $pagesToShow;
  48. /**
  49. * GET variable holding the current page number.
  50. *
  51. * @access public
  52. * @var string
  53. */
  54. public $pageVariable;
  55. /**
  56. * Page separator used in the HTML pages generator.
  57. *
  58. * @access public
  59. * @var string
  60. */
  61. public $pageSeparator;
  62. /**
  63. * Array of sortable column names.
  64. *
  65. * @access protected
  66. * @var array
  67. */
  68. protected $sortableColumns = array();
  69. /**
  70. * Current column sort order.
  71. *
  72. * @access public
  73. * @var array
  74. */
  75. public $currentSortOrder = array();
  76. /**
  77. * Original request URI.
  78. *
  79. * @access public
  80. * @var string
  81. */
  82. public $requestURI;
  83. /**
  84. * Create new paginator instance.
  85. *
  86. * @param int $total Number of record.
  87. * @param string $requestURI Original request URI.
  88. * @param array $options Paginator options.
  89. * @access public
  90. */
  91. public function __construct($total, $requestURI = null, array $options = array())
  92. {
  93. if (!$requestURI) {
  94. $requestURI = $_SERVER['REQUEST_URI'];
  95. }
  96. $this->requestURI = $requestURI;
  97. $perPage = Flux::config('ResultsPerPage');
  98. if (!$perPage) {
  99. $perPage = 20;
  100. }
  101. $pagesToShow = Flux::config('PagesToShow');
  102. if (!$pagesToShow) {
  103. $pagesToShow = 10;
  104. }
  105. $showSinglePage = (bool)Flux::config('ShowSinglePage');
  106. $options = array_merge(
  107. array(
  108. 'showSinglePage' => $showSinglePage,
  109. 'perPage' => $perPage,
  110. 'pagesToShow' => $pagesToShow,
  111. 'pageVariable' => 'p',
  112. 'pageSeparator' => '|'),
  113. $options
  114. );
  115. $this->total = (int)$total;
  116. $this->showSinglePage = $options['showSinglePage'];
  117. $this->perPage = $options['perPage'];
  118. $this->pagesToShow = $options['pagesToShow'];
  119. $this->pageVariable = $options['pageVariable'];
  120. $this->pageSeparator = $options['pageSeparator'];
  121. $this->currentPage = isset($_GET[$this->pageVariable]) && $_GET[$this->pageVariable] > 0 ? $_GET[$this->pageVariable] : 1;
  122. $this->calculatePages();
  123. }
  124. /**
  125. * Calculate the number of pages.
  126. *
  127. * @access private
  128. */
  129. private function calculatePages()
  130. {
  131. $this->numberOfPages = (int)ceil($this->total / $this->perPage);
  132. }
  133. /**
  134. * Get an SQL query with the "LIMIT offset,num" and appropriate "ORDER BY"
  135. * strings appended to the end.
  136. *
  137. * @param string $sql
  138. * @return string
  139. * @access public
  140. */
  141. public function getSQL($sql)
  142. {
  143. $orderBy = false;
  144. foreach ($this->sortableColumns as $column => $value) {
  145. if (strpos($column, '.') !== false) {
  146. list ($table, $column) = explode('.', $column, 2);
  147. $param = "{$table}_{$column}_order";
  148. $columnName = "{$table}.{$column}";
  149. }
  150. else {
  151. $table = false;
  152. $param = "{$column}_order";
  153. $columnName = $column;
  154. }
  155. $sortValues = array('ASC', 'DESC', 'NONE');
  156. // First, check if a GET parameter was passed for this column.
  157. if (isset($_GET[$param]) && in_array(strtoupper($_GET[$param]), $sortValues)) {
  158. $value = $_GET[$param];
  159. }
  160. // Check again just in case we're working with the default here.
  161. if (!is_null($value) && in_array( ($value=strtoupper($value)), $sortValues ) && $value != 'NONE') {
  162. $this->currentSortOrder[$columnName] = $value;
  163. if (!$orderBy) {
  164. $sql .= ' ORDER BY';
  165. $orderBy = true;
  166. }
  167. if ($value == 'ASC') {
  168. $sql .= " (CASE WHEN $columnName IS NULL THEN 1 ELSE 0 END) ASC, $columnName ASC,";
  169. //ORDER BY CASE WHEN myColumn IS NULL THEN 1 ELSE 0 END ASC, myColumn ASC
  170. //$sql .= " ch.$columnName ASC,";
  171. }
  172. else {
  173. $sql .= " $columnName $value,";
  174. }
  175. }
  176. }
  177. if ($orderBy) {
  178. $sql = rtrim($sql, ',');
  179. }
  180. $offset = ($this->perPage * $this->currentPage) - $this->perPage;
  181. return "$sql LIMIT $offset,{$this->perPage}";
  182. }
  183. /**
  184. * Generate some basic HTML which creates a list of page numbers. Will
  185. * return an empty string if DisplaySinglePages config is set to false.
  186. *
  187. * @return string
  188. * @access public
  189. */
  190. public function getHTML()
  191. {
  192. if (!Flux::config('DisplaySinglePages') && $this->numberOfPages === 1) {
  193. return '';
  194. }
  195. $pages = array();
  196. $show = $this->pagesToShow;
  197. $start = (floor(($this->currentPage - 1) / $this->pagesToShow) * $this->pagesToShow) + 1;
  198. $end = $start + $this->pagesToShow + 1;
  199. if ($end > $this->numberOfPages) {
  200. $end = $this->numberOfPages + 1;
  201. }
  202. else {
  203. $end = $end - 1;
  204. }
  205. $hasPrev = $start > 1;
  206. $hasNext = $end < $this->numberOfPages;
  207. for ($i = $start; $i < $end; ++$i) {
  208. $request = $this->getPageURI($i);
  209. if ($i == $this->currentPage) {
  210. $pages[] = sprintf(
  211. '<a title="Page #%d" class="page-num current-page">%d</a>',
  212. $i, $i
  213. );
  214. }
  215. else {
  216. $pages[] = sprintf(
  217. '<a href="%s" title="Page #%d" class="page-num">%d</a>',
  218. $request, $i, $i
  219. );
  220. }
  221. }
  222. if ($hasPrev) {
  223. array_unshift($pages, sprintf('<a href="%s" title="Previous Pane (p#%d)" class="page-prev">Prev.</a> ', $this->getPageURI($start - 1), $start - 1));
  224. }
  225. if ($hasNext) {
  226. array_push($pages, sprintf(' <a href="%s" title="Next Pane (p#%d)" class="page-next">Next</a>', $this->getPageURI($end), $end));
  227. }
  228. $links = sprintf('<div class="pages">%s</div>', implode(" {$this->pageSeparator} ", $pages))."\n";
  229. if (Flux::config('ShowPageJump') && $this->numberOfPages > Flux::config('PageJumpMinimumPages')) {
  230. // This is some tricky shit. Don't even attempt to understand it =(
  231. // Page jumping is entirely JavaScript dependent.
  232. $pageVar = preg_quote($this->pageVariable);
  233. $event = "location.href='".$this->getPageURI(0)."'";
  234. $event = preg_replace("/$pageVar=0/", "{$this->pageVariable}='+this.value+'", $event);
  235. $jump = '<label>Page Jump: <input type="text" name="jump_to_page" id="jump_to_page" size="4" onkeypress="if (event.keyCode == 13) { %s }" /></label>';
  236. $jump = sprintf($jump, $event);
  237. $links .= sprintf('<div class="jump-to-page">%s</div>', $jump);
  238. }
  239. if (!$this->showSinglePage && $this->numberOfPages === 1) {
  240. return null;
  241. }
  242. else {
  243. return $links;
  244. }
  245. }
  246. /**
  247. * Create a link to the current request with a different page number.
  248. *
  249. * @param int $pageNumber
  250. * @return string
  251. * @access protected
  252. */
  253. protected function getPageURI($pageNumber)
  254. {
  255. $request = preg_replace('/(\?.*)$/', '', $this->requestURI);
  256. $qString = $_SERVER['QUERY_STRING'];
  257. $pageVar = preg_quote($this->pageVariable);
  258. $pageNum = (int)$pageNumber;
  259. $qStringVars = array();
  260. $qStringLines = preg_split('/&/', $qString, -1, PREG_SPLIT_NO_EMPTY);
  261. foreach ($qStringLines as $qStringVar) {
  262. if (strpos($qStringVar, '=') !== false) {
  263. list($qStringKey, $qStringVal) = explode('=', $qStringVar, 2);
  264. $qStringVars[$qStringKey] = $qStringVal;
  265. }
  266. }
  267. $qStringVars[$pageVar] = $pageNum;
  268. $qStringLines = array();
  269. foreach ($qStringVars as $qStringKey => $qStringVal) {
  270. $qStringLines[] = sprintf('%s=%s', $qStringKey, $qStringVal);
  271. }
  272. return sprintf('%s?%s', $request, implode('&', $qStringLines));
  273. }
  274. /**
  275. * Specify an array (or a string single column name) of columns that are
  276. * sortable by the paginator's features.
  277. *
  278. * @param array $columns
  279. * @return array
  280. * @access public
  281. */
  282. public function setSortableColumns($columns)
  283. {
  284. if (!is_array($columns)) {
  285. $columns = array($columns);
  286. }
  287. foreach ($columns as $key => $column) {
  288. if (!is_numeric($key)) {
  289. $value = $column;
  290. $column = $key;
  291. }
  292. else {
  293. $value = null;
  294. }
  295. $this->sortableColumns[$column] = $value;
  296. }
  297. return $this->sortableColumns;
  298. }
  299. /**
  300. * Get an HTML anchor which automatically links to the current request
  301. * based on current sorting conditions and sets ascending/descending
  302. * sorting parameters accordingly.
  303. *
  304. * @param string $column
  305. * @param string $name
  306. * @return string
  307. * @access public
  308. */
  309. public function sortableColumn($column, $name = null)
  310. {
  311. if (!$name) {
  312. $name = $column;
  313. }
  314. if (!array_key_exists($column, $this->sortableColumns)) {
  315. return htmlspecialchars($name);
  316. }
  317. else {
  318. if (strpos($column, '.') !== false) {
  319. list ($_table, $_column) = explode('.', $column, 2);
  320. $param = "{$_table}_{$_column}_order";
  321. }
  322. else {
  323. $table = false;
  324. $param = "{$column}_order";
  325. }
  326. $order = 'asc';
  327. $format = '<a href="%s" class="sortable">%s</a>';
  328. $name = htmlspecialchars($name);
  329. $request = $_SERVER['REQUEST_URI'];
  330. if (isset($this->currentSortOrder[$column])) {
  331. switch (strtolower($this->currentSortOrder[$column])) {
  332. case 'asc':
  333. $order = 'desc';
  334. $name .= Flux::config('ColumnSortAscending');
  335. break;
  336. case 'desc':
  337. $order = is_null($this->sortableColumns[$column]) ? false : 'none';
  338. $name .= Flux::config('ColumnSortDescending');
  339. break;
  340. default:
  341. $order = 'asc';
  342. break;
  343. }
  344. }
  345. if ($order) {
  346. $value = "$param=$order";
  347. if (preg_match("/$param=(\w*)/", $request)) {
  348. $request = preg_replace("/$param=(\w*)/", $value, $request);
  349. }
  350. elseif (empty($_SERVER['QUERY_STRING'])) {
  351. $request = "$request?$value";
  352. }
  353. else {
  354. $request = "$request&$value";
  355. }
  356. return sprintf($format, $request, $name);
  357. }
  358. else {
  359. $request = rtrim(preg_replace("%(?:(\?)$param=(?:\w*)&?|&?$param=(?:\w*))%", '$1', $request), '?');
  360. return sprintf($format, $request, $name);
  361. }
  362. }
  363. }
  364. /**
  365. *
  366. */
  367. public function infoText()
  368. {
  369. $currPage = $this->currentPage;
  370. $results = Flux::config('ResultsPerPage');
  371. $infoText = sprintf(
  372. Flux::message('FoundSearchResults'),
  373. $this->total, $this->numberOfPages, ($currPage*$results-($results - 1)), $currPage * $results < $this->total ? ($currPage*$results) : ($this->total)
  374. );
  375. return sprintf('<p class="info-text">%s</p>', $infoText);
  376. }
  377. }
  378. ?>