Views API: Существует ли в Сети внятное объяснение, как правильно писать свои хендлеры для фильтров?

Главные вкладки

Аватар пользователя ingumsky@drupal.org ingumsky@drupal.org 29 сентября 2010 в 0:52

Коллеги!

Поделитесь ссылками на внятные статьи/руководства, откуда можно было бы узнать, как писать хендлеры для фильтров views. Я нашёл ряд материалов в сети, в которых разные люди рассказывают, как они добавляли хендлеры фильтров под свои задачи:
http://zugec.com/drupal/creating-custom-filters-in-views-2
http://thereisamoduleforthat.com/content/fun-views-and-location
http://www.examiner.com/open-source-in-national/drupal-how-to-create-vie...
http://www.dave.st/content/creating-datetime-view-handler-views2
Но мне этого не хватает, потому что авторы статей не заостряют внимания на том, почему они в одном случае создают в хендлере форму, а в другом — пишут объект, с помощью которого добавляют условия в запрос. Разумеется, я и документацию по API смотрел здесь: http://views2.logrus.com/doc/html/index.html Однако, мне остаётся непонятным, как действует «расширение» одного хендлера другим, как передавать значения фильтра запросу к базе (объект?), и как, в конечном итоге, добиться того, чтобы в разделе с фильтрами появился мой собственный, который будет обрабатывать введённые поля так, как я того хочу.

Пока я научился только сообщать views о созданных мной таблицах, чтобы с ними можно было работать (всё благодаря http://graker.ru/drupal/papers/views_integration), но это к хендлерам фильтров мало относится.

Если говорить о конкретной задаче, которую я пытаюсь решить, то это открытый фильтр по дате, ограничивающий количество результатов определённым временным отрезком (в моём случае — год), причём каждый такой отрезок начинается и заканчивается 1 июля. Пользователю с помощью селект-бокса остаётся выбрать один из таких отрезков. Я могу спокойно составить запрос к базе, который это сделает, но пока не могу понять, как мне превратить этот запрос в фильтр. Помогите, пожалуйста, материалами и/или советом.

Комментарии

Аватар пользователя neochief neochief 29 сентября 2010 в 1:09

Рискую остаться единственный откомментировавшем этот топик. В сети внятного объяснения я не встречал. Если приходилось писать что-то эдакое, просто брал похожий хендлер и смотрел как он работает, вплоть до разбора класса вьюхи. Это долго и нудно, но в конечном итоге начинаешь понимать все.

Аватар пользователя ingumsky@drupal.org ingumsky@drupal.org 29 сентября 2010 в 2:15

Спасибо. Этого я и боялся. С моими знаниями мне как раз не хватает просто копать вглубь — нужно какое-то теоретическое сопровождение, а оно, к сожалению, отсутствует. Может быть Вы в таком случае сможете ответить на пару вопросов: что следует понимать под «расширением» одного хендлера другим, почему в одних хендлерах средствами друпал создаётся форма для ввода значений, а в других присутствует только класс, через который запросу скармливается where, и как ограничить радиус действия моего хендлера, чтобы он не влиял на родительские хендлеры, но влиял только на мой фильтр?

Я понимаю, что вопросы, по сути, базового уровня, но именно ответов на них мне не хватает, чтобы начать «въезжать в тему».

Аватар пользователя penexe penexe 29 сентября 2010 в 10:44

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

"<a href="mailto:ingumsky@drupal.org">ingumsky@drupal.org</a>" wrote:
что следует понимать под «расширением» одного хендлера другим

банальноне наследование методов
"<a href="mailto:ingumsky@drupal.org">ingumsky@drupal.org</a>" wrote:
почему в одних хендлерах средствами друпал создаётся форма для ввода значений, а в других присутствует только класс, через который запросу скармливается where

Значит в одном случае форма у родителя не была задана, или приходится менять её, а в другом просто надо чуть изменить запрос родителя.
"<a href="mailto:ingumsky@drupal.org">ingumsky@drupal.org</a>" wrote:
ак ограничить радиус действия моего хендлера, чтобы он не влиял на родительские хендлеры, но влиял только на мой фильтр?

в в классах не силен, но сдается мне, из дочернего класса нельзя повлиять на родительский. только если переопределить родителя явно через hook_views_handlers()

по поводу вашей задачи:
наследуем от главного класса для фильтров views_handler_filter, поэтому создаем свою форму, задаем значения, запросы.

<?php
// наследуем от главного обработчика для фильтров
class watchdog_handler_filter_time extends views_handler_filter {
  function construct() {
    parent::construct();
    $this->value_title = t('Options');
    $this->value_options = NULL;
    $this->no_operator = TRUE; // отключаем блок условных операторов
  }

  function get_value_options() {
    if (isset($this->value_options)) {
      return;
    }
    // задаём возможные значения
    $this->value_options = array(
      '01.07.09|01.07.10' => '01.07.09 - 01.07.10',
      '01.07.10|01.07.11' => '01.07.10 - 01.07.11',
      '01.07.11|01.07.12' => '01.07.11 - 01.07.12',
      '01.07.12|01.07.13' => '01.07.12 - 01.07.13',
      '01.07.13|01.07.14' => '01.07.13 - 01.07.14',
      '01.09.10|30.09.10' => 'сентябрь 2010',
    );
  }

  // здесь мы определим нашу форму
  function value_form(&$form, &$form_state) {
    $form['value'] = array();

    $this->get_value_options(); //задаём значения
    $options = $this->value_options; // получаем их
    $default_value = (array) $this->value;

    // задаем дефолтное значение для раскрытого фильтра
    if (!empty($form_state['exposed'])) {
      $identifier = $this->options['expose']['identifier'];

      if (!empty($this->options['expose']['single'])) {

        if (!empty($this->options['expose']['optional']) && (empty($default_value))) {
          $default_value = 'All';
        }
        else if (empty($default_value)) {
          $keys = array_keys($options);
          $default_value = array_shift($keys);
        }
        else {
          $copy = $default_value;
          $default_value = array_shift($copy);
        }
      }
    }

    $form['value'] = array(
      '#type' => 'select',
      '#title' => $this->value_title,
      '#options' => $options,
      '#default_value' => $default_value,
      '#multiple' => TRUE,  // здесь мы говорим задаем множественный выбор,
      //потом views автоматически в зависимости от значения $this->options['expose']['single'] поменяем его
      '#size' => count($options) > 8 ? 8 : count($options),
    );

    if (!empty($form_state['exposed']) && !isset($form_state['input'][$identifier])) {
      $form_state['input'][$identifier] = $default_value;
    }
  }

  function query() {
    if (empty($this->value)) {
      return;
    }

    $this->ensure_my_table();
   
    if (is_array($this->value)) {
      foreach ($this->value as $value) {
        $values = array_map('_to_timestamp', explode('|', $value)); // подготавливаем значения
        // добавляем условия
        $this->query->add_where($this->options['group'], "$this->table_alias.$this->real_field BETWEEN %d AND %d", array_values($values));
        // Устатнавливаем оператор OR для группы условий нашего фильтра
        $this->query->set_where_group('OR', $this->options['group']);
      }
    }
  }
 
  function admin_summary() {
    if (!empty($this->options['exposed'])) {
      return t('exposed');
    }
  }
}

function _to_timestamp($value) {
  list($d, $m, $y) = explode('.', $value);
  return mktime(0, 0, 0, $m, $d ,$y);
}

Аватар пользователя penexe penexe 29 сентября 2010 в 11:11

если кому надо

function test_views_data() {
  $data['watchdog']['table']['group']  = t('Watchdog');

  $data['watchdog']['table']['base'] = array(
    'field' => 'wid',
    'title' => t('Watchdog'),
    'help' => t("Watchdog."),
    'weight' => -10,
  );

  // nid
  $data['watchdog']['wid'] = array(
    'title' => t('Wid'),
    'help' => t('The node ID of the watchdog.'),
    'field' => array(
      'handler' => 'views_handler_field',
      'click sortable' => TRUE,
    ),
    'filter' => array(
      'handler' => 'views_handler_filter_numeric',
    ),
  );
 
  $data['watchdog']['timestamp'] = array(
    'title' => t('Time'),
    'help' => t('Timestamp.'),
    'field' => array(
      'handler' => 'views_handler_field_date',
      'click sortable' => TRUE,
    ),
    'sort' => array(
      'handler' => 'views_handler_sort_date',
    ),
    'filter' => array(
      'handler' => 'views_handler_filter_date',
    ),
  );

  $data['watchdog']['timestamp2'] = array(
    'title' => t('Time 2'),
    'help' => t('Timestamp.'),
    'real field' => 'timestamp',
    'filter' => array(
      'handler' => 'watchdog_handler_filter_time',
    ),
  );

  return $data;
}

function test_views_handlers() {
  return array(
    'info' => array(
      'path' => drupal_get_path('module', 'test') .'/views',
    ),
    'handlers' => array(
      // filters
      'watchdog_handler_filter_time' => array(
        'parent' => 'views_handler_filter',
      ),
    ),
  );
}

Аватар пользователя Siegfrid@drupal.org Siegfrid@drupal.org 29 сентября 2010 в 11:49

Прежде чем браться за views api бы ло бы неплохо познакомиться с основами ООП, тогда будет понятно, как передаются параметры и почему где то создаются обьекты, а где то web формы

Аватар пользователя ingumsky@drupal.org ingumsky@drupal.org 29 сентября 2010 в 13:52

penexe
Ого! Большое Вам спасибо! Не рассчитывал на столь подробный ответ, да ещё и с демонстрацией того, что именно нужно сделать для решения моей конкретной задачи. Сейчас буду смотреть, как это работает.

vgoodvin
Спасибо. Действительно полезная статья для тех, кто только начинает погружаться во Views API.

Siegfrid@drupal.org
Наверное, Вы правы, но, сталкиваясь с чем-то для себя новым, всё время надеешься, что сейчас ещё чуть-чуть почитаешь, поковыряешь, и всё само в голове станет по местам.

Аватар пользователя Siegfrid@drupal.org Siegfrid@drupal.org 29 сентября 2010 в 22:29

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

Если научится грамотно исполтзлвать views в своих модулях, то можно будет достаточно легко решать огромный спектр задач, так что удачи!