Постраничная навигация на PHP
Часто при разработке и выводе контента появляется необходимость использования постраничной навигации. Кто-то скорее всего использует готовые решения от своего фреймворка. Кто-то, возможно, не заморачивается и лупит страницы просто циклом. У кого-то есть свои наработки в этом направлении. Вот я как раз и хочу поделиться своим решением данной задачи.
Существует множество вариаций расположения и отображения кнопок, лично я пришел к следующему решению, которое по моему мнению наиболее наглядно и удобно. Подходит как для 5 страниц так и для 5000.
Пример HTML кода
Не буду ходить вокруг да около, сразу приложу пример сформированного скриптом html кода:
Назад 1 . 4 5 6 7 8 9 10 . 17 Вперед
Логика построения
По настройкам параметров я остановлюсь чуть позже после приведения кода, сейчас опишу логику формирования самих номеров.
С кнопками «Назад» и «Вперед» думаю все понятно, к тому же их можно просто отключить, поэтому на них не буду заострять внимания.
Первый и последний номер страницы отображается всегда, своего рода кнопки «В начало» и «В конец».
Середина формируется уже по простому алгоритму. Отображается просматриваемая страница и по N страниц по бокам. На примере отображается по N=3 страницы. В принципе все просто и понятно, но особая хитрость используется при приближении к краям. Опишу на примерах:
Страница 1-3 (где 3 = N)
Отображаются первые N*2 страниц и последняя.
Отображается первая и дальше сформированная строка от 4-3=1 до 4+3=7. Первая страница зарезервирована поэтому формируются номера от 2 до 7.
Пожалуй во всех навигациях что я видел (включая хабр) строка была бы сформирована с пропуском, т.е. 1… 3 4 5 6 7 8 9… 17
Но ведь это не логично, отображать многоточие вместо одного числа. При построении второго многоточия выполняется аналогичная проверка.
Формирование окончания аналогично началу
Редиректы
Помимо этого из особенностей хочу выделить еще 2 момента, это проверка существования страницы и редирект на «правильный» адрес. Т.е. к примеру, тут же на хабре первая страница может быть доступна сразу по 2м адресам:
habrahabr.ru/sandbox/page1
habrahabr.ru/sandbox
Скрипт не дает зайти на адрес page/1/ и выполняет редирект на «чистый» адрес
Так же если указан слишком большой номер страницы будет выполнен редирект на последнюю существующую. К примеру были удалены материалы или изменено количество записей на страницу. Не могу правда однозначно сказать полезно ли это будет с точки зрения СЕО, но для пользователей мне кажется так будет удобнее.
PHP код и его использование
class PaginateNavigationBuilder < /** * Чистый URL по умолчанию * В адресе может быть указано место для размещения блока с номером страницы, тег * Пример: * /some_url.html * В итоге адрес будет: * /some_url.html * /some_url/page_2.html * Если тег не указан, то страницы будут дописываться в конец адреса * * @var string */ private $baseUrl = '/'; /** * Шаблон ссылки навигации * * @var string */ public $tpl = 'page//'; /** * Обертка кнопок * * @var string */ public $wrap = "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 $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 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( "", $list, $this->wrap ); > /** * Формирование адреса * @param int $page_num номер страницы * @return string сформированный адрес */ private function getUrl( $page_num = 0 ) < $page = $page_num >1 ? str_replace( '', $page_num, $this->tpl ) : ''; if( stripos( $this->baseUrl, '' ) !== false )< return str_replace( '', $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 "else < return "getUrl($page_num)>\"> "; > > >
Для наглядности, приведу пример построения навигации песочницы:
habrahabr.ru/sandbox/page12
$navi = new PaginateNavigationBuilder( "/sandbox/" ); $navi->tpl = "page/"; $navi->spread = 4; $template = $navi->build( $limit, $count_all, $page_num );
Или же если номер страницы прописан внутри URL:
example.com/some_url/1.html — первая страница
example.com/some_url/1-page2.html — вторая страница
$navi = new PaginateNavigationBuilder( "/some_url/1.html" ); $navi->tpl = "-page"; $template = $navi->build( $limit, $count_all, $page_num );
$limit — количество записей на страницу
$count_all — общее количество записей
$page_num — номер страницы на которой находится пользователь
На этом, пожалуй, всё. Буду рад любой конструктивной критике.
PS. Огромное спасибо всем отписавшимся, особенно тем кто ругает (и правильно делает).
Обещаю со всем ознакомиться, принять во внимание и исправиться.
How to Create Pagination with PHP and MySQL
For this tutorial we will be creating pagination with PHP and MySQL, pagination is how users will navigate certain parts of your website that contains large amounts of data, we will be using MySQL in this tutorial, we will select records from a database and display them in a table using HTML and PHP with pagination.
We will display the next and previous buttons in this pagination, as well as the first and last pages, and the pages in between with the current page being highlighted.
We need to do a bit more work than just showing the next and previous buttons, but this is because we can change the code, change it to how we want it, if we want to remove the pagination pages we can do so.
Pagination.php
Let’s go ahead and create the pagination file, we will call this pagination.php.
We now need to connect to our database, we can do so with the following line of code:
$mysqli = mysqli_connect('localhost', 'root', '', 'pagination');
You can change the connection information, replace the text with your MySQL details: mysqli_connect(YOUR_HOST, YOUR_USERNAME, YOUR_PASSWORD, YOUR_DATABASE)
For the purpose of this tutorial I have created the database pagination, below is the SQL statement to this database, run the SQL statement with phpMyAdmin, or your preferred database management tool.
CREATE DATABASE IF NOT EXISTS `pagination` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; USE `pagination`; CREATE TABLE IF NOT EXISTS `students` ( `id` int(11) NOT NULL, `name` varchar(255) NOT NULL, `age` tinyint(1) NOT NULL, `joined` varchar(10) NOT NULL ) ENGINE=InnoDB AUTO_INCREMENT=42 DEFAULT CHARSET=utf8; INSERT INTO `students` (`id`, `name`, `age`, `joined`) VALUES (1, 'David Deacon', 23, '17/08/2018'), (2, 'Sheri Warner', 19, '03/05/2018'), (3, 'Sean Glover', 24, '24/07/2018'), (4, 'John West', 17, '13/08/2018'), (5, 'Rufus Clarke', 20, '28/07/2018'), (6, 'Roosevelt Myers', 20, '25/07/2018'), (7, 'Elvira Andrews', 22, '02/07/2018'), (8, 'Richard Cook', 26, '19/07/2018'), (9, 'Lorenzo Harris', 23, '01/07/2018'), (10, 'Eduardo Hoffman', 17, '03/07/2018'), (11, 'Jeanne Fisher', 20, '13/08/2018'), (12, 'Tracy Bowers', 30, '07/07/2018'), (13, 'Heidi Lawrence', 18, '04/06/2018'), (14, 'Tara Holland', 25, '01/07/1991'), (15, 'Grant Edwards', 22, '22/06/2018'), (16, 'Bradford Green', 29, '02/05/2018'), (17, 'Gwen Schultz', 20, '02/05/2018'), (18, 'Hope Dawson', 28, '21/08/2018'), (19, 'Florence Osborne', 19, '17/05/2018'), (20, 'Rickey Poole', 26, '28/06/2018'), (21, 'Casey Sutton', 28, '06/07/2018'), (22, 'Willie Lowe', 23, '11/05/2018'), (23, 'Stephen Schultz', 28, '15/07/2018'), (24, 'Eileen Lynch', 18, '12/06/2018'), (25, 'Aaron Ruiz', 29, '02/05/2018'), (26, 'Mae Murray', 30, '24/06/2018'), (27, 'Regina Hanson', 21, '26/07/2018'), (28, 'Cameron Mclaughlin', 20, '29/07/2018'), (29, 'Earl Hale', 17, '30/06/2018'), (30, 'Marta Blair', 24, '10/06/2018'), (31, 'Alberta Silva', 22, '05/06/2018'), (32, 'Joanna Holmes', 20, '20/05/2018'), (33, 'Alex Brock', 30, '12/05/2018'), (34, 'Colin Wright', 19, '28/05/2018'), (35, 'Peter Schmidt', 25, '10/07/2018'), (36, 'Joshua Price', 27, '13/07/2018'), (37, 'Elias Chandler', 22, '19/07/2018'), (38, 'Stanley Ross', 21, '02/06/2018'), (39, 'Vera Cole', 26, '02/05/2018'), (40, 'Johnny Daniels', 29, '19/07/2018'), (41, 'Yvonne Hopkins', 21, '16/07/2018'); ALTER TABLE `students` ADD PRIMARY KEY (`id`); ALTER TABLE `students` MODIFY `id` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=42;
Let’s get back to the pagination PHP file, what we need to do now is to create some important variables that we will use for the pagination.
// Get the total number of records from our table "students". $total_pages = $mysqli->query('SELECT COUNT(*) FROM students')->fetch_row()[0]; // Check if the page number is specified and check if it's a number, if not return the default page number which is 1. $page = isset($_GET['page']) && is_numeric($_GET['page']) ? $_GET['page'] : 1; // Number of results to show on each page. $num_results_on_page = 5;
With the first variable we are getting the total number of rows from the students table in our database, this will be used to calculate how many pages there will be. The second variable is used to determine which page the user is on, for example, if the URL is pagination.php?page=3 then the page number will be 3, if the page doesn’t exist in the URL it will default to 1. The third variable determines how many results will be shown on each page, for example, if we wanted to show 10 results (list of 10 students) we will change the number to 10, the default is set to 5.
So now we need to get the records from the database for the current page, the following code below will do just that.
if ($stmt = $mysqli->prepare('SELECT * FROM students ORDER BY name LIMIT . ')) < // Calculate the page to get the results we need from our table. $calc_page = ($page - 1) * $num_results_on_page; $stmt->bind_param('ii', $calc_page, $num_results_on_page); $stmt->execute(); // Get the results. $result = $stmt->get_result(); $stmt->close(); >
There are a few things that are going on in the above code, the first thing we do is prepare the statement, this prevents SQL injection, if you do not do this you may be at risk of being hacked, the $calc_page variable basically determines the start index in our table, after, we bind the variables (i is for integer), and then execute the SQL statement. The result will be stored in the $result variable.
Now we need to display these results, the results will be displayed in a table, you can display the results how you want but for this tutorial, we will use a table.
fetch_assoc()): ?>
The above code will basically display each student in the table with their name, age, and joined date.
Now we need to display the pagination.
The first thing we do here is to check if there are more than 1 page, no need to display the pagination if there is only 1 page. We then check if the current page is greater than 1, if it is, show the previous button, and then if the page is greater than 3 we can show the first page, which is 1.
After that we display the 2 pages before and after the current page, so if the page is 5 it will be displayed like this 1 . 3 4 5 6 7 . 14, it’s basically the same as we have done after that but in reverse.
Now we need to add some style to our pagination and table, the following CSS code will do just that.
table < border-collapse: collapse; width: 500px; >td, th < padding: 10px; >th < background-color: #54585d; color: #ffffff; font-weight: bold; font-size: 13px; border: 1px solid #54585d; >td < color: #636363; border: 1px solid #dddfe1; >tr < background-color: #f9fafb; >tr:nth-child(odd) < background-color: #ffffff; >.pagination < list-style-type: none; padding: 10px 0; display: inline-flex; justify-content: space-between; box-sizing: border-box; >.pagination li < box-sizing: border-box; padding-right: 10px; >.pagination li a < box-sizing: border-box; background-color: #e2e6e6; padding: 8px; text-decoration: none; font-size: 12px; font-weight: bold; color: #616872; border-radius: 4px; >.pagination li a:hover < background-color: #d4dada; >.pagination .next a, .pagination .prev a < text-transform: uppercase; font-size: 12px; >.pagination .currentpage a < background-color: #518acb; color: #fff; >.pagination .currentpage a:hover
You should now have something that will look like this:
Source
The full pagination.php source:
Conclusion
Congratulations, you have successfully created a fully working pagination for your website, there are many different ways you can create pagination, this way is probably one of the best and it’s secure! Remember to subscribe and share, like, follow our social media accounts if you like this tutorial, help our site grow and we will provide lots of more useful tutorials.
About Author
David Adams
Enthusiastic website developer, I’ve been designing and developing web applications for over 10 years, I enjoy the creativity I put into my projects and enjoy what others bring to the awesome web. My goal is to help newcomers learn the ways of the web.