Как разбить данные на страницы с помощью PHP
() translation by (you can also view the original English article)
Я вспоминаю, когда много лет назад, когда я только начинал писать код PHP и MySQL, я был весьма рад, когда впервые моя информация из базы данных отобразилась в браузере.
Я был на триумфальной высоте перед теми, у кого не было больших знаний в сфере баз данных и программирования, и кто видел те строки таблицы, появляющимися на экране в результате написанного мною кода (ну хорошо, я просто скопировал пример из книги - не будем вдаваться в подробности). Возможно, я не вполне понимал всех тонкостей этой работы, но этот первый успех стимулировал меня к работе над большими и лучшими проектами.
Хотя мой уровень в отношении баз данных не таков, как был раньше, сейчас он значительно богаче,
со времени моего первого знакомства с PHP и MySQL, меня пленила
мысль делать вещи более простыми и легкими в использовании.
Как разработчик я постоянно сталкиваюсь с одной проблемой: принятие большого количества информации и попытка легко усвоить ее. Является ли это список клиентов крупной компании или личный mp3-каталог, вынужденность сидеть и смотреть на строку над строкой, строку над строкой разных данных может быть весьма скучным занятием
и даже более - вы можете разочароваться. Что может сделать хороший разработчик? Разбить на страницы!
1. Разбивка на страницы
Разбиение на страницы - это по существу процесс принятия набора результатов и распределение
их по страницам, чтоб их было легче читать.



Ещё раньше я размышлял, если бы у меня было 500 строчек информации, которую нужно отобразить, это было бы
не только головной болью для того, кто попытается все это прочитать, но и большинство браузеров
потратили бы целую вечность (т.е. более чем пять секунд) на отображение этой информации.
Чтобы решить эту проблему я мог бы написать разные инструкции SQL, чтобы вытащить фрагменты данных, и если бы у меня
было хорошее настроение, я бы даже сделал пару кнопок, «далее» и «предыдущая».
Через некоторое время, мне нужно было внести этот код в очень похожий проект и настроить
его, чтобы подогнать под старый вариант. Быстро. И как каждый хороший разработчик знает, лень рождает изобретательность
или что-то в этом роде. Поэтому, однажды, я сел и решил придумать простой,
гибкий и легкий в использовании класс PHP, который автоматически бы делал грязную работу
вместо меня.
Несколько слов обо мне и классах PHP. Я не волшебник в сфере объектно-ориентированного программирования. На самом деле, я практически
никогда не использую подобные вещи. Но после прочтения некоторых примеров ООП и учебных материалов, а также
простых примеров проб и ошибок, я решил сделать решительный поворот, и знаете, что?
Он идеально работает для разбивки на страницы. Код, который я здесь использую, написан в PHP 4, но
он работает и в PHP 5.
2. База данных
Вам нужно любить MySQL. Не хочу обидеть другие системы баз данных, но
для меня, все что мне нужно - это MySQL. Одной из прекрасных особенностью MySQL является то, что они дают вам
некоторые бесплатные примеры баз данных, с которыми вы можете поиграться: http://dev.mysql.com/doc/#sampledb.
В моем случае я буду использовать всемирную базу данных (архив ~ 90 k), которая содержит
свыше 4000 записей, с которыми можно поработать, но красота PHP-скрипта, который мы будем создавать, в том,
что его можна использовать для любой базы данных. Теперь, думаю, мы все можем согласиться, что если мы решили
не разбивать наши результаты, в итоге мы получим очень длинный и громоздкий
список результатов, как примерно показано ниже:

(кликните, чтобы получить изображение в полном размере, оно невероятно длинное, ~ 338k)
Потому давайте пойдем дальше, разбивая наши данные в легко обрабатываемые байты, как показано ниже:



Красиво, не так ли? После того, как вы ввели клас разбивки на страницы в ваш код, вы можете
быстро и просто превратить огромный набор данных в страницы, по которым можно легко перемещаться
с помощью всего нескольких строк кода. Это на самом деле так.
3. Пагинатор
Данный пример будет состоять с двух скриптов, многоразового класса paginator и файла index, который будет выводить элементы таблицы и элементы управления.
Paginator.class.php
Класс рaginator будет иметь только два метода и конструктор, мы будем строить их постепенно, объясняя каждый шаг по мере того, как мы движемся вперед.
1 |
<?php
|
2 |
|
3 |
class Paginator { |
4 |
|
5 |
private $_conn; |
6 |
private $_limit; |
7 |
private $_page; |
8 |
private $_query; |
9 |
private $_total; |
10 |
|
11 |
}
|
Это определение только устанавливает необходимые члены-переменные пагинатора. Так как это вспомогательный класс и он разработан только для пагинации, он будет полагаться на валидное подключение к серверу MySQL и на уже определенный запрос, который мы добавим к параметрам, необходимым для разбивки результатов. Мы начнем с метода конструктор.
1 |
<?php
|
2 |
|
3 |
public function __construct( $conn, $query ) { |
4 |
|
5 |
$this->_conn = $conn; |
6 |
$this->_query = $query; |
7 |
|
8 |
$rs= $this->_conn->query( $this->_query ); |
9 |
$this->_total = $rs->num_rows; |
10 |
|
11 |
}
|
Довольно просто, не так ли? Этот метод только устанавливает соединение базы данных объекта и необходимый запрос, после чего он вычисляет общее количество строк, полученных этим запросом без каких-либо ограничений или пропусков параметров. Эта сумма необходима для создания ссылок для пагинатора.
Обратите внимание, что для простоты мы не выполняем проверку ошибок или любую другую проверку данных параметров, но в реальности эта проверка необходима.
Получение результатов
Теперь давайте создадим метод, который, собственно, будет выполнять пагинацию данных и возвращать результаты.
1 |
<?php
|
2 |
public function getData( $limit = 10, $page = 1 ) { |
3 |
|
4 |
$this->_limit = $limit; |
5 |
$this->_page = $page; |
6 |
|
7 |
if ( $this->_limit == 'all' ) { |
8 |
$query = $this->_query; |
9 |
} else { |
10 |
$query = $this->_query . " LIMIT " . ( ( $this->_page - 1 ) * $this->_limit ) . ", $this->_limit"; |
11 |
}
|
12 |
$rs = $this->_conn->query( $query ); |
13 |
|
14 |
while ( $row = $rs->fetch_assoc() ) { |
15 |
$results[] = $row; |
16 |
}
|
17 |
|
18 |
$result = new stdClass(); |
19 |
$result->page = $this->_page; |
20 |
$result->limit = $this->_limit; |
21 |
$result->total = $this->_total; |
22 |
$result->data = $results; |
23 |
|
24 |
return $result; |
25 |
}
|
Давайте сейчас проанализируем этот шаг: сначала мы установили лимит и параметры страницы, которые по умолчанию установлены на 10 и 1 соответственно. Далее мы проверяем, запрашивает ли пользователь заданное число строк или все из них и, основываясь на этом и на параметрах страницы, мы устанавливаем параметр LIMIT
этого запроса. Значение "-1" берется с учётом того, что мы начинаем нумерацию страницы с 1, но не с 0.
После этого мы просто оцениваем запрос и получаем результаты, наконец, мы создаем новый объект результатов, который содержит лимит, страницу и общие параметры выполненного запроса, а также данные для каждой из полученных строк.
Отображение пагинальных ссылок
Теперь давайте напишем метод, который используется для получения пагинальных ссылок.
1 |
<?php
|
2 |
public function createLinks( $links, $list_class ) { |
3 |
if ( $this->_limit == 'all' ) { |
4 |
return ''; |
5 |
}
|
6 |
|
7 |
$last = ceil( $this->_total / $this->_limit ); |
8 |
|
9 |
$start = ( ( $this->_page - $links ) > 0 ) ? $this->_page - $links : 1; |
10 |
$end = ( ( $this->_page + $links ) < $last ) ? $this->_page + $links : $last; |
11 |
|
12 |
$html = '<ul class="' . $list_class . '">'; |
13 |
|
14 |
$class = ( $this->_page == 1 ) ? "disabled" : ""; |
15 |
$html .= '<li class="' . $class . '"><a href="?limit=' . $this->_limit . '&page=' . ( $this->_page - 1 ) . '">«</a></li>'; |
16 |
|
17 |
if ( $start > 1 ) { |
18 |
$html .= '<li><a href="?limit=' . $this->_limit . '&page=1">1</a></li>'; |
19 |
$html .= '<li class="disabled"><span>...</span></li>'; |
20 |
}
|
21 |
|
22 |
for ( $i = $start ; $i <= $end; $i++ ) { |
23 |
$class = ( $this->_page == $i ) ? "active" : ""; |
24 |
$html .= '<li class="' . $class . '"><a href="?limit=' . $this->_limit . '&page=' . $i . '">' . $i . '</a></li>'; |
25 |
}
|
26 |
|
27 |
if ( $end < $last ) { |
28 |
$html .= '<li class="disabled"><span>...</span></li>'; |
29 |
$html .= '<li><a href="?limit=' . $this->_limit . '&page=' . $last . '">' . $last . '</a></li>'; |
30 |
}
|
31 |
|
32 |
$class = ( $this->_page == $last ) ? "disabled" : ""; |
33 |
$html .= '<li class="' . $class . '"><a href="?limit=' . $this->_limit . '&page=' . ( $this->_page + 1 ) . '">»</a></li>'; |
34 |
|
35 |
$html .= '</ul>'; |
36 |
|
37 |
return $html; |
38 |
}
|
Это довольно длинный метод, примерно на 34 строки кода, потому давайте объясним, что как происходит в этом методе.
- Сначала мы оцениваем, требует ли пользователь заданное количество ссылок или все из них, в противном случае, мы просто возвращаем пустую строку, так как пагинация не требуется.
- После этого мы вычисляем последнюю страницу на основе общего количества доступных строк и элементов, необходимых для каждой страницы.
- После этого мы берем параметры ссылок, которые отображают количество ссылок, которые нужно разместить вверху и внизу текущей страницы и вычисляем, когда создать начальную и конечную ссылку.
- Теперь мы создаем открывающий тег для списка и устанавливаем для него параметр класса и добавляем ссылку "предыдущая страница". Обратите внимание, что для этой ссылки мы проверяем, является ли текущая страница первой, и если да, то мы устанавливаем для ссылки свойство "недоступен".
- На данный момент мы отображаем ссылку на первой странице и символ многоточия в случае, если начальная ссылка не является первой.
- Затем мы добавляем ссылки ниже и выше текущей страницы, исходя из ранее вычисленных начальных и конечных параметров. На каждом шагу мы снова оцениваем текущую страницу, отображаемую страницу ссылки, и соответственно устанавливаем активный класс.
- После этого мы отображаем другой символ многоточия и ссылку на последнюю страницу в случае, если конечная ссылка не есть последняя.
- Наконец мы показываем ссылку «Следующая страница» и устанавливаем отключенное состояние, когда пользователь просматривает последнюю страницу. Закройте список и вернитесь на сгенерированную строку HTML.
Это все, что касается Paginator.class. Конечно, мы могли бы добавить сеттеры и геттеры для подключения к базе данных, ограничения, страницы, запрос и полные параметры, но для простоты мы сохраним его таким как есть.
4. Index.php
Теперь мы создадим файл, отвечающий за использование класса Paginator и отображение данных, поэтому сначала позвольте мне показать вам базовый HTML.
1 |
<!DOCTYPE html>
|
2 |
<head>
|
3 |
<title>PHP Pagination</title> |
4 |
<link rel="stylesheet" href="css/bootstrap.min.css"> |
5 |
</head>
|
6 |
<body>
|
7 |
<div class="container"> |
8 |
<div class="col-md-10 col-md-offset-1"> |
9 |
<h1>PHP Pagination</h1> |
10 |
<table class="table table-striped table-condensed table-bordered table-rounded"> |
11 |
<thead>
|
12 |
<tr>
|
13 |
<th>City</th> |
14 |
<th width="20%">Country</th> |
15 |
<th width="20%">Continent</th> |
16 |
<th width="25%">Region</th> |
17 |
</tr>
|
18 |
</thead>
|
19 |
<tbody></tbody>
|
20 |
</table>
|
21 |
</div>
|
22 |
</div>
|
23 |
</body>
|
24 |
</html>
|
Довольно просто, этот файл отображает только таблицу, которую мы заполним требуемой с баз данных информацией. Обратите внимание, что для этого примера я использую bootstrap для базового стиля страницы.
Использование пагинатора
1 |
<?php for( $i = 0; $i < count( $results->data ); $i++ ) : ?> |
2 |
<tr>
|
3 |
<td><?php echo $results->data[$i]['Name']; ?></td> |
4 |
<td><?php echo $results->data[$i]['Country']; ?></td> |
5 |
<td><?php echo $results->data[$i]['Continent']; ?></td> |
6 |
<td><?php echo $results->data[$i]['Region']; ?></td> |
7 |
</tr>
|
8 |
<?php endfor; ?> |
Теперь, чтобы воспользоваться классом пагинатора, добавьте следующий РНР-код в верхней части документа.
1 |
<?php
|
2 |
require_once 'Paginator.class.php'; |
3 |
|
4 |
$conn = new mysqli( '127.0.0.1', 'root', 'root', 'world' ); |
5 |
|
6 |
$limit = ( isset( $_GET['limit'] ) ) ? $_GET['limit'] : 25; |
7 |
$page = ( isset( $_GET['page'] ) ) ? $_GET['page'] : 1; |
8 |
$links = ( isset( $_GET['links'] ) ) ? $_GET['links'] : 7; |
9 |
$query = "SELECT City.Name, City.CountryCode, Country.Code, Country.Name AS Country, Country.Continent, Country.Region FROM City, Country WHERE City.CountryCode = Country.Code"; |
10 |
|
11 |
$Paginator = new Paginator( $conn, $query ); |
12 |
|
13 |
$results = $Paginator->getData( $page, $limit ); |
14 |
?>
|
Этот скрипт достаточно прост. Мы просто вызвали класс пагинатор, обратите внимание, что этот код предполагает, что этот файл находится в той же папке, что и файл index.php
. Если это не так, вы должны соответствующим образом обновить путь.
Затем мы создаем соединение с нашей базой данных с помощью библиотеки MySQLi, извлекаем параметры пагинатора из запроса GET и устанавливаем запрос, так как это не статья на MySQL или что-либо другое, во что я не буду вдаваться в подробности.
Наконец, мы создали объект Paginator и извлекли результаты для текущей страницы.
Отображение результатов
Теперь, чтобы отобразить полученные результаты, добавьте следующий код в тело таблицы.
1 |
<?php for( $i = 0; $i < count( $results->data ); $i++ ) : ?> |
2 |
<tr>
|
3 |
<td><?php echo $results->data[$i]['Name']; ?></td> |
4 |
<td><?php echo $results->data[$i]['Country']; ?></td> |
5 |
<td><?php echo $results->data[$i]['Continent']; ?></td> |
6 |
<td><?php echo $results->data[$i]['Region']; ?></td> |
7 |
</tr>
|
8 |
<?php endfor; ?> |
Здесь мы просто повторяем атрибут данных результатов, содержащих записи городов, и создавая строку таблицы для каждого из них.
Ссылки пагинации
Теперь, чтобы отобразить ссылки пагинатора, добавьте следующий код ниже таблицы.
1 |
<?php echo $Paginator->createLinks( $links, 'pagination pagination-sm' ); ?> |
Для метода Paginator createLinks мы передаем полученный параметр links и класс css для ссылок пагинации, использованных на bootstrap. Вот результат созданной страницы.



Заключение
Этот материал должен обеспечить вас всем, что вам нужно, чтобы осуществить пагинацию в вашем приложении.
Пожалуйста, не стесняйтесь, оставляйте ниже ваши вопросы, комментарии или обобщенный отзыв!