Создаем разные виды Drupal HTML таблиц (часть 2)

Создаем разные виды Drupal HTML таблиц (часть 2)

Как и обещал, представляю продолжение статьи часть 1. Хук меню, описывающий страницу отображения каждого вида таблиц абсолютно идентичен из первой части, поэтому дублировать его не будем. Будут использованы те же имена функций MYMODULE_tables_page() и MYMODULE_get_all_nodes(). Их содержимое будет также описано (или указано) для каждого из рассматриваемых видов.

Содержание

Таблица с иерархией строк

Довольно распространенный вид таблиц. Первое, что приходит в голову - это таблица терминов таксономии, таблица меню со ссылками. В данном виде таблиц можно указать родительский и дочерний элемент.
По традиции начнем с hook_theme(). В файле MYMODULE.module

1
2
3
4
5
6
7
8
9
10
11
/**
 * Implements hook_theme().
 */
function MYMODULE_theme() {
  return array(
    'hierarchy' => array(
      'render element' => 'form',
      'file' => 'MYMODULE.theme.inc',
    ),
  );
}

Не забудем упомянуть функцию по выборке необходимых значений из таблицы node.

1
2
3
4
5
6
7
8
9
10
/**
 * Get nodes data.
 */
function MYMODULE_get_all_nodes() {
  return db_select('node', 'n')
    ->fields('n', array('nid', 'type', 'title', 'created'))
    ->condition('n.status', NODE_PUBLISHED)
    ->execute()
    ->fetchAllAssoc('nid');
}

Далее идет описание функции MYMODULE_tables_page()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/**
 * Example tables page.
 */
function MYMODULE_tables_page() {
  $nodes = MYMODULE_get_all_nodes();
  $form['table'] = array(
    '#type'  => 'container',
    '#theme' => 'hierarchy',
  );
 
  foreach ($nodes as $nid => $node) {
    // Идентификатор строки (в нашем случае ID ноды).
    $form['table'][$nid]['rowid'] = array(
      '#type'  => 'hidden',
      '#value' => $nid,
    );
 
    // Элемент формы, отвечающий за иерархию в дереве.
    $form['table'][$nid]['depth'] = array(
      '#type' => 'hidden',
      // Здесь должно быть сохраненное значение порядка иерархии.
      '#default_value' => '',
    );
 
    // Элемент формы, отвечающий за родительскую связь.
    $form['table'][$nid]['parent'] = array(
      '#type' => 'hidden',
      // Здесь должно быть сохраненное значение ID родительского элемента
      // (в нашем случае ID родительской ноды).
      '#default_value' => '',
    );
 
    $form['table'][$nid]['title'] = array(
      '#markup' => $node->title,
    );
 
    $form['table'][$nid]['type'] = array(
      '#markup' => $node->type,
    );
 
    $form['table'][$nid]['created'] = array(
      '#markup' => date('d-m-Y H:i', $node->created),
    );
 
    $form['table'][$nid]['link'] = array(
      '#type' => 'link',
      '#title' => t('Edit'),
      '#href' => $nid . '/edit',
    );
 
    // Вес.
    $form['table'][$nid]['weight'] = array(
      '#type'  => 'weight',
      '#delta' => 10,
      '#title' => t('Weight'),
      '#title_display' => 'invisible',
      '#default_value' => '',
    );
  }
 
  return $form;
}

Функция темизации theme_hierarchy(), размещенная в файле MYMODULE.theme.inc.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/**
 * Returns HTML for a hierarchy table.
 */
function theme_hierarchy($vars) {
  $form = $vars['form'];
  $rows = array();
  foreach (element_children($form) as $key) {
    $item = &$form[$key];
 
    // Добавляем css классы к элементам формы.
    $item['rowid']['#attributes']['class'][]  = 'table-rowid';
    $item['depth']['#attributes']['class'][]  = 'table-depth';
    $item['parent']['#attributes']['class'][] = 'table-parent';
    $item['weight']['#attributes']['class'][] = 'table-weight';
 
    // Смещение.
    $indentation = '';
    if (!empty($item['depth']['#value'])) {
      $indentation = theme('indentation', array(
        'size' => $item['depth']['#value'],
      ));
    }
 
    $row = array();
    // Заполняем строки отрендеренными элементами.
    $row[] = $indentation . drupal_render($item['title']);
    $row[0] .= drupal_render($item['parent']) . drupal_render($item['rowid']) . drupal_render($item['depth']);
    $row[] = drupal_render($item['type']);
    $row[] = drupal_render($item['created']);
    $row[] = drupal_render($item['link']);
    $row[] = drupal_render($item['weight']);
 
    $rows[$key]['data'] = $row;
    $rows[$key]['class'][] = 'draggable';
  }
 
  // Шапка таблицы.
  $header = array(
    t('Title'),
    t('Type'),
    t('Date'),
    t('Action'),
    t('Weight'),
  );
 
  $output = theme('table', array(
    'header' => $header,
    'rows'   => $rows,
    'empty'  => t('Table is empty'),
    'attributes' => array(
      'id' => 'table-id',
    ),
  ));
 
  // Добавляем tableDrag JavaScript поведение.
  drupal_add_tabledrag('table-id', 'match', 'parent', 'table-parent', 'table-parent', 'table-rowid', FALSE);
  drupal_add_tabledrag('table-id', 'depth', 'group', 'table-depth', NULL, NULL, FALSE);
  drupal_add_tabledrag('table-id', 'order', 'sibling', 'table-weight');
 
  $output .= drupal_render_children($form);
  return $output;
}

В итоге получаем вот такую таблицу
hierarchy_table

Draggable таблица с возможностью выбора строк

Это симбиоз двух видов таблиц: draggable и tableselect. Из коробки друпал не предоставляет такой вид таблиц, поэтому придется "извращаться":)
Опишем хук темы.

1
2
3
4
5
6
7
8
9
10
11
/**
 * Implements hook_theme().
 */
function MYMODULE_theme() {
  return array(
    'drag_tableselect' => array(
      'render element' => 'form',
      'file' => 'MYMODULE.theme.inc',
    ),
  );
}

Содержимое функции MYMODULE_tables_page() приведено ниже. Элемент формы weight не содержит атрибутов #theme, #theme_wrappers, #pre_render. Именно поэтому вызов напрямую drupal_render() возвращает пустую строку. Скорее всего в данной ситуации есть несколько корректных решений, но мы применим следующее: вызовем процесс функцию form_process_weight() напрямую, которая в свою очередь преобразует элемент weight в элемент select.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/**
 * Example tables page.
 */
function MYMODULE_tables_page() {
  $nodes = MYMODULE_get_all_nodes();
  $options = array();
 
  // Элемент веса.
  $weight = array(
    '#type'  => 'weight',
    '#delta' => 10,
    '#title' => t('Weight'),
    '#title_display' => 'invisible',
    '#default_value' => 0,
    '#attributes' => array(
      'class' => array('drag-order-weight'),
    ),
  );
  $weight = form_process_weight($weight);
 
  // Заполняем массив options.
  foreach ($nodes as $nid => $node) {
    $options[$nid][] = $node->title;
    $options[$nid][] = $node->type;
    $options[$nid][] = date('d-m-Y H:i', $node->created);
    $options[$nid][] = l(t('Edit'), $nid . '/edit');
    $options[$nid][] = $weight;
  }
 
  // Шапка таблицы.
  $header = array(
    t('Title'),
    t('Type'),
    t('Date'),
    t('Action'),
    t('Weight'),
  );
 
  $form['table'] = array(
    '#type' => 'tableselect',
    '#theme' => 'drag_tableselect',
    '#header' => $header,
    '#options' => $options,
    '#empty' => t('No content available.'),
  );
 
  return $form;
}

Несмотря на то, что тема theme_drag_tableselect() будет очень похожа на тему theme_tableselect(), нам необходимо указать тип tableselect элементу table. Это позволит использовать препроцесс функцию для чекбоксов, которая уже реализована для типа элемента формы tableselect.
Подошли непосредственно к самой темирующей функции.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/**
 * Returns HTML for drag_tableselect table.
 */
function theme_drag_tableselect($vars) {
  $form = $vars['form'];
  $rows = array();
  $header = $form['#header'];
  if (!empty($form['#options'])) {
    // Генерируем строку таблицы.
    foreach (element_children($form) as $key) {
      $row = array();
 
      $row['data'] = array();
      if (isset($form['#options'][$key]['#attributes'])) {
        $row += $form['#options'][$key]['#attributes'];
      }
      // Рендерим чекбокс/радиокнопку.
      $row['data'][] = drupal_render($form[$key]);
      $row['class'][] = 'draggable';
 
      // Заполняем остальные ячейки.
      foreach ($form['#header'] as $fieldname => $title) {
        $item = $form['#options'][$key][$fieldname];
        $row['data'][] = is_array($item) ? drupal_render($item) : $item;
      }
      $rows[] = $row;
    }
    // Отображем доп. чекбокс "Выбрать все".
    if ($form['#js_select']) {
      drupal_add_js('misc/tableselect.js');
      array_unshift($header, array('class' => array('select-all')));
    }
    else {
      // Добавляем пустую ячейку в шапку таблицы в случае, когда отоюражаем
      // радиокнопки или когда не нужно отображать чекбокс "Выбрать все".
      array_unshift($header, '');
    }
  }
  $form['#attributes']['id'] = 'drag-tableselect-id';
  drupal_add_tabledrag('drag-tableselect-id', 'order', 'sibling', 'drag-order-weight');
  return theme('table', array(
    'header' => $header,
    'rows' => $rows,
    'empty' => $form['#empty'],
    'attributes' => $form['#attributes'],
  ));
}

Результат
draggable_tableselect

Таблица блоков (как на странице admin/structure/blocks)

Очень интересный вид таблицы. Из коробки также нет удобного инструмента для создания данной таблицы. Для большей наглядности возьмем таблицу базы данных, в которой есть связь родитель-потомок, например, таблица menu_links.
Объявляем hook_theme() в MYMODULE.module. Помимо основной темирующей функции (назовем ее blocks_table) еще потребуется шаблон blocks_table_template. Шаблон находится в папке templates.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
 * Implements hook_theme().
 */
function MYMODULE_theme() {
  return array(
    'blocks_table' => array(
      'render element' => 'form',
      'file' => 'MYMODULE.theme.inc',
    ),
    'blocks_table_template' => array(
      'template'  => '/templates/MYMODULE-blocks-table',
      'variables' => array(
        'rows' => array(),
        'cells' => array(),
        'header' => array(),
        'empty' => NULL,
      ),
    ),
  );
}

Функция MYMODULE_tables_page() примет следующий вид.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
/**
 * Example tables page.
 */
function MYMODULE_tables_page($form, $form_state) {
  // Массив меню объектов.
  $menus = MYMODULE_get_menus();
  $form['table'] = array(
    '#theme' => 'blocks_table',
    '#tree' => TRUE,
  );
  // Подключение необходимых файлов js и css.
  $path = drupal_get_path('module', 'MYMODULE');
  $form['table']['#attached']['css'][] = $path . '/css/MYMODULE.css';
  $form['table']['#attached']['js'][]  = $path . '/js/MYMODULE.js';
 
  // Формируем заголовки регионов.
  $regions = array();
  foreach ($menus as $menu_name => $menu) {
    $regions[$menu_name] = $menu->title;
    $form['table']['rows'][$menu_name]['name'] = array(
      '#markup' => $menu->title,
    );
  }
  // Список ссылок меню.
  $links = MYMODULE_get_menu_links();
  // Формируем массив ячеек, которые будут располагаться в строке.
  foreach ($links as $mlid => $link) {
    $menu = $link->menu_name;
    $form['table']['cells'][$menu][$mlid]['link_title'] = array(
      '#markup' => $link->link_title,
    );
    $form['table']['cells'][$menu][$mlid]['link_path'] = array(
      '#markup' => $link->link_path,
    );
    $form['table']['cells'][$menu][$mlid]['menu_name'] = array(
      '#markup' => $link->menu_name,
    );
    $form['table']['cells'][$menu][$mlid]['region'] = array(
      '#type' => 'select',
      '#default_value' => $menu,
      '#options' => $regions,
      '#attributes' => array(
        'class' => array('cell-region-select', 'cell-region-' . $menu),
      ),
    );
    $form['table']['cells'][$menu][$mlid]['weight'] = array(
      '#type'  => 'weight',
      '#delta' => 20,
      '#default_value' => 0,
      '#attributes' => array(
        'class' => array('cell-weight', 'cell-weight-' . $menu),
      ),
    );
  }
 
  return $form;
}

Функция MYMODULE_get_menus() возвращает массив меню объектов из таблицы бд menu_custom.

1
2
3
4
5
6
7
8
9
/**
 * Menu list.
 */
function MYMODULE_get_menus() {
  return db_select('menu_custom', 'm')
    ->fields('m')
    ->execute()
    ->fetchAllAssoc('menu_name');
}

Фукнция MYMODULE_get_menu_links() в свою очередь возвращает массив объектов ссылок. Здесь ограничимся ссылками из меню user-menu, количества которых вполне достаточно для демонстрации таблицы и ее работы.

1
2
3
4
5
6
7
8
9
10
/**
 * Get "user-menu" links.
 */
function MYMODULE_get_menu_links() {
  return db_select('menu_links', 'm')
    ->fields('m')
    ->condition('m.menu_name', 'user-menu')
    ->execute()
    ->fetchAllAssoc('mlid');
}

Содержимое файла MYMODULE.js честно позаимствовано из файла block.js модуля ядра block с минимальными изменениями.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/**
 * Позволяет перемещать строки из одного региона в другой.
 */
(function($){
  Drupal.behaviors.CellsDrag = {
    attach: function (context, settings) {
      // Проверяем, что присутствует объект tableDrag.
      if (typeof Drupal.tableDrag == 'undefined' || typeof Drupal.tableDrag.cells == 'undefined') {
        return;
      }
 
      var table = $('table#cells');
      var tableDrag = Drupal.tableDrag.cells;
 
      // Обновление пустых регионов, когда строки изменили положение.
      tableDrag.row.prototype.onSwap = function (swappedRow) {
        checkEmptyRegions(table, this);
      };
 
      // Сообщение о необходимости сохранить совершенные изменения.
      Drupal.theme.tableDragChangedWarning = function () {
        return '<div class="messages warning">' + Drupal.theme('tableDragChangedMarker') +
          ' ' + Drupal.t('The changes to these blocks will not be saved until the <em>Save blocks</em> button is clicked.') +
          '</div>';
      };
 
      // Обновление строк, перемещенных в новые регионы.
      tableDrag.onDrop = function () {
        var dragObject = this;
 
        var regionRow = $(dragObject.rowObject.element).prevAll('tr.row-message').get(0);
        var regionName = regionRow.className.replace(/([^ ]+[ ]+)*row-([^ ]+)-message([ ]+[^ ]+)*/, '$2');
        var regionField = $('select.cell-region-select', dragObject.rowObject.element);
 
        if ($('option[value=' + regionName + ']', regionField).length == 0) {
          alert(Drupal.t('The block cannot be placed in this region.'));
          regionField.change();
        }
        else if ($(dragObject.rowObject.element).prev('tr').is('.row-message')) {
          var weightField = $('select.cell-weight', dragObject.rowObject.element);
          var oldRegionName = weightField[0].className.replace(/([^ ]+[ ]+)*cell-weight-([^ ]+)([ ]+[^ ]+)*/, '$2');
 
          if (!regionField.is('.cell-region-' + regionName)) {
            regionField.removeClass('cell-region-' + oldRegionName).addClass('cell-region-' + regionName);
            weightField.removeClass('cell-weight-' + oldRegionName).addClass('cell-weight-' + regionName);
            regionField.val(regionName);
          }
        }
      };
 
      // Обратчик выбора регионов из выпадющего списка.
      $('select.cell-region-select', context).once('cell-region-select', function () {
        $(this).change(function (event) {
          var row = $(this).closest('tr');
          var select = $(this);
          tableDrag.rowObject = new tableDrag.row(row);
          table.find('.row-' + select[0].value + '-message').nextUntil('.row-message').last().before(row);
          checkEmptyRegions(table, row);
          select.get(0).blur();
        });
      });
 
      var checkEmptyRegions = function (table, rowObject) {
        $('tr.row-message', table).each(function () {
          if ($(this).prev('tr').get(0) == rowObject.element) {
            if ((rowObject.method != 'keyboard' || rowObject.direction == 'down')) {
              rowObject.swap('after', this);
            }
          }
          // Регион пуст.
          if ($(this).next('tr').is(':not(.draggable)') || $(this).next('tr').length == 0) {
            $(this).removeClass('row-populated').addClass('row-empty');
          }
          // Регион заполнен.
          else if ($(this).is('.row-empty')) {
            $(this).removeClass('row-empty').addClass('row-populated');
          }
        });
      };
    }
  }
})(jQuery);

Файл MYMODULE.css содержит миниммум стилей.

1
2
3
4
5
6
7
8
.row-populated {
  display: none;
}
 
.row-title {
  font-weight: bold;
  text-transform: uppercase;
}

Теперь черед темирующей функции theme_blocks_table().

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/**
 * Returns HTML for a blocks_table table.
 */
function theme_blocks_table($vars) {
  $element = $vars['form'];
 
  // Шапка таблицы.
  $header = array(
    t('Title'),
    t('Path'),
    t('Menu name'),
    t('Region'),
    t('Weight'),
  );
 
  // Инициализируем массив опций.
  $options = array(
    'rows' => array(),
    'cells' => array(),
  );
 
  foreach (element_children($element['rows']) as $name) {
    $options['rows'][$name] = new stdClass();
    $options['rows'][$name]->name = drupal_render($element['rows'][$name]['name']);
 
    // Подключаем js draggable поведение.
    drupal_add_tabledrag('cells', 'match', 'sibling', 'cell-region-select', 'cell-region-' . $name, NULL, FALSE);
    drupal_add_tabledrag('cells', 'order', 'sibling', 'cell-weight', 'cell-weight-' . $name);
 
    if (!empty($element['cells'][$name])) {
      foreach (element_children($element['cells'][$name]) as $mlid) {
        $cell = &$element['cells'][$name][$mlid];
        $options['cells'][$name][$mlid] = new stdClass();
        $options['cells'][$name][$mlid]->link_title = drupal_render($cell['link_title']);
        $options['cells'][$name][$mlid]->link_path = drupal_render($cell['link_path']);
        $options['cells'][$name][$mlid]->menu_name = drupal_render($cell['menu_name']);
        $options['cells'][$name][$mlid]->region = drupal_render($cell['region']);
        $options['cells'][$name][$mlid]->weight = drupal_render($cell['weight']);
      }
    }
  }
 
  // Передаем обработанные данные в шаблон.
  return theme('blocks_table_template', array(
    'header' => $header,
    'rows'  => $options['rows'],
    'cells' => $options['cells'],
    'empty' => t('No data'),
  ));
}

Шаблон MYMODULE-blocks-table.tpl.php содержит следующий код.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<table id="cells" class="sticky-enabled">
  <thead>
  <tr>
    <?php foreach($header as $title): ?>
      <th><?php print $title; ?></th>
    <?php endforeach; ?>
  </tr>
  </thead>
 
  <tbody>
  <?php $i = 0; ?>
  <?php if (!empty($rows)): ?>
    <?php foreach ($rows as $menu_name => $row): ?>
      <tr class="row-title row-title-<?php print $menu_name?>">
        <td colspan="<?php print count($rows); ?>"><?php print $row->name; ?></td>
      </tr>
      <tr class="row-message row-<?php print $menu_name?>-message <?php print empty($cells[$menu_name]) ? 'row-empty' : 'row-populated'; ?>">
        <td colspan="<?php print count($rows); ?>"><em><?php print t('No symbols in this region'); ?></em></td>
      </tr>
 
      <?php if (!empty($cells[$menu_name])): ?>
        <?php foreach ($cells[$menu_name] as $sid => $data): ?>
          <tr class="draggable <?php print $i % 2 == 0 ? 'odd' : 'even'; ?>">
            <td class="cell"><?php print $data->link_title; ?></td>
            <td><?php print $data->link_path; ?></td>
            <td><?php print $data->menu_name; ?></td>
            <td><?php print $data->region; ?></td>
            <td><?php print $data->weight; ?></td>
          </tr>
          <?php $i++; ?>
        <?php endforeach; ?>
      <?php endif; ?>
 
    <?php endforeach; ?>
  <?php else: ?>
    <tr><td colspan="<?php print count($rows); ?>"><?php print $empty; ?></td></tr>
  <?php endif; ?>
  </tbody>
</table>

В результате, получаем вот такую табличку
blocks_table

Таблица с фильтром

Таблица с фильтром будет построена на примере простой таблицы, то есть, hook_theme() и theme_simple() будут точно такие же как в первом примере из статьи "Создаем разные виды Drupal HTML таблиц (часть 1)". Изменения коснутся функции выборки MYMODULE_get_all_nodes() и функции MYMODULE_tables_page().
Данные фильтра будем хранить в сессии. Если нужно сделать индивидуальные настройки фильтра для каждого пользователя - используйте cookie.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/**
 * Example tables page.
 */
function MYMODULE_tables_page($form, $form_state) {
  // Массив дефолтных значений для фильтра.
  $options = array('title' => '', 'type' => 'noselect');
  if (!empty($_SESSION['MYMODULE_filter'])) {
    $options = $_SESSION['MYMODULE_filter'];
  }
 
  // Подключаем фильтр.
  MYMODULE_table_filter($form, $form_state, $options);
 
  $form['simple_table'] = array(
    '#type'  => 'container',
    '#theme' => 'simple',
  );
 
  $nodes = MYMODULE_get_all_nodes($options);
  foreach ($nodes as $nid => $node) {
    $form['simple_table'][$nid]['title'] = array(
      '#markup' => $node->title,
    );
 
    $form['simple_table'][$nid]['type'] = array(
      '#markup' => $node->type,
    );
 
    $form['simple_table'][$nid]['created'] = array(
      '#markup' => date('d-m-Y H:i', $node->created),
    );
 
    $form['simple_table'][$nid]['link'] = array(
      '#type' => 'link',
      '#title' => t('Edit'),
      '#href' => $nid . '/edit',
    );
  }
 
  return $form;
}

Функция MYMODULE_get_all_nodes() принимает дополнительный аргумент $options - это массив сохраненных значений фильтра (будет участвовать в условиях выборки).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
 * Get nodes data.
 */
function MYMODULE_get_all_nodes($options) {
  $query = db_select('node', 'n');
  $query->fields('n', array('nid', 'type', 'title', 'created'));
  $query->condition('n.status', NODE_PUBLISHED);
  // Проверяем заполнен ли title.
  if (!empty($options['title'])) {
    $query->condition('n.title', db_like($options['title']) . '%', 'LIKE');
  }
  // Проверяем, что выбран тип ноды.
  if ($options['type'] != 'noselect') {
    $query->condition('n.type', $options['type']);
  }
 
  $result = $query->execute()->fetchAllAssoc('nid');
  return $result;
}

Фильтр представляет собой филдсет с текстовым полем для ввода заголовка ноды, селектом для выбора типа и двумя кнопками - "фильтр" и "сброс".

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/**
 * Filter.
 */
function MYMODULE_table_filter(&$form, &$form_state, $options) {
  // Разворачиваем или оставляем свернутым филдсет в зависимости от того,
  // есть значения в сессии или нет.
  $form['filter'] = array(
    '#type' => 'fieldset',
    '#title' => t('Filter'),
    '#collapsible' => TRUE,
    '#collapsed' => !empty($_SESSION['MYMODULE_filter']) ? FALSE : TRUE,
  );
 
  $form['filter']['title'] = array(
    '#type' => 'textfield',
    '#title' => t('Title'),
    '#default_value' => $options['title'],
  );
 
  $types = array('noselect' => t('No select'));
  $types += node_type_get_names();
  $form['filter']['type'] = array(
    '#type' => 'select',
    '#title' => t('Type'),
    '#options' => $types,
    '#default_value' => $options['type'],
  );
 
  $form['filter']['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Filter'),
    '#submit' => array('MYMODULE_filter_submit'),
  );
 
  $form['filter']['reset'] = array(
    '#type' => 'submit',
    '#value' => t('Reset'),
    '#submit' => array('MYMODULE_filter_reset'),
  );
 
  return $form;
}

В колбэке для сабмита фильтра добавляем значения в сессию.

1
2
3
4
5
6
7
8
9
10
/**
 * Submit filter callback.
 */
function MYMODULE_filter_submit($form, $form_state) {
  $values = $form_state['values'];
  $_SESSION['MYMODULE_filter'] = array(
    'title' => $values['title'],
    'type'  => $values['type'],
  );
}

Соответственно в reset колбэке - удаляем массив данных из сессии.

1
2
3
4
5
6
/**
 * Reset filter callback.
 */
function MYMODULE_filter_reset($form, $form_state) {
  unset($_SESSION['MYMODULE_filter']);
}

Таблица с фильтром (добавлены минимальные css стили для выравнивания блоков в фильтре).
table_with_filter

1 Комментарий