() translation by (you can also view the original English article)
Я пам'ятаю, як багато років тому, коли я тільки починав писати код РНР і 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 записів, з якими можна попрацювати. Але краса РНР-скрипта, який ми будемо використовувати, в тому,
що його можна використовувати для будь-якої бази даних. Думаю, тепер ми всі можемо погодитись, що якщо ми вирішили
не розбивати наші результати, в кінці кінців ми отримаємо дуже довгий і громіздкими
список результатів, як для прикладу показано нижче:

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



Красиво, чи не так? Після того, як ви ввели клас пагінації в ваш код, ви можете
швидко і просто перетворити величезний набір даних в сторінки, по яким можна легко переміщуватися
за допомогою декількох рядків коду. Це справді так.
3. Рaginator
Даний приклад складатиметься з двох скриптів: багаторазового класу paginator і файла index, який буде виводити елементи таблиці і елементи управління.
Paginator.Class.php
Клас paginator буде мати тільки два метода і конструктор, ми будемо будувати їх поступово, пояснюючи кожний крок по мірі просування вперед.
1 |
<?php
|
2 |
|
3 |
class Paginator { |
4 |
|
5 |
private $_conn; |
6 |
private $_limit; |
7 |
private $_page; |
8 |
private $_query; |
9 |
private $_total; |
10 |
|
11 |
}
|
Це визначення тільки встановлює необхідні змінні-члени paginator. Оскільки це допоміжний клас і він розроблений тільки для пагінації, він буде покладатися на валідне підключення до сервера 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 |
}
|
Досить просто, чи не так? Цей метод тільки встановлює з'єднання об'єкта бази даних і необхідного запиту, після чого він обчислює необхідну кількість рядків, отриманих через цей запит без будь-яких обмежень і пропусків параметрів. Ця сумма необхідна для створення посилань на paginator.
Зверніть увагу, що для спрощення ми не виконуємо перевірку помилок або будь-яку іншу перевірку даних параметрів, але в реальності така перевірка необхідна.
Отримання результатів
Тепер давайте створимо метод, який фактично буде розбивати дані і повертати результати.
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. Іndex. 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; ?> |
Тепер, щоб скористатися класом Paginator, додайте наступний РНР-код в верхній частині документа.
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 |
?>
|
Цей скрипт досить простий. Ми всього лиш викликали клас Paginator. Зверінть увагу, що даний код підрозуміває, що цей файл знаходиться в тій же папці, що і файл 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. Ось результат отриманої сторінки.



Висновок
Даний матеріал повинен забезпечити вас всім, що необхідно, для реалізації пагінації в вашому додатку.
Будь ласка, не вагайтесь, залишайте нижче ваші питання, коментарі або загальний відгук!