В первой части статьи рассказывалось об использовании плагина 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])? ('
('.$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:
ConnLimiter::check('log/conn_col_arts.log');
echo $oDt->outputDataMap('art_types', 'id', 'title');
break;
case 3: // category:
ConnLimiter::check('log/conn_col_arts.log');
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:
ConnLimiter::check('log/conn_col_arts.log');
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;
}
}
На этом статья закончена. Спасибо за внимание.
Автор: xmeoff