Вывод и фильтрация больших таблиц данных с помощью плагина DataTables + ColumnFilter. Часть 2

В первой части статьи рассказывалось об использовании плагина DataTables для вывода больших таблиц данных в режиме обработки данных на стороне сервера и применении дополнения Column Filter для индивидуальной фильтрации по столбцам

В этой части речь пойдет о более продвинутом использовании дополнения Column Filter, включающем:

  1. создание фильтров в виде списков с множественным выбором (т.е. с возможностью выбора нескольких значений);
  2. построение цепочек зависимых списков фильтрации.

Немного слов о втором пункте: цепочка зависимых фильтр-списков – это когда содержимое (опции) одного списка зависит от выбранной опции (опций) в другом списке (списках). Причем эта зависимость может быть строгой и нестрогой. Строгая зависимость имеет место, когда данный список не позволяет сделать выбор до тех пор, пока не сделан выбор в другом списке. Хорошо знакомый пример такой зависимости — выбор города проживания в онлайн-анкетах: сначала нужно выбрать регион (страну, область/штат/провинцию) и только после этого можно выбирать город. Зависимость может также быть и нестрогой: когда список позволяет сделать выбор без участия других фильтр-списков, но при этом выбор, сделанный в других списках, может изменить (ограничить) содержимое данного списка. Как правило это выглядит так: в начале список содержит полный набор всех возможных (для него) опций, который уменьшается в зависимости от выбранных опций в других фильтр-списках.

Для поддержки перечисленных возможностей мне пришлось внести в код дополнения небольшие изменения. Но об этом чуть позже.

По традиции предлагаю сразу посмотреть на работающий пример. Там же можно найти и его исходники.

Пример таблицы

В реализации примера были использованы:

  • плагин 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 + ColumnFilter. Часть 2: 2 комментария

  1. Савр, спасибо за статью, именно то что мне требуется сейчас: добавить пользовательские фильтры к плагину DataTables, но уже в wordpress.
    Пока только разбираюсь как это работает.
    Поделитесь, как вы отлаживали плагин, что логировали в ConnLimiter::check(‘log/conn_col_arts.log’)

  2. Спасибо за вопрос. Как именно я отлаживал плагин я уже и не помню, если честно), это было довольно давно. Впрочем, в самом плагине ColumnFilter отлаживать было особо нечего, он ведь написан не мной. Я только чуток поправил его код, чтобы добавить поддержку мультиселектов и зависимых списков. Изменения я скорее всего отлаживал с помощью console.log, как и обычно.
    А та строка, которую вы упомянули — не логирование, а ограничение числа запросов к AJAX-скрипту, это используется только в демо на моем сайте (для снижения нагрузки на сервер). Вообще-то этого не должно быть в статье, это попало туда по ошибке. Спасибо, что обратили на это внимание, я уже убрал эти строки из статьи.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>