Веб-разработчики довольно часто сталкиваются с задачей отображения на странице данных в табличной форме с возможностью их сортировки, поиска/фильтрации, разбиения на страницы и удобной навигации по ним. Например, для меня это стало актуально при создании админки для одного сайта. Каждый разработчик решает эту задачу по-своему. Многие не сильно с этим заморачиваются и в результате получаются интерфейсы в стиле ранних версий phpMyAdmin, с полным обновлением страницы после любого клика мышью. Но сейчас уже не начало 2000-х, а значит, пора уже и админки переводить на AJAX и jQuery. Мне в этом очень помог замечательный плагин DataTables, который избавил меня от необходимости изобретать велосипед и писать тонны кода. С его помощью я смог в сжатые сроки придать админке современный вид. Теперь я хочу поделиться некоторым опытом, накопленным за время использования этого плагина.
В этой статье я расскажу как с помощью DataTables и его дополнения Column Filter организовать постраничный вывод больших объемов данных с поддержкой индивидуальной фильтрации по столбцам. В этом случае необходимо задействовать в плагине режим обработки данных на стороне сервера. На сайте плагина есть подробный пример реализации этого режима. Здесь же я рассмотрю практические аспекты применения дополнения Column Filter и особенности реализации обработки данных на стороне сервера в этом случае.
Для тех, кому не терпится посмотреть, как это все работает, вот ссылка на работающий пример, где выводится таблица пользователей гипотетического хабра-подобного сайта). Там же можно найти и исходники примера.
В отличие от самого плагина, его дополнение Column Filter, к сожалению, не может похвастать подробной документацией. Множество его возможностей и особенностей почему-то нигде не описаны. Так, в примере настройки Column Filter не указана полная форма инициализации списка (ее можно найти только в исходниках дополнения), вместо нее используется краткая форма:
values: [ 'A', 'B', 'C' ]
которая актуальна только для списков с текстовыми значениями опций, совпадающими с их метками. Это может и годится для простейших статических таблиц, но не подходит для использования с базами данных, где, как правило, значения таких столбцов хранятся отдельно (в другой таблице) и связываются с основной таблицей с помощью ключей. В этом случае следует использовать полную форму инициализации списка:
values: [ {value:1, label:"Текст 1"}, {value:2, label:"Текст 2"}, ... ]
Кроме того, на сайте дополнения почему-то не указано, что в режиме обработки данных на стороне сервера ColumnFilter добавляет в Ajax-запросы к серверу свои дополнительные параметры, которые необходимо учитывать и обрабатывать соответствующим образом в скрипте обработки, иначе индивидуальная фильтрация не будет работать.
На основе примера с сайта DataTables я написал PHP-класс, реализующий обработку данных на стороне сервера, в котором содержатся изменения, необходимые для нормальной работы дополнения Column Filter. Его исходный код можно скачать со страницы примера. А здесь я привожу только интерфейс класса (объявления открытых методов) с комментариями:
abstract class DataTableBase { /* * Вывод данных из таблицы в формате JSON: * @param string $sTable - имя таблицы (используется в запросе SELECT) * @param array $aCols - массив столбцов для сортировки и фильтрации * @param string $sIndexCol - индексируемый столбец * @param array $aColFltFlags - массив флажков, определяющих, подлежит ли столбец общей фильтрации * @param string $sFetchCols - список выбираемых столбцов * @param string $sTableXtra - дополнение для блока FROM (для INNER JOIN) * @param string $sPreCond - необходимое условие (используется в блоке WHERE) * @return string|false - данные в формате JSON */ function output($sTable, $aCols, $sIndexCol='', $aColFltFlags=0, $sFetchCols='', $sTableXtra='', $sPreCond=''); /* * Вывод списка в формате JSON: * @param string $sTable - имя таблицы * @param string $sIdCol - индексируемый столбец * @param string $sTitleCol - текстовый столбец * @param string $sCond - условие фильтрации * @return string|false - список в формате JSON */ function outputDataMap($sTable, $sIdCol, $sTitleCol, $sCond='') /* * Подготовка данных для вывода (этот метод должен быть определен в наследнике): * @param array $aData - массив данных, извлеченных из БД * @return array - подготовленные данные */ abstract protected function buildData($aData); }
Данный класс поддерживает все типы фильтров для столбцов, используемые в Column Filter:
- обычный текст;
- числа и диапазоны чисел;
- даты и диапазоны дат;
- списки.
Следует отметить, что диапазоны дат в Column Filter реализуются с помощью Jquery UI, поэтому для их нормальной работы необходимо подключение этого плагина, а также соответствующих стилей и ресурсов. В основном по этой причине (мне не очень нравится этот громоздкий плагин) я и не стал использовать в моем примере фильтр с диапазоном дат, хотя в принципе это возможно – рассмотренный класс поддерживает обработку диапазонов дат.
Пример использования класса DataTableBase:
class DataTableUsers extends DataTableBase { function buildData($aData) { $a_res= array(); $a_badges= $this->sqlDataMap('user_badges', 'id', 'title'); foreach ($aData as $a_item) { $a_row= array(); $a_row[]= $a_item['login']; $a_row[]= $a_item['name']; $a_row[]= $a_item['email']; $a_row[]= $a_item['karma']; $a_row[]= isset($a_badges[$id= $a_item['badge']])? $a_badges[$id]: ''; $a_res[]= $a_row; } return $a_res; } function outputWrap() { return $this->output( 'users', array('login', 'name', 'email', 'karma', 'badge'), 'user_id', array(true, true, true, true, 0) ); } } /* Код подключения к БД */ require_once 'db_init.php'; if (!isset($_GET['action'])) return; $oDt= new DataTableUsers(); switch ($_GET['action']) { case 'data': echo $oDt->outputWrap(); break; case 'col_badges': if ($s= $oDt->outputDataMap('user_badges', 'id', 'title')) echo $s; break; }
На этом первая часть статьи закончена. Спасибо за внимание.
Во второй части я расскажу о том, как с помощью тех же самых инструментов плюс немного шаманства построить иерархию зависимых списков фильтрации и добавить к спискам возможность выбора нескольких значений.