Модуль Views — API. Основы

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

    Чтобы было понятно, далее всё на живом примере:

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

    Задача: Отобразить в блоке количество новых сообщений и количество всех сообщений в папке «Входящие» текущего пользователя.

    Решение: Написать модуль, который добавил бы в конструктор Views необходимые значения.

    К сожалению на просторах интернета очень мало информации по этому поводу, а мануал по Views API довольно сложен и непонятен.

    Итак, приступим.
    1. Создаем новый модуль.
      Как положено, создаем директорию, называем её именем нашего будущего модуля. Я назвал его privatemsg_extraviews.
      В директории создаем файлы privatemsg_extraviews.info, privatemsg_extraviews.module, privatemsg_extraviews.views.inc. Далее нам понадобится создать еще 2 файла, но об этом позже.
    2. privatemsg_extraviews.info
      Этот файл содержит информацию о нашем модуле. Нужен, чтобы друпал опознал модуль.
      name = privatemsg_extraviews
      description = Добавляет поддержку views в Private Messages
      core = 6.x
      package = Mail
      version = "6.x-1.1"


      * This source code was highlighted with Source Code Highlighter.

      Этого достаточно, чтобы друпал опознал наш модуль.
    3. privatemsg_extraviews.module
      Основной файл модуля. В нем пишется вся структура модуля, все хуки, и вообще всё-всё.
      Нам нужно лишь указать, что модуль работает с Views.
      <?php

      /**
      * Implementation of hook_views_api().
      */
      function privatemsg_extraviews_views_api() {
       return array(
        'api' => 2, // Указываем версию API
        'path' => drupal_get_path('module', 'privatemsg_extraviews'), // Указываем откуда брать файл для Views
       );
      }


      * This source code was highlighted with Source Code Highlighter.

    4. privatemsg_extraviews.views.inc
      Самое интересное. В этом файле мы и пишем всю систему работы с Views. Модуль его сам найдет, т.к. мы указали директорию в хуке выше.
      Основной хук, который нам потребуется, это hook_views_data(). В нем мы определяем, какую таблицу БД мы добавляем, и какую информацию возвращаем.
      <?php
      function privatemsg_extraviews_views_data() {
        // Определяем новую группу во Views
        $data['privatemsg']['table']['group'] = t('Private Messages');
        // Указываем таблицы
        $data['privatemsg']['table']['join'] = array(
          // users - значит наши значения будут доступны только при выборке пользователей
          'users' => array(
            // Указываем ключи
            'left_field' => 'uid',
            'field' => 'uid',
          ),
        );
        // count - столбец таблицы, по которому будет выборка
        $data['privatemsg']['count'] = array(
          'title' => t('Количество входящих сообщений'),
          'help' => t('Количество сообщений в папке "Входящие"'),
          // Указываем возвращаемое поле
          'field' => array(
            // Обработчик для этого поля
            'handler' => 'privatemsg_extraviews_handler_field_count',
            // Доступна сортировка по этому полю
            'click sortable' => TRUE,
          ),
        );
        // Аналогично
        $data['privatemsg']['count_new'] = array(
          'title' => t('Количество новых сообщений'),
          'help' => t('Количество новых сообщений в папке "Входящие"'),
          'field' => array(
            'handler' => 'privatemsg_extraviews_handler_field_count_new',
            'click sortable' => TRUE,
          ),
        );
        return $data;
      }


      * This source code was highlighted with Source Code Highlighter.


      Далее нам потребуется hook_views_handlers() для инициализации обработчиков полей.
      function privatemsg_extraviews_views_handlers() {
        return array(
          // Обработчики
          'handlers' => array(
            'privatemsg_extraviews_handler_field_count' => array(
              // Указываем, что возвращаем числовое значение
              'parent' => 'views_handler_field_numeric',
              // И путь к файлу обработчика
              'path' => drupal_get_path('module', 'privatemsg_extraviews'),
            ),
            // Аналогично
            'privatemsg_extraviews_handler_field_count_new' => array(
              'parent' => 'views_handler_field_numeric',
              'path' => drupal_get_path('module', 'privatemsg_extraviews'),
            ),
          ),
        );
      }


      * This source code was highlighted with Source Code Highlighter.


      Как я уже и говорил выше, нам понадобится создать еще 2 файла — это как раз обработчики полей. Создадим в директории модуля файлы privatemsg_extraviews_handler_field_count.inc и privatemsg_extraviews_handler_field_count_new.inc.
    5. privatemsg_extraviews_handler_field_count.inc
      Обработчик для поля «Количество сообщений».
      <?php
      // Определяем обработчик, наследуем от стандартного класса "числовое значение"
      class privatemsg_extraviews_handler_field_count extends views_handler_field_numeric {
        // Запрос к БД
        function query() {
          // Определяем таблицу
          $table = $this->query->ensure_table('pm_index');
          // Запрашиваем количество сообщений
          $sql = "SELECT COUNT(DISTINCT thread_id) FROM {pm_index} p WHERE p.deleted = 0 AND p.uid = users.uid";
          // Указываем название для возвращаемого поля
          $this->query->add_field('', "($sql)", 'count');
          $this->field_alias = 'count';
        }
        // Возвращаем полученное значение
        function render($values) {
          $txt = $values->count;
          if ($txt) {
            return $txt;
          }
          else {
            return parent::render($values);
          }
        }
      }

      * This source code was highlighted with Source Code Highlighter.

    6. privatemsg_extraviews_handler_field_count_new.inc
      Обработчик для поля «Количество новых сообщений». (аналогично)
      <?php

      class privatemsg_extraviews_handler_field_count_new extends views_handler_field_numeric {
        function query() {
          $table = $this->query->ensure_table('pm_index');
          $sql = "SELECT COUNT(DISTINCT thread_id) FROM {pm_index} p WHERE p.deleted = 0 AND p.is_new = 1 AND p.uid = users.uid";
          $this->query->add_field('', "($sql)", 'count_new');
          $this->field_alias = 'count_new';
        }

        function render($values) {
          $txt = $values->count_new;
          if ($txt) {
            return $txt;
          }
          else {
            return parent::render($values);
          }
        }
      }


      * This source code was highlighted with Source Code Highlighter.



    После всего проделанного Views сформирует запрос типа
    SELECT users.uid AS uid,
      (SELECT COUNT(DISTINCT thread_id) FROM pm_index p WHERE p.deleted = 0 AND p.uid = users.uid) AS count,
      (SELECT COUNT(DISTINCT thread_id) FROM pm_index p WHERE p.deleted = 0 AND p.is_new = 1 AND p.uid = users.uid) AS count_new
    FROM users users
    WHERE users.uid = 1


    * This source code was highlighted with Source Code Highlighter.

    и вернет необходимые значения.

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

    UPD: Перенес в блог «Drupal». Спасибо за карму.
    Поделиться публикацией
    Похожие публикации
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 29
    • +2
      спасибо, интересно. и про Views я лично не отказался бы почитать :)
      • +4
        Почитаете. Не думаю, что это последний мой пост про Views.
      • 0
        Отличная статья. Всё кратко и доходчиво. Теперь осталось применить на практике, но придется подождать случая как подвернется такая задача.

        Если у вас есть такая возможность, то напишите статью о том, как с помощью Views API создавать нужные ссылки для представлений.
        • 0
          Не совсем понял, что Вы имели ввиду под «ссылки для представлений»?
        • 0
          Отлично! Огромный плюс за комментарии в функциях.
          • +2
            Спасибо, за отличный материал. Приятно читать про Drupal на Хабре, с друпалом только и работаю последний год.
            • 0
              Более подробно про Views было бы интересно, особенно в связке с CCK
              • 0
                Это больше относится к CCK нежели к Views. Что конкретно Вас интересует? Вроде и так все CCK поля доступны во Views.
                Постараюсь об этом в будущем также написать.
                • 0
                  Ну, если честно, во Views я толком не разобрался, то есть простейшие примеры со страницами или заметками понимаю как работают, новый тип материалов в CCK создать могу, а вот вывести их в нужном мне виде так и не осилил. В итоге просто пишу модули реализующие нужные мне типы материалов и их представление, хотя понимаю, что по идее это можно сделать в админке, используя CCK+Views.

                  То есть в принципе хотелось бы «более подробно о Views, там еще много интересного: как создать свои настройки для полей, как создавать поля, доступные в фильтрах и сортировках, свои аргументы и связи, и многое другое.» просто на примерах с CCK :)
                  • 0
                    Ок. В следующем посте о Views прихвачу CCK.
                    • 0
                      Заранее спасибо :)
              • 0
                <?php
                // для начала создадим sql функцию
                function privatemsg_sql_getcount(&$fragments, $uid) {
                  // указываем таблицу
                  $fragments['primary_table'] = '{pm_message} pm';
                  
                  // добавляем поле
                  $fragments['select'][] = 'count(pm.mid)';
                  
                  // добавляем условия
                  $fragments['where'][] = 'pm.account = %d';
                  $fragments['query_args'][] = $uid;
                }
                
                // Теперь мы можем использовать наш запрос
                $query = _privatemsg_assemble_query('getcount', $user->uid);
                $result = db_result($query['query']);
                ?>
                
                // Для получения количества новых сообщений залогиненного юзера
                $new_count = privatemsg_unread_count();
                


                Все это можно сделать в препроцессе конкретного блока. Мне кажется, что так намного проще.
                Из Вашего поста я не понял, зачем использовать Views?
                Хотя, подход и интересный, но слишком сложный.

                Можно приватно посмотреть live версию?
                • –2
                  Все это можно сделать в препроцессе конкретного блока. Мне кажется, что так намного проще.

                  Проще не есть правильнее.

                  Во-первых: Если делать препроцесс конкретного блока, мы автоматически привязываемся к данному конкретному блоку на данном конкретном сайте. Мне же нужно было универсальное решение, которое можно без труда вставлять на все сайты + удобство добавления информации прямо из конструктора Views.
                  Во-вторых:
                  $result = db_result($query['query']);

                  privatemsg_unread_count();

                  это +2 отдельных запроса к бд минимум. При использовании Views, вся выборка осуществляется одним запросом (с подзапросами конечно, но всё-же), так что тут Вы неправы.

                  • 0
                    Извините, но, во-первых, у Вас 3 запроса в одном, что само по себе уже «тяжко», а во-вторых, есть разница, прикручивать целый модуль или одну функцию в препроцесс темплейта.
                    Функция в темплейте гораздо гибче и понятнее, нежели препроцессы и хэндлеры Views.
                    imho
                    • 0
                      Не путайте вложенные запросы и отдельные запросы из php. Мне кажется разница очевидна.

                      И вообще, Вы название топика читали? Вроде как я писал о том, как работать с Views, а не о конкретной задаче, которую я привел исключительно для примера.
                • 0
                  Не путайте вложенные запросы и отдельные запросы из php. Мне кажется разница очевидна.
                  Совершенно верно. Разница очевидна ;)
                  Вы не забывайте, что в Вашем случае, Вы заставляете работать еще кучу сторонних функций модуля Views ;)
                  • 0
                    Вообщем спор считаю состоявшимся. Вам удобнее делать иначе — дело ваше. Пост был не о том, и мы ушли от темы.
                    • 0
                      Дык, я пытаюсь понять, о чем пост с таким заголовком. О модуле Views? Нет.
                      О Вашем умении использовать хендлеры? Может быть.
                      Но, не кажется ли Вам, что начиная серию постов о модуле Views, Вы начали с конца?
                      • 0
                        Мне как раз кажется, что я начал с начала. На примере доступно изложил как добавлять свои данные в конструктор Views. Далее я планирую написать как добавлять к своим полям настройки, как работать с полями CCK, как добавлять фильтры, сортировки и т.п. и это всё будет базироваться как раз на простых вещах, описанных в этом посте. Вы вероятно с друпалом мало работали.
                        • 0
                          Очень спорное начало.
                          Да, и с друпалом я, действительно, очень мало работаю ;)
                          • 0
                            может быть начать все же стоит с того как устроен views внутри, почему он такой бесстыдно-объекто-ориентированный в отличие от ядра друпала. какие хуки он поддерживает, как добавить свой тип данных… ну вобщем действительно по порядку… уж если вы смогли осилить оф.документацию, думаю изложить все это в простой форме будет несложно?
                    • –1
                      ждём продолжения
                      • –1
                        Алилуйя! пост про друпал, но не про то какой скачать модуль, и где найти какую галочку в админке! У views реально все плохо как-то по документации, вообще не нравится она мне… а сам код посложнее чем у ядра для понимания. По поводу views vs блок — года два назад я отдавал предпочтение кастомному куску кода, сейчас все чаще предпочитаю views.
                        • 0
                          Спасибо за статью! Буду ждать продолжения :)
                          Views очень функциональны, но очень громоздки — всегда интересно почитать чьи-то решения по работе с ними.
                          • 0
                            проще и быстрее ручками написать 1 select, чем мастерить такое)
                            • 0
                              Проще — да. Быстрее — да. Познаете ли основы вьюса — нет.
                            • 0
                              функция t() неправильно используется, ей должен передаваться текст на английском

                            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.