Взгляд на Views API

Готов перевод третьей статьи из цикла "20 API за 20 дней". В ней рассказывается о том, как встроить данные своего модуля во Views, рассматриваются некоторые хуки Views API. Статья не очень большая, всех секретов мироздания не раскрывает. Но базовая информация о предмете приведена, плюс указаны направления для дальнейшего изучения.

Оригинальная статья: View on Views API.
Автор: Мартин Грабовцин.
Переводили: graker, pereskokov.

Взгляд на Views API

Среди дополнительных модулей Друпала Views - самый широко используемый. Он предлагает возможность легко создавать страницы и блоки динамического содержимого, которые можно разрабатывать прямо через веб-интерфейс. Также он позволяет администраторам сайта прикреплять к представлениям фильтры, обеспечивающие для пользователя возможность без особых усилий прореживать данные.

А еще модуль Views обладает определенным API для программной работы с представлениями. Разработчики могут вызывать представления внутри модулей, модифицировать отображение представлений и даже программно создавать новые представления. Существует исчерпывающая документация и раздел для обсуждения в сообществе Друпала, посвященные различным способам прогрммной работы с представлениями. В данном выпуске цикла "20 API за 20 дней" мы рассмотрим несколько не очень известных возможностей API, которые наша команда находит полезными в разработке сайтов.

Хуки данных модуля Views

Если вы разрабатываете модули, то скорее всего неоднократно хотели, чтобы при установке вашего модуля в нем уже были какие-то представления Views. Также, возможно, вам хотелось, чтобы данные, генерируемые модулем, можно было отображать и в пользовательских представлениях. Views API предоставляет нам хуки данных, позволяющие этого добиться.

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

Хуки данных, доступные разработчикам в модуле Views 6.x:

  • hook_views_api - этот очень простой хук регистрирует модуль во Views API. Он возвращает некоторые данные о поддерживаемой модулем версии Views API.
  • hook_views_default_views - этот хук позволяет разработчикам модулей создавать собственные представления при инсталляции модулей. Это избавляет пользователей от необходимости самостоятельно создавать представления, а также обеспечивает наличие добротных интерфейсов в модуле.
  • hook_views_data - данный хук передает модулю Views информацию о таблицах, из которых Views может извлечь данные. Он определяет отношения между этими и другими таблицами в Друпале, чтобы модуль Views мог составить подходящие запросы.
  • hook_views_handlers - этот хук делает две простые вещи: во-первых, позволяет разработчикам контролировать, как будет отображаться при создании представления интерфейс каждого поля. Во-вторых, он управляет отображением поля в самом представлении. Но у него есть и более сложные функции.
  • hook_views_plugins - модуль Views можно расширять новыми плагинами. Плагины могут вводить во Views новые способы вывода представлений (отображения, стили, строки), контроля за доступом к представлениям и валидации аргументов.

Вместе эти функции, входящие в набор хуков данных, создают мощный API для связи с представлениями Views. С их помощью ваш модуль может передавать в представления Views данные, управлять способом их отображения и реализовывать сложную фильтрацию. Вот пример хуков данных в действии, взятый из модуля nodequeue:

/**
* hook_views_api - регистрирует модуль во Views API
* В данном примере Друпалу сообщается, что необходимо использовать API версии 2
*/

function nodequeue_views_api() {
  return array(
    'api' => 2,
    'path' => drupal_get_path('module', 'nodequeue') .'/includes/views',
  );
}

/**
* Реализация hook_views_data()
*
* hook_views_data рассказывает модулю Views, где искать данные. Для каждого нового поля представления
* здесь должна быть запись. В рамках примера функция сокращена ради экономии места.
*
*/

function nodequeue_views_data() {

  $data = array();

// ----------------------------------------------------------------
// Таблица nodequeue_nodes
  $data['nodequeue_nodes']['table']['group'] = t('Nodequeue');

// В данном примере показано, как поле связывается с представлениями. Фильтрующая и сортирующая
// функции привязываются к полю, что позволяет представлениям выполнять эти функции без необходимости
// писать дополнительный код.
  $data['nodequeue_nodes']['position'] = array(
    'title' => t('Position'),
    'help' => t('The position of the node within a queue.'),
    'field' => array(
      'handler' => 'views_handler_field_numeric',
      'click sortable' => TRUE,
    ),
    'sort' => array(
      'handler' => 'views_handler_sort',
    ),
    'filter' => array(
      'handler' => 'views_handler_filter_numeric',
    ),
    'argument' => array(
      'handler' => 'views_handler_argument_numeric',
    ),
  );

// обратите внимание, какие хэндлеры сортировки и фильтрации применяются к полям.
// В модуле Views есть собственные инструменты для работы с датами, числами, текстом и другими видами данных.
  $data['nodequeue_nodes']['timestamp'] = array(
    'title' => t('Added date'),
    'help' => t('The date the node was added to a queue.'),
    'field' => array(
      'handler' => 'views_handler_field_date',
      'click sortable' => TRUE,
    ),
    'sort' => array(
      'handler' => 'views_handler_sort_date',
    ),
    'filter' => array(
      'handler' => 'views_handler_filter_date',
    ),
  );

// в этой части функции содержится группа других полей, но предыдущих двух
// достаточно для примера привязки полей к представлениям
//...

  return $data;
}

/**
* Реализация hook_views_handlers()
*
* hook_views_handlers рассказывает модулю Views, как показывать поля в интерфейсе Views и
* как их отображеть в самом представлении. В директории модуля вы найдете файл, соответствующий каждому
* parent-элементу массивов.
*
*/

function nodequeue_views_handlers() {
  return array(
    'info' => array(
      'path' => drupal_get_path('module', 'nodequeue') .'/includes/views',
    ),
    'handlers' => array(
      'nodequeue_handler_argument_subqueue_qid' => array(
        'parent' => 'views_handler_filter_numeric',
      ),
      'nodequeue_handler_field_all_queues' => array(
        'parent' => 'views_handler_field_prerender_list',
      ),
      'nodequeue_handler_field_all_subqueues' => array(
        'parent' => 'nodequeue_handler_field_all_queues',
      ),
      'nodequeue_handler_field_links' => array(
        'parent' => 'views_handler_field_node_link',
      ),
      'nodequeue_handler_field_queue_tab' => array(
        'parent' => 'views_handler_field_node_link',
      ),
      'nodequeue_handler_filter_in_queue' => array(
        'parent' => 'views_handler_filter_boolean_operator',
      ),
      'nodequeue_handler_relationship_nodequeue' => array(
        'parent' => 'views_handler_relationship',
      ),
    ),
  );
}

Это достаточно простой пример работы с хуками данных в собственных модулях для их привязки к Views API. Ни в коем случае не считайте его исчерпывающим. Подробная документация по Views API доступна по адресу http://views.doc.logrus.com/.

Изменяйте представления при построении

Еще одна группа хуков Views API - изменяющие хуки. С помощью этих хуков вы можете менять поведение представления Views на разных фазах его построения. Изменения могут быть любыми, какие только возможны через интерфейс Views. Каждое представление - это объект PHP, состоящий из множества разных свойств. Изменение этих свойств может изменить и результирующее представление. Давайте посмотрим на список изменяющих хуков, приведенных в порядке их вызова модулем Views:

  • hook_views_pre_view - данный хук вызывается в самом начале обработки представления, прежде всего остального.
  • hook_views_pre_execute - хук вызывается прямо перед запуском процесса. Запрос уже полностью сформирован, но еще не обработан функцией db_rewrite_sql.
  • hook_views_pre_render - этот хук вызывается перед процессом генерации представления. Запрос уже был выполнен, фаза pre_render() для хендлеров уже прошла, так что все данные должны быть доступны.
  • hook_views_post_render - пост-обработка данных в сгенерированном представлении.

У изменяющих хуков много применений в разработке, они могут сделать представления более удобными без необходимости писать много кода. Данный пример демонстрирует, как прикрепить какой-нибудь текст перед представлением. Использование hook_views_pre_render позволяет нам убрать PHP-код из заголовка представления и отображать некоторые представления в более постоянной форме:

/**
* Прикрепляем описание термина к представлению с аргументом
*/

function MYMODULE_views_pre_render(&$view) {
  if ($view->name == 'view-i-want-to-change') {
    // у нас есть доступ к аргументам views
    $term = taxonomy_get_term($view->args[0]);
    if ($term) {
      $view->attachment_before = check_markup($term->description, FILTER_FORMAT_DEFAULT, FALSE);
    }
  }
}

Помимо управления отображением представлений, можно также модифицировать их фильтры и сортировки. Данный пример демонстрирует, как изменить порядок, в котором представление выводит данные.
function MYMODULE_views_pre_render(&$view) {
  if ($view->name == 'view-i-want-to-change') {
    // мы задали 3 сортировки в интерфейсе создания представления
    $available_sorts = array('node_title' => t('Title'), 'node_created' => t('Post Date'), 'user_name' => t('Author'));
    // определяем, какая сортировка доступна
    $current_sort = isset($_GET['sort']) && isset($available_sorts[$_GET['sort']) ? $_GET['sort'] : 'node_title';
    // теперь мы удалим все сортировки кроме используемой
    $sort = $view->display_handler->get_option('sort');
    foreach (array_keys($sort) as $sort_name) {
      if ($sort_name != $current_sort) {
        // вот мы удаляем неиспользуемые сортировки
        unset($sort[$sort_name]);
      }
    }
    $view->display_handler->sort_option('sort', $sort);

    // формируем ссылки для сортировок
    $links = array();
    foreach ($available_sorts as $type => $title) {
      $links[] = array(
        'title' => $title,
        'path' => $_GET['q'],
        'query' => array('sort' => $type)
      );
    }
    // прикрепляем ссылки к представлению
    $view->attachment_before = theme('links', $links);
  }
}

Следует отметить, что правильное изменение сортировки представления может причинить немало головной боли и потребовать решить кучу проблем. Для исследования объектов представлений не забывайте использовать модуль Devel и его прекрасную функцию dpm, они избавят вас от необходимости целыми днями чесать репу. Вот пример:
function MYMODULE_views_pre_render(&$view) {
  dpm($view);
}

Приводите код в порядок

В данной статье упоминалось много хуков; в целом нет никакой необходимости хранить весь код Views API в одном файле. По правде говоря, как правило, это плохая мысль - хранить все в одном месте - так как загрузка всего кода при каждой загрузке представления приведет к проблемам производительности. Но во Views API код может быть разделен на несколько файлов без особых трудностей; тогда для генерации представления будут загружаться только необходимые файлы.

Так какой хук куда класть? Некоторые размышления привели к общим принципам структурирования кода в модулях так, чтобы с ними было легче работать. Ниже набросана небольшая схема для демонстрации обычного шаблона реализации кода Views.

  • MYMODULE.module – основной файл модуля
    • hook_views_api
    • Все изменяющие хуки (hook_views_pre_view, hook_views_pre_execute, hook_views_pre_render, hook_views_post_render)
  • MYMODULE.views.inc – в данном файле содержатся хуки, которые передают данные и плагины представлениям
    • hook_views_data
    • hook_views_handlers
    • hook_views_plugins
  • MYMODULE.views_default.inc – содержит только один хук с длинными определениями представлений
    • hook_views_default_views
Другие важные функции, которые должен знать каждый разработчик

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

  • views_embed_view - используйте эту функцию, чтобы отобразить представление на web-странице. Очень просто.
  • views_get_view - функция возвращает весь гигантский объект представления целиком. Используйте ее, когда хотите изменить основные параметры, связанные с представлением, без необходимости лезть в изменяющие хуки. Есть определенные вещи, которые нельзя модифицировать через изменяющие хуки, поэтому иногда эта функция представляется более кратким путем к цели.
Дополнительные ресурсы

Данный обзор описывает лишь некоторые подходы к работе с представлениями - те, которыми мы часто пользуемся в компании Trellon. Ниже приводится список дополнительных ресурсов для подробного изучения Views API и изыскания своих собственных решений типовых задач. Убедитесь, что прочли все наши статьи про API - в них мы исследуем основные инструменты расширения функций Друпала.

wolfxxxl (гость)
Аватар пользователя wolfxxxl

Спасибо за перевод! Не смотря на то что с многим уже сталкивался, как бы мозаика информации обрела четкую структуру.

andypost (гость)
Аватар пользователя andypost

Замечу, что реализация хэндлеров обычно лежит в своем файле. Расположение этих файлов определяется info->path в хуках hook_views_handlers() и hook_views_plugins() и может быть отличным от path определенного в hook_view_api()

graker
Аватар пользователя graker

Дельное замечание. В своей статье про Views я хэндлеры как раз в отдельные файлы пихал: ['handlers']['имяхэндлера']['file'] = 'имяфайла'.

dan (гость)
Аватар пользователя dan

Спасибо за переводы!

Исправь ошибку плиз, в строке $term = taxonomy_get_term($view->arg[0]); надо использовать args

graker
Аватар пользователя graker

Спасибо, поправил.

orangeudav (гость)
Аватар пользователя orangeudav

Теоретически можно залезть грязными руками в sql-запрос прямо перед его выполнением без использования хуков

$view = view_get_view(...)
$view->build();
// меняем $view->query как хотим
$view->execute();
echo $view->render();

НО
1) у меня отвалился пейджер
2) работает через раз, хотя все кеширование вроде бы отключил

graker
Аватар пользователя graker

А если через хуки - работает?

orangeudav (гость)
Аватар пользователя orangeudav

наверняка заработает, в других ситуациях работало же.
но мне то нужно менять вьюху из темплейта (откуда я собственно ее и дергаю).

graker
Аватар пользователя graker

Видимо не так меняете что-то. Без конкретики и отладки большего я все равно сказать не смогу.

orangeudav (гость)
Аватар пользователя orangeudav

Попроовал через Views хуки - когда вызывается hook_views_pre_view никакого намека на query еще нет, а когда вызывается hook_views_pre_execute и query уже есть и селект полностью собран в $view->build_info['query']. Кстати после этого тут же вызывается hook_db_rewrite_sql, мне он не подошел так как позволяет добавить только join, where и distinct, а мне нужна была еще и сортировка.
Так что пришлось слелать по другому:

$view = views_get_view(...);
$view->pre_execute();
$view->build();
// тут меняем текст селекта как хотим $view->build_info['query']
// если в результате меняется количество элементов выборки,
// то меняем и $view->build_info['query_count'] чтобы правильно работал пейджер
$view->execute();
echo $view->render();
$view->post_execute();

В таком виде все работает отлично. Я, к слову, делал сортировку товаров по цене из сторонней таблицы так, чтобы сортировка управлялась переключателем над стандартным столбцом sell_price во вьюхе.

graker
Аватар пользователя graker

А, понятно. Я бы к этому вопросу подошел с позиции добавления к вьюхе дополнительного хэндлера сортировки. Это как бы более основательно, но делать дольше, т.к. надо модуль создавать и все такое.

Но для быстрого решения ваш метод очень хорошо годится.

Гость (гость)
Аватар пользователя Гость

Супер, что я нашел решение у вас! Вот только я не программист и не понимаю куда и как все это вставлять, и точный текст а то я не совсем понял что прописать надо. Буду очень благодарен, а то щас выглядит пейджер некрасиво, вот так http://master-dressirovka.ru/video/real_video?page=0%2C0%2C0%2C0%2C0%2C1

graker
Аватар пользователя graker

Что вставлять, что прописывать? Ничего не понял.

dan (гость)
Аватар пользователя dan

Это спамер, kill'em all!

graker
Аватар пользователя graker

dan пишет:
Это спамер, kill'em all!
Да непохоже. Вот, он тут что-то еще пишет: http://drupal.ru/node/50658, и ссылку сюда дает.

dan (гость)
Аватар пользователя dan

Ёклмн, это я их "призвал", походу...

graker
Аватар пользователя graker

No worries, спам подтер

Ch (Гость) (гость)
Аватар пользователя Ch (Гость)

Где можно API для views 3 найти?

P.S. head scratching – чесать репу
Прикольно :-)

graker
Аватар пользователя graker

Ch (Гость) пишет:
Где можно API для views 3 найти?

http://graker.ru/news/2011/08/18/views_3
То есть почти нигде, только у лулаботов чуть-чуть.

Отправить комментарий

CAPTCHA
Пройдите, пожалуйста, проверку. Она нужна, чтобы отличать людей от спам-роботов. А если не хотите проходить эту проверку регулярно — зарегистрируйтесь.
Image CAPTCHA
Введите цифры, изображенные на картинке (без пробелов).