📜 ⬆️ ⬇️

PHP page navigation

Often, when developing and displaying content, it becomes necessary to use page-by-page navigation. Someone is likely to use ready-made solutions from their framework. Someone may not bother and pound the pages in a simple loop. Someone has his own work in this direction. So I just want to share my solution to this problem.

There are many variations of the location and display of buttons, I personally came to the following decision, which in my opinion is the most obvious and convenient. Suitable for both 5 pages and 5000.

HTML code sample


I will not beat around the bush, I will immediately attach an example of the html code generated by the script:

<div class="navigation"> <a href="/playlist/1.html?page=6"></a> <a href="/playlist/1.html">1</a> <i>...</i> <a href="/playlist/1.html?page=4">4</a> <a href="/playlist/1.html?page=5">5</a> <a href="/playlist/1.html?page=6">6</a> <span class="link_active">7</span> <a href="/playlist/1.html?page=8">8</a> <a href="/playlist/1.html?page=9">9</a> <a href="/playlist/1.html?page=10">10</a> <i>...</i> <a href="/playlist/1.html?page=17">17</a> <a href="/playlist/1.html?page=8"></a> </div> 

Build logic


On the settings of the parameters, I will stop a little later after the code is brought, now I will describe the logic of the formation of the numbers themselves.
')
I think everything is clear with the “Back” and “Forward” buttons, besides they can be simply turned off, so I will not focus on them.

The first and last page number is always displayed, a kind of “Top” and “End” buttons.

The middle is already formed by a simple algorithm. Displayed page is viewed and N pages on the sides. The example displays N = 3 pages. In principle, everything is simple and clear, but a special trick is used when approaching the edges. I will describe with examples:

Page 1-3 (where 3 = N)
1 2 3 4 5 6… 17
The first N * 2 pages and the last are displayed.

Page 4
1 2 3 4 5 6 7 ... 17
The first and further formed line is displayed from 4-3 = 1 to 4 + 3 = 7. The first page is reserved so numbers are formed from 2 to 7.

Page 5
1 2 3 4 5 6 7 8… 17
from 5-3 = 2 to 5 + 3 = 8.

Page 6
1 2 3 4 5 6 7 8 9… 17
Perhaps in all the navigation that I saw (including Habr), the line would have been formed with a pass, i.e. 1 ... 3 4 5 6 7 8 9 ... 17
But after all, it is not logical to display ellipsis instead of a single number. When building the second dot, a similar check is performed.

Page 7
1 ... 4 5 6 7 8 9 10 ... 17
The middle is already standard.

Forming the end is similar to the beginning

Page 12
1 ... 9 10 11 12 13 14 15 16 17

Page 13
1 ... 10 11 12 13 14 15 16 17

Page 14
1 ... 11 12 13 14 15 16 17

Page 15-17
1 ... 12 13 14 15 16 17

Redirects


In addition to this, I want to highlight 2 more points from this, it is a check for the existence of the page and a redirect to the "correct" address. Those. for example, right there on Habré, the first page can be accessed immediately by 2 addresses:
habrahabr.ru/sandbox/page1
habrahabr.ru/sandbox

The script does not allow access to the page / 1 / address and performs a redirect to the “clean” address.
Also, if the page number specified is too large, it will be redirected to the last existing one. For example, the materials were removed or the number of records per page was changed. I can’t really say whether it will be useful from the point of view of the CEO, but for users it will seem more convenient to me.

PHP code and its use


Php code
 class PaginateNavigationBuilder { /** *  URL   *            ,  {page} * : * /some_url{page}.html *    : * /some_url.html * /some_url/page_2.html *   {page}  ,        * * @var string */ private $baseUrl = '/'; /** *    * * @var string */ public $tpl = 'page/{page}/'; /** *   * * @var string */ public $wrap = "<div class=\"navigation\">{pages}</div>"; /** *         * : * $spread = 2 *  9      5 * 1 ... 3 4 5 6 7 ... 9 * * @var integer */ public $spread = 5; /** *     * * @var string */ public $separator = "<i>...</i>"; /** *     * * @var string */ public $activeClass = 'link_active'; /** *    * * @var integer */ private $currentPage = 0; /** *   ""  "" * * @var bool */ public $nextPrev = true; /** *   "" * * @var string */ public $prevTitle = ''; /** *   "" * * @var string */ public $nextTitle = ''; /** *   * * @param string $baseUrl URL       */ public function __construct($baseUrl = '/') { $this->baseUrl = $baseUrl; } /** *      * * @param integer $limit    1  * @param integer $count_all     * @param integer $currentPage    * @return mixed       */ public function build($limit, $count_all, $currentPage = 1) { if( $limit < 1 OR $count_all <= $limit ) return; $count_pages = ceil( $count_all / $limit ); if( $currentPage > $count_pages ) { header( "HTTP/1.0 301 Moved Permanently" ); header( "Location: " . $this->getUrl( $count_pages ) ); die( "Redirect" ); } if( $currentPage == 1 AND $_SERVER['REQUEST_URI'] != $this->getUrl( $currentPage ) ) { header( "HTTP/1.0 301 Moved Permanently" ); header( "Location: " . $this->getUrl( $currentPage ) ); die( "Redirect" ); } $this->currentPage = intval( $currentPage ); if( $this->currentPage < 1 ) $this->currentPage = 1; $shift_start = max( $this->currentPage - $this->spread, 2 ); $shift_end = min( $this->currentPage + $this->spread, $count_pages-1 ); if( $shift_end < $this->spread*2 ) { $shift_end = min( $this->spread*2, $count_pages-1 ); } if( $shift_end == $count_pages - 1 AND $shift_start > 3 ) { $shift_start = max( 3, min( $count_pages - $this->spread*2 + 1, $shift_start ) ); } $list = $this->getItem( 1 ); if ($shift_start == 3) { $list .= $this->getItem( 2 ); } elseif ( $shift_start > 3 ) { $list .= $this->separator; } for( $i = $shift_start; $i <= $shift_end; $i++ ) { $list .= $this->getItem( $i ); } $last_page = $count_pages - 1; if( $shift_end == $last_page-1 ){ $list .= $this->getItem( $last_page ); } elseif( $shift_end < $last_page ) { $list .= $this->separator; } $list .= $this->getItem( $count_pages ); if( $this->nextPrev ) { $list = $this->getItem( $this->currentPage > 1 ? $this->currentPage - 1 : 1, $this->prevTitle, true ) . $list . $this->getItem( $this->currentPage < $count_pages ? $this->currentPage + 1 : $count_pages, $this->nextTitle, true ); } return str_replace( "{pages}", $list, $this->wrap ); } /** *   * @param int $page_num   * @return string   */ private function getUrl( $page_num = 0 ) { $page = $page_num > 1 ? str_replace( '{page}', $page_num, $this->tpl ) : ''; if( stripos( $this->baseUrl, '{page}' ) !== false ){ return str_replace( '{page}', $page, $this->baseUrl ); } else { return $this->baseUrl . $page; } } /** *  / * @param int $page_num   * @param string $page_name  ,       * @param bool $noclass * @return - span      . */ private function getItem( $page_num, $page_name = '', $noclass = false ) { $page_name = $page_name ?: $page_num; $className = $noclass ? '' : $this->activeClass; if( $this->currentPage == $page_num ) { return "<span class=\"{$className}\">{$page_name}</span>"; } else { return "<a href=\"{$this->getUrl($page_num)}\">{$page_name}</a>"; } } } 

For clarity, here is an example of building a sandbox navigation:
habrahabr.ru/sandbox/page12

 $navi = new PaginateNavigationBuilder( "/sandbox/" ); $navi->tpl = "page{page}/"; $navi->spread = 4; $template = $navi->build( $limit, $count_all, $page_num ); 


Or if the page number is registered inside the URL:
example.com/some_url/1.html - first page
example.com/some_url/1-page2.html - second page
 $navi = new PaginateNavigationBuilder( "/some_url/1{page}.html" ); $navi->tpl = "-page{page}"; $template = $navi->build( $limit, $count_all, $page_num ); 

Where

$ limit - the number of entries per page
$ count_all - total number of records
$ page_num - page number on which the user is located

On this, perhaps, everything. I will be glad to any constructive criticism.

Ps. Many thanks to all unsubscribing, especially those who scold (and rightly so).
I promise to read everything, take into account and correct.

Source: https://habr.com/ru/post/340246/


All Articles