Статьи

Разбивка на страницы с помощью jQuery, AJAX и PHP

В этой статье мы расскажем, как легко разбить на пейджинг ваш набор данных с помощью PHP и AJAX с помощью jQuery . Мы также собираемся использовать инфраструктуру Silex для простоты.

jQuery Logo

Источник данных

Во-первых, нам нужны данные для разбивки на страницы!

Я бы имя возраст
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) ); }; 

Теперь, когда мы подключены к базе данных, мы собираемся предоставить три маршрута в нашем приложении, которые позволят;

  1. Получение раздела набора результатов, который мы хотим отобразить
  2. Получение общего количества строк в наборе результатов
  3. Просмотр 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 лучше всего подходит для больших наборов результатов, где объем данных настолько велик, что он либо:

  1. Вредно для производительности браузера, чтобы включить и манипулировать всем набором результатов в DOM одновременно, или;
  2. Очень медленно получать и получать весь набор результатов из СУБД в одном запросе.

Если результирующий набор недостаточно велик, чтобы вызвать такие проблемы, тогда предпочтительным методом будет загрузка всего результирующего набора в DOM при загрузке страницы, а затем использование Javascript для разбивки данных на страницы в пользовательском интерфейсе с использованием CSS «показать / скрыть» техника.

И это все, что нужно сделать! Если вы хотите просмотреть и загрузить полный исходный код для описанного примера, вы можете сделать это с помощью этого репозитория GitHub .