В этой статье мы расскажем, как легко разбить на пейджинг ваш набор данных с помощью PHP и AJAX с помощью jQuery . Мы также собираемся использовать инфраструктуру Silex для простоты.
Источник данных
Во-первых, нам нужны данные для разбивки на страницы!
Я бы | имя | возраст |
---|---|---|
1 | Джейми | 43 |
2 | Джо | 24 |
3 | Фред | 23 |
4 | Clive | 92 |
5 | Рой | 73 |
6 | Geoff | 24 |
7 | луч | 12 |
8 | Джон | 9 |
9 | Пит | 32 |
10 | Ральф | 34 |
В этом примере мы будем использовать MySQL , но, конечно, мы можем легко поменять его для SQLite или любой другой СУБД. Мы назовем наш example
базы данных, и в таблице будут названы people
.
Бэкэнд
Поскольку мы собираемся построить наше приложение на Silex, нам нужно сначала установить Silex с помощью Composer . Установите его через composer с помощью команды composer require silex/silex
.
Далее нам нужно настроить наш файл index.php
, подключиться к источнику данных и выбрать базу данных. Мы собираемся использовать PDO для этого, так как позже его легко заменить на другое программное обеспечение базы данных, и оно также обрабатывает пользовательский ввод (таким образом предотвращая атаки с использованием SQL-инъекций). Если вы все еще застряли на mysqli
или, что еще хуже, на расширении mysql
, посмотрите этот учебник . Мы собираемся поместить соединение в контейнер $app
, чтобы позже его было легко использовать в наших маршрутах.
$app['db'] = function () { $host = 'localhost'; $db_name = 'example'; $user = 'root'; $pass = ''; return new \PDO( "mysql:host={$host};dbname={$db_name}", $user, $pass, array(\PDO::ATTR_EMULATE_PREPARES => false) ); };
Теперь, когда мы подключены к базе данных, мы собираемся предоставить три маршрута в нашем приложении, которые позволят;
- Получение раздела набора результатов, который мы хотим отобразить
- Получение общего количества строк в наборе результатов
- Просмотр HTML-интерфейса
Первый маршрут выглядит следующим образом:
$app->get('/data/page/{page_num}/{rows_per_page}', function ($page_num, $rows_per_page) use ($app) { $start = ((int)$page_num - 1) * (int)$rows_per_page; $total_rows = (int)$rows_per_page; $stmt = $app['db']->prepare( 'SELECT `name` FROM `people` ORDER BY `name` LIMIT :from, :total_rows' ); $stmt->bindParam('from', $start); $stmt->bindParam('total_rows', $total_rows); $stmt->execute(); $result = $stmt->fetchAll(\PDO::FETCH_ASSOC); return $app->json($result); });
Это позволяет нашему веб-интерфейсу получить подмножество набора результатов из базы данных. Два параметра могут быть предоставлены через URI; один для номера страницы и один для того, сколько строк должно быть на каждой странице. Номер страницы используется рядом со строками на странице, чтобы определить, с какой строки в наборе результатов мы должны начать извлекать данные.
В этом примере мы собираемся разбить на страницы все данные из таблицы. Однако в реальном приложении нам, вероятно, потребуется включить предложение WHERE
для фильтрации возвращаемых данных. Например, если мы хотим отображать людей моложе 30 лет, мы бы изменили приведенный выше код, включив в запрос предложение WHERE
:
$stmt = $app['db']->prepare( 'SELECT `name` FROM `people` WHERE `age` < 30 ORDER BY `name` LIMIT :from, :total_rows' );
Запрос использует подготовленный оператор для вставки переменных, для которых был запрошен номер страницы и сколько строк вывести на страницу. Они предоставляются в URI и затем динамически вставляются в предложение LIMIT
в запросе SQL.
Второй маршрут предоставляет возможность выполнить запрос, чтобы вернуть общее количество строк в таблице. Это важно, потому что мы хотим использовать ссылки с номерами страниц во внешнем интерфейсе. Этот маршрут должен выглядеть так:
$app->get('/data/countrows', function () use ($app) { $stmt = $app['db']->query( 'SELECT COUNT(`id`) AS `total_rows` FROM `people`' ); $result = $stmt->fetch(\PDO::FETCH_ASSOC); return $app->json($result); });
Здесь мы используем агрегатную функцию SQL, которая называется COUNT()
. Это функция GROUP BY
— это означает, что она сгруппирует выбранные строки вместе, чтобы обеспечить единственную строку. В этом случае он предоставляет сумму всех выбранных строк в виде целого числа.
Следует отметить еще одну важную внутреннюю функцию, заключающуюся в том, что маршруты выборки данных должны возвращаться в формате JSON, поскольку это облегчит интеграцию в интерфейс. Silex позаботится об этом для нас, используя вспомогательный метод JSON .
Последний маршрут просто инструктирует корневой URI для вывода HTML-страницы.
$app->get('/', function () use ($app) { return file_get_contents(__DIR__.'/../resources/views/index.html'); });
Что приводит нас к …
Интерфейс
Теперь самое интересное!
Нам нужно убедиться, что наш веб-интерфейс включает в себя jQuery и содержит контейнер для ссылок на номера страниц и сами данные.
<ul id="rows"></ul> <ul id="page-numbers"></ul> <script src="//code.jquery.com/jquery-1.11.2.min.js"></script>
В этом примере мы собираемся использовать <ul>
s, однако в реальном приложении мы можем захотеть использовать <table>
для данных (особенно важно, если мы хотим отображать более одной части информации в строке).
Нам понадобятся две пользовательские функции в Javascript; один для получения конкретной страницы, а другой для инициализации ссылок на номера страниц. В нашем примере мы также сделали небольшую настройку, прежде чем перейдем к основному разделу кода:
var rows_per_page = 3; var total_rows;
Это инициализирует две глобальные переменные для количества строк, которые мы хотим показать на странице, и общего количества строк в таблице (последние будут получены через AJAX в ближайшее время).
Чтобы инициализировать ссылки на номера страниц, нам нужно сделать AJAX-вызов нашего скрипта PHP, чтобы получить общее количество строк в таблице. Затем мы будем использовать обратный вызов функции success, чтобы внедрить HTML-код для ссылки на каждую страницу в наш контейнер ссылок на номера страниц в зависимости от общего количества строк. Это должно выглядеть примерно так:
function initPageNumbers() { //Get total rows number $.get('data/countrows', function(data){ total_rows = parseInt(data.total_rows); //Loop through every available page and output a page link var count = 1; for(var x = 0; x < total_rows; x += rows_per_page) { $('#page-numbers').append('<li><a href="#'+count+'" onclick="getPage('+count+');">'+count+'</a></li>'); count++; } }); }
Обратите внимание, что мы внедряем HTML-код для ссылок на страницы с уже прикрепленным вызовом функции Javascript. Это означает, что щелчок по этим ссылкам вызовет нашу функцию извлечения страниц, а мы не добавляем событие нажатия вручную. Также обратите внимание, что для атрибута href
задан фрагмент хеша с номером страницы — это означает, что URI будет изменен, чтобы отразить, какой номер страницы мы просматриваем.
Функция для извлечения определенной страницы также использует вызов AJAX, но имеет несколько ключевых отличий;
function getPage(page_num) { //Clear the existing data view $('#rows').html(''); //Get subset of data $.get('data/page/'+page_num+'/'+rows_per_page, function(data){ //Loop through each row and output the data $(data).each(function(){ $('#rows').append('<li>'+this.name+'</li>'); }); }); }
.function getPage(page_num) { //Clear the existing data view $('#rows').html(''); //Get subset of data $.get('data/page/'+page_num+'/'+rows_per_page, function(data){ //Loop through each row and output the data $(data).each(function(){ $('#rows').append('<li>'+this.name+'</li>'); }); }); }
Первое отличие, которое следует отметить, заключается в том, что контейнер сначала очищается с помощью функции jQuery html()
. Это связано с тем, что эта функция будет вызываться несколько раз, и для ее работы каждый раз требуется чистый лист. Второе отличие состоит в том, что сами данные перебираются при обратном вызове функции успеха. Это делается с помощью очень полезной функции jQuery each()
.
Обе функции используют сокращенную функцию get()
jQuery AJAX для выполнения HTTP-запроса GET. Вместо этого может быть использована функция jQuery ajax()
, которая допускает дополнительную настройку, но сокращение вполне подходит для этого примера.
Функция getPage()
также отличается от функции initPageNumbers()
что она передает два параметра вместе с запросом HTTP GET. Они предоставляются в объекте как второй параметр вызова функции get()
. Затем они обрабатываются сценарием PHP, как определено ранее.
Теперь осталось только настроить логику инициализации страницы. Для этого мы будем использовать функцию jQuery ready()
;
$(document).ready(function(){ //Set up the page number links initPageNumbers(); //Set the default page number var page_num = 1; //If there's a hash fragment specifying a page number if(window.location.hash !== '') { //Get the hash fragment as an integer var hash_num = parseInt(window.location.hash.substring(1)); //If the hash fragment integer is valid if(hash_num > 0) { //Overwrite the default page number with the user supplied number page_num = hash_num; } } //Load the first page getPage(page_num); });
Это вызывает нашу функцию инициализации номера страницы и выбирает страницу набора результатов для отображения.
Если в конце URI был предоставлен хеш-фрагмент, он будет проанализирован в номер страницы и установлен в качестве отображаемой страницы, в противном случае страница 1 отображается по умолчанию. Эта функциональность фрагмента хеша позволяет поисковым системам индексировать наши отдельные страницы, а не только первую страницу. Это также позволяет нам предоставлять внешние ссылки непосредственно на определенный номер страницы.
Усовершенствованием этого метода было бы предоставление ссылок, таких как app.com/page/2
, app.com/page/3
и т. Д. Преимущество такого использования по сравнению с использованием хеш-фрагментов или строк запроса (суффикс ?
В URI) что он лучше поддерживается поисковыми системами и сканерами веб-сайтов.
Следует обратить внимание на логику инициализации нашей страницы: две операции AJAX будут выполняться асинхронно, а это означает, что браузер отключится и извлечет страницу данных одновременно с определением количества отображаемых ссылок на странице. Это очень удобно для пользователей, так как означает меньше времени для ожидания, прежде чем получить полный опыт.
Заключительные замечания и альтернативные решения
Техника разбиения на страницы набора результатов с использованием AJAX лучше всего подходит для больших наборов результатов, где объем данных настолько велик, что он либо:
- Вредно для производительности браузера, чтобы включить и манипулировать всем набором результатов в DOM одновременно, или;
- Очень медленно получать и получать весь набор результатов из СУБД в одном запросе.
Если результирующий набор недостаточно велик, чтобы вызвать такие проблемы, тогда предпочтительным методом будет загрузка всего результирующего набора в DOM при загрузке страницы, а затем использование Javascript для разбивки данных на страницы в пользовательском интерфейсе с использованием CSS «показать / скрыть» техника.
И это все, что нужно сделать! Если вы хотите просмотреть и загрузить полный исходный код для описанного примера, вы можете сделать это с помощью этого репозитория GitHub .