Drupal → Сложные фильтры в Views, способ 2 — пишем свой filter handler

12.07.2011

В первом способе было рассмотрено создание сложной выборки с помощью контекстного фильтра. В этом способе покажу, как тоже самое можно сделать с помощью написания собственного filter handler-а.

Filter Handler — это класс, в котором прописана логика работы определённого типа фильтра. По умолчанию в Views 3 есть около 10 таких хэндлеров, это:

  • views_handler_filter — базовый хэндлер, от которого должны наследоваться все остальные.
  • views_handler_filter_numeric — позволяет фильтровать по числовым полям (больше, меньше, между и т.д.).
  • views_handler_filter_string — позволяет фильтровать по строковым полям (равно, содержит, и т.д.).
  • views_handler_filter_date — расширяет views_handler_filter_numeric и позволяет фильтровать по полям, в которых хранится Unix timestamp (позже, раньше, и т.д.).

и т.д.

Дефолтные хэндлеры хранятся в папке handlers модуля Views (рекомендую к изучению).

Процесс создания своего фильтра:

1. В файле .module реализуем хук hook_views_api(), в котором сообщаем Views, что мы желаем расширить его функционал:

/**
 * Implements of hook_views_api().
 */
function mymodule_views_api() {
  return array('api' => 3);
}

2. Создаём в папке модуля файл mymodule.views.inc.

3. Реализуем в нём хук hook_views_data(), в котором рассказываем Views о нашем кастомном фильтре:

/**
 * Implements of hook_views_data().
 */
function mymodule_views_data() {
  return array(
    'node' => array(
      'mycustomfilter' => array(
        'group' => t('Custom'),
        'title' => t('My custom filter'),
        'help' => t('My custom filter'),
        'filter' => array(
          'handler' => 'mymodule_handler_filter_mycustomfilter',
        ),
      )
    )
  );
}

node — это базовая таблица (подробнее тут), mycustomfilter — системное название нашего фильтра, mymodule_handler_filter_mycustomfilter — хэндлер (класс), в котором будет описана логика фильтра.

Хэндлеры принято именовать по шаблону — [module_name]_handler_[handler_type]_[handler_name].

Если сейчас включить модуль и попытаться добавить новый фильтр, то в списке появится и наш:

Форма добавления фильтра

4. В папке модуля создаём файл mymodule_handler_filter_mycustomfilter.inc.

5. Добавляем в него класс mymodule_handler_filter_mycustomfilter унаследованный от views_handler_filter и переопределяем в этом классе функцию query():

/**
 * My custom filter handler
 */
class mymodule_handler_filter_mycustomfilter extends views_handler_filter {
  function query() {
    $this->ensure_my_table();
    // ...
  }
}

Вместо троеточия, с помощью функций $this->query->add_where() и $this->query->add_where_expression() реализуем логику работы фильтра. Например если мы хотим сделать фильтр по нодам, дата изменения которых больше даты создания, то код будет:

$this->query->add_where_expression($this->options['group'], "{$this->table_alias}.changed > {$this->table_alias}.created");

Другие примеры ищите в файлах handlers/views_handler_filter_*.inc.

6. Добавляем в .info файл информацию о новом классе:

files[] = mymodule_handler_filter_mycustomfilter.inc

7. Сбрасываем кэш.

Всё. Исходники демо модуля.

Аналогичным способом можно добавить хэндлеры для полей, сортировки и контекстных фильтров.

Полезные ссылки:
Creating Custom Filters in Views
How To Create A Custom Filter Handler In Views

Написанное актуально для
Drupal 7, Views 3
Похожие записи

Комментарии

Когда ты только успеваешь :) Спасибо за познавательный материал.

Рено Гринлиф
18.08.2011, 16:03

Очень полезные сведения.

Webtoucher
21.09.2011, 08:26

Всё-таки я пока не совсем понял... А как фильтровать по значению какого-нибудь кастомного поля?? Назовём его, например, field_class.
Как будет выглядеть условие?? Да и вообще, я толком не понял, где указывать, для какого именно поля фильтрация...

Как будет выглядеть условие?

$this->query->add_where(0, 'field_class', '...', '=');
Да и вообще, я толком не понял

изучайте handlers/views_handler_filter_*.inc

Webtoucher
21.09.2011, 11:34

Фильтры я все уже перечитал. Собственно, всё написано, просто не получается нужное поле брать. Даже на простейший запрос вышеуказанным способом
$this->query->add_where(0, 'field_class', 'B', '=');
все равно ругается:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'field_class' in 'where clause'
Может как-то рассказывать views про это поле надо в mymodule_views_data()?? Или ещё что-то... Просто для седьмого друпала очень мало написано про Views API. В шестёрке поля по другому хранились. Тут ведь отдельная таблица для каждого поля. Он должен сам подхватывать значения без приклеивания этих таблиц JOINом??

Он должен сам подхватывать значения без приклеивания этих таблиц JOINом??

не должен

а как изменить отображение фильтра для числового поля: в случаи "between" вместо двух полей вывести список интервалов и соответственно корректно обработать их?

переопределите метод options_form. я сам этим не занимался, поэтому не помощник. смотрите исходники дефолтных фильтров.

Дима Мельник
03.02.2012, 17:19

Webtoucher, нужно сначала присоединить таблицу с нужным вам полем. Для вашего случая:

$join = new views_join();
$join->construct('таблица_с_полем', $this->table_alias, 'nid', 'entity_id');
$this->query->ensure_table('таблица_с_полем', $this->relationship, $join);
$this->query->add_where(0, 'field_class', '...', '=');
Василий А
12.05.2012, 01:12

Дима Мельник, спасибо!

А может кто знает, как сделать GROUP BY по этому полю
Пробую
$this->query->add_groupby('таблица_с_полем.поле');
не работает

Андрей
05.06.2012, 12:58

А как добавляются кастомные сортировки к вьюшкам? тоже описываются в mymodule_views_data ? интересует именно шаг два. Что должно поменяться в этом коде, для описания сортировки

function mymodule_views_data() {
  return array(
    'node' => array(
      'mycustomfilter' => array(
        'group' => t('Custom'),
        'title' => t('My custom filter'),
        'help' => t('My custom filter'),
        'filter' => array('handler' => 'mymodule_handler_filter_mycustomfilter'),
      )
    )
  );
}
Андрей
05.06.2012, 15:50

А что означает $this->options['group'] ?

'sort' => array('handler' => 'mymodule_handler_sort_mycustomsort'),

А как можно удалить условие из результирующего sql-запроса ?

olezhkafp
10.04.2013, 20:21

Здравствуйте. Вроде бы все сделал по вашей статье, но когда применяю фильтр выводится ошибка базы данных, думаю вся причина в том, что я не правильно делаю запрос, или оно не знает к какой таблице я хочу подключится, или вообще не пойму в чем причина. Помогите пожалуйста. в коде хендлера я пишу

function query() {
    $this->ensure_my_table();
    global $user;
	$current_user_uid=$user->uid;
	$this->query->add_where(...)

и вот дальше тупик! вот в этом и проблема, что я не знаю как этот запрос составить, в папке хендлеров пересмотрел все, но ничего не понял... есть таблица og_membership в ней поле etid нужно сделать 2 запроса базе данных. этот код работает

global $user;
$current_user_uid=$user->uid;

$current_user_group=db_select('og_membership', 'ogm')
->fields('ogm', array('gid', 'etid'))
->condition('ogm.etid', $current_user_uid)
->execute()->fetchField();
$myres=db_select('og_membership', 'ogm')
->fields('ogm')
->condition('ogm.gid', $current_user_group)
->execute()->fetchAll();

то есть, нужно чтобы фильтр смотрел к какой группе принадлежит текущий пользователь и выводил только тех пользователей, с которыми он в одной группе

так понимаю вам нужно приджойнить таблицу? ищите примеры использования $this->query->add_table()

что-то нихрена не понял куда какой файл класть. Хотел сделать выборку 5 ближайших событий по дате указанной в типе материала.

Робот
29.11.2013, 13:39

В общем, в дополнение к предыдущей статье, если вы хотите добавить фильтр не к новой, а к существующей entity, нужно делать так:

/**
 * Implements of hook_views_data_alter().
 */
function ..._views_data_alter(&$data) {
  $data['existing_table_name']['mycustomfilter'] = array(
    'group' => t('Custom'),
    'title' => t('My custom filter'),
    'help' => t('My custom filter'),
    'filter' => array('handler' => 'My handler name'),
  );
}

hook_views_data в этом случае не нужен.

Подскажите пожалуйста, как написать модуль для зависимости фильтров.

Гость
16.09.2014, 05:03

Notice: Undefined index: mycustomfilter в функции views_handler_filter->accept_exposed_input() как исправить?

Гость
12.11.2014, 15:39

Спасибо за статью, получилось сделать сложный фильтр на основе views_handler_filter_numeric.inc
Теперь стоит задача сделать фильтр с выбором вариантов, пытаюсь сделать по аналогии с views_handler_filter_node_type.inc:

class mymodule_handler_filter_mycustomfilter extends views_handler_filter_in_operator {
  function get_value_options() {
    if (!isset($this->value_options)) {
      $options = array('ключ1' => 'значение1', 'ключ2' => 'значение2', 'ключ3' => 'значение3');
      $this->value_options = $options;
    }
  }
}

Фильтр создается, все замечательно.
Подскажите пожалуйста, где теперь прописывать сами условия фильтрации?

Гость
12.11.2014, 16:20

Все, сама разобралась, также в function op_simple()

Максим
15.08.2015, 03:06

Подскажите пожалуйста где мне взять название "таблицы_с_полем" которая применяется в этом коде... про который упоминалось выше... разбираю эту тему и немного запутался что куда

$join = new views_join();
$join->construct('таблица_с_полем', $this->table_alias, 'nid', 'entity_id');
$this->query->ensure_table('таблица_с_полем', $this->relationship, $join);
$this->query->add_where(0, 'field_class', '...', '=');
Евгений
26.08.2015, 14:36

Статья очень важная и нужная. Полагаю, решение моей задачи где-то рядом. Может быть подскажете?
Есть два критерия фильтрации, у обоих стоит отметка «помнить последнее значение». Задаю значерия в URL (?a=1&b=2), но Drupal их помнит лишь до тех пор, пока одно из значений не будет изменено (например, если задам в адресе ?b=3). Где / что нужно переопределить, чтобы менялось только то значение, которое задано, а незаданные значения были взяты с «прошлого раза»?

Гость
10.02.2017, 12:38

А если мне например нужно через views вывести словарь в котором 200 терминов, и каждый термин мне нужно отфильтровать на наличие контента и к примеру я уже сделал хендлер который это делает. В этом случае views сделает 200 запросов в базу данных?

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