В первой части статьи рассказывалось об использовании плагина DataTables для вывода больших таблиц данных в режиме обработки данных на стороне сервера и применении дополнения Column Filter для индивидуальной фильтрации по столбцам
В этой части речь пойдет о более продвинутом использовании дополнения Column Filter, включающем:
- создание фильтров в виде списков с множественным выбором (т.е. с возможностью выбора нескольких значений);
- построение цепочек зависимых списков фильтрации.
Немного слов о втором пункте: цепочка зависимых фильтр-списков – это когда содержимое (опции) одного списка зависит от выбранной опции (опций) в другом списке (списках). Причем эта зависимость может быть строгой и нестрогой. Строгая зависимость имеет место, когда данный список не позволяет сделать выбор до тех пор, пока не сделан выбор в другом списке. Хорошо знакомый пример такой зависимости — выбор города проживания в онлайн-анкетах: сначала нужно выбрать регион (страну, область/штат/провинцию) и только после этого можно выбирать город. Зависимость может также быть и нестрогой: когда список позволяет сделать выбор без участия других фильтр-списков, но при этом выбор, сделанный в других списках, может изменить (ограничить) содержимое данного списка. Как правило это выглядит так: в начале список содержит полный набор всех возможных (для него) опций, который уменьшается в зависимости от выбранных опций в других фильтр-списках.
Для поддержки перечисленных возможностей мне пришлось внести в код дополнения небольшие изменения. Но об этом чуть позже.
По традиции предлагаю сразу посмотреть на работающий пример. Там же можно найти и его исходники.
В реализации примера были использованы:
- плагин DataTables;
- PHP-класс DataTableBase (см. часть 1) – основа для реализации режима обработки данных на стороне сервера;
- моя расширенная версия дополнения ColumnFilter – ColumnFilterExt.
Справка по использованию дополнения ColumnFilterExt на стороне клиента
Инициализация ColumnFilter(Ext):
oTable.columnFilter(oSettings);
где:
oTable — объект возвращаемый контекстным методом dataTable();
oSettings — объект содержащий параметры настройки ColumnFilter(Ext).
Список свойств объекта параметров настройки ColumnFilter(Ext):
Имя свойства | Тип | Описание |
---|---|---|
sPlaceHolder | String | Место куда должны вставляться фильтры столбцов. Возможные значения: "tfoot", "thead:before", "thead:after". По умолчанию: "tfoot" (подвал таблицы). |
sRangeSeparator | String | Разделитель, используемый при отправке на сервер диапазонов значений. По умолчанию: "~". |
sRangeFormat | String | Шаблон текста для ввода диапазонов значений. По умолчанию: "From {from} to {to}". |
aoColumns | Array | Массив объектов, содержащих настройки индивидуальных фильтров для каждого столбца. |
aoColumns[*] | Object | Объект, содержащий настройки индивидуального фильтра. Его свойства описываются ниже. |
aoColumns[*].type | String | Тип индивидуального фильтра для текущего столбца (обязательное свойство). Возможные значения: "text", "number", "date", "select". |
aoColumns[*].values | Array | Массив, содержащий опции списка, в случае использования фильтра типа "select". Каждый из элементов массива может быть строкой либо объектом в форме {value:"value", label:"label"}. |
aoColumns[*].bMultiselect | Boolean | Признак использования списка с множественным выбором. |
aoColumns[*].aiFactors | Array | Массив позиций столбцов фильтр-списков, влияющих на содержимое фильтр-списка текущего столбца. Позиции задаются в виде отрицательных смещений относительно текущего столбца (их порядок в массиве не важен). |
aoColumns[*].iMasterFactor | Number | Позиция столбца фильтр-списка, влияющего со строгой зависимостью на содержимое фильтр-списка текущего столбца. Задается в виде отрицательного числа. |
sSelectSource | String | Адрес (URL) общего внешнего источника для содержимого всех фильтр-списков, для которых не заданы опции (через свойство values). |
oSelectSourceParams | Object | Дополнительные данные (HTTP-параметры) посылаемые при Ajax-запросе содержимого фильтр-списков. |
oSelectSourceMethod | String | Тип Ajax-запроса содержимого фильтр-списков ("GET" или "POST"). |
Пример использования дополнения ColumnFilterExt на стороне клиента
<table width="100%" cellpadding="0" cellspacing="0" border="0" class="display" id="table-example"> <thead> <tr> <th>Название</th> <th>Автор</th> <th>Вид</th> <th>Категория</th> <th>Тема</th> </tr> </thead> <tbody> <tr> <td colspan="5" class="dataTables_empty">Loading data from server</td> </tr> </tbody> <tfoot> <tr> <th>Название</th> <th>Автор</th> <th>Вид:</th> <th>Категория</th> <th>Тема</th> </tr> </tfoot> </table> ... <script type="text/javascript"> $().ready(function() { $('#table-example').dataTable( { aoColumnDefs: [ { bSortable: false, aTargets: [1] } ], sPaginationType: "full_numbers", oLanguage: { sUrl: "datatables_lang_rus.txt" }, aaSorting: [[0, "asc" ]], bServerSide: true, fnServerParams: function (aData) { aData.push({name:'action', value:'data'}); }, sAjaxSource: "ajax_table_arts.php", bProcessing: false } ).columnFilter({ aoColumns: [ { type: "text" }, { type: "select" }, { type: "select", bMultiselect: true }, { type: "select", aiFactors: [-1] }, { type: "select", aiFactors: [-2], iMasterFactor: -1 } ], sRangeFormat: "От {from} до {to}", sSelectSource: "ajax_table_arts.php", oSelectSourceParams: {'action': "col_values"} }); }); </script>
Справка по использованию дополнения ColumnFilterExt на стороне сервера
При запросе содержимого фильтр-списков дополнение ColumnFilterExt отправляет на сервер (см. параметр настройки sSelectSource) следующие параметры:
Имя | Тип | Описание |
---|---|---|
iColumn | Number | Номер столбца содержащего фильтр-список (нумерация от нуля) |
sFactorCol(int) | Number / String | Значение выбранной опции фильтр-списка для столбца с номером (int), от которого зависит данный список. В случае выбора нескольких опций влияющего фильтр-списка на сервер передается строка из значений выбранных опций, разделенных запятой |
Класс DataTableBase (см. часть 1) содержит методы getRequestedColIndex() и buildFactorCondition(), упрощающие обработку перечисленных параметров в обработчике Ajax-запроса. Вот их объявления:
/* * Определение номера столбца запрашиваемого фильтр-списка: * Возвращает: * [integer] - номер столбца в Ajax-запросе */ function getRequestedColIndex(); /* * Построение SQL условия, накладываемого влияющим фильтр-списком: * Parameters: * $sColName - имя столбца (в БД) влияющего фильтр-списка * $iColIndex - номер столбца влияющего фильтр-списка * Returns: * [string] - SQL условие */ function buildFactorCondition($sColName, $iColIndex);
В ответ на Ajax-запрос сервер должен возвратить JSON-массив, содержащий опции фильтр-списка. Массив должен иметь такой же формат, как у параметра настройки aoColumns[*].values.
Пример использования дополнения ColumnFilterExt на стороне сервера:
class DataTableArts extends DataTableBase { function buildData($aData) { $aRet= array(); $a_usernames= $this->sqlDataMap('users', 'user_id', 'name'); $a_userlogins= $this->sqlDataMap('users', 'user_id', 'login'); $a_types= $this->sqlDataMap('art_types', 'id', 'title'); $a_cats= $this->sqlDataMap('art_cats', 'id', 'title'); $a_subjs= $this->sqlDataMap('art_subjs', 'id', 'title'); foreach ($aData as $a_item) { $a_row = array(); $a_row[]= $a_item['title']; $id= $a_item['author_id']; $a_row[]= (isset($a_usernames[$id])? $a_usernames[$id]: ''). (isset($a_userlogins[$id])? ('<br>('.$a_userlogins[$id].')'): ''); $a_row[]= isset($a_types[$id= $a_item['type_id']])? $a_types[$id]: ''; $a_row[]= isset($a_cats[$id= $a_item['cat_id']])? $a_cats[$id]: ''; $a_row[]= isset($a_subjs[$id= $a_item['subj_id']])? $a_subjs[$id]: ''; $aRet[] = $a_row; } return $aRet; } function outputWrap() { return $this->output( 'articles', array('title', 'author_id', 'type_id', 'cat_id', 'subj_id'), 'id', array(true, 0, 0, 0, 0, 0) ); } } /* Код подключения к БД */ require_once 'db_init.php'; if (!isset($_GET['action'])) return; $oDt= new DataTableArts(); switch ($_GET['action']) { case 'data': echo $oDt->outputWrap(); break; case 'col_values': switch ($oDt->getRequestedColIndex()) { case 1: // user: echo $oDt->outputDataMap('users ORDER BY name', 'user_id', 'name'); break; case 2: // type: echo $oDt->outputDataMap('art_types', 'id', 'title'); break; case 3: // category: if ($cond= $oDt->buildFactorCondition('type_id', 2)) echo $oDt->outputDataMap( 'art_cats x INNER JOIN articles y ON x.id = cat_id', 'x.id', 'x.title', $cond ); else echo $oDt->outputDataMap('art_cats', 'id', 'title'); break; case 4: // subject: if ($cond2= $oDt->buildFactorCondition('type_id', 2)) { if ($cond= $oDt->buildFactorCondition('y.cat_id', 3)) echo $oDt->outputDataMap( 'art_subjs x INNER JOIN articles y ON x.id = subj_id', 'x.id', 'x.title', "$cond2 AND $cond" ); } else { if ($cond= $oDt->buildFactorCondition('cat_id', 3)) echo $oDt->outputDataMap( 'art_subjs', 'id', 'title', $cond ); } break; } }
На этом статья закончена. Спасибо за внимание.
Савр, спасибо за статью, именно то что мне требуется сейчас: добавить пользовательские фильтры к плагину DataTables, но уже в wordpress.
Пока только разбираюсь как это работает.
Поделитесь, как вы отлаживали плагин, что логировали в ConnLimiter::check(‘log/conn_col_arts.log’)
Спасибо за вопрос. Как именно я отлаживал плагин я уже и не помню, если честно), это было довольно давно. Впрочем, в самом плагине ColumnFilter отлаживать было особо нечего, он ведь написан не мной. Я только чуток поправил его код, чтобы добавить поддержку мультиселектов и зависимых списков. Изменения я скорее всего отлаживал с помощью console.log, как и обычно.
А та строка, которую вы упомянули — не логирование, а ограничение числа запросов к AJAX-скрипту, это используется только в демо на моем сайте (для снижения нагрузки на сервер). Вообще-то этого не должно быть в статье, это попало туда по ошибке. Спасибо, что обратили на это внимание, я уже убрал эти строки из статьи.