Views и подсчет нод в терминах

Вопрос «Как вывести термины таксономии с числом нод» мелькает на форумах с завидной регулярностью, то есть вопрос этот — важный. Можно воспользоваться модулем (только Drupal 6), обходным маневром (через контекстные фильтры Views) или включить в том же модуле Views аггрегацию. На последнем способе в этой заметке мы и остановимся подробнее, так как первый — устарел, а второй — не всегда может подойти.

Основная проблема аггрегации Views в том, что она никак не учитывает материалы в дочерних терминах. Вот о том, как это победить, и пойдет речь.

Чтоб далеко не ходить за примером, возьмем разработанный нами каталог Decking-Mall. На главной странице как раз выводится каталог товаров с подсчетом числа позиций внутри категории. У этого каталога ряд особенностей. Во-первых, словарь каталога выводится не с корня (потому что для разных регионов нашей страны выводится свой каталог), поэтому зацепиться за верхний термин будет непросто. Во-вторых, выводятся не все дочерние термины, а только два уровня родитель-дети. И в третьих, подсчитывать число материалов нужно только для дочерних терминов, а для родителей — не надо. В общем, в силу этих особенностей проще было сделать вывод с помощью аггрегации Views. Однако, подсчет материалов в каталоге, тем не менее, происходит с учетом дочерних терминов. Сделать это нетрудно, но нужно написать код.

Допустим, наше представление каталога называется product_categories. Создадим модуль и добавим в него код с реализацией хука hook_views_pre_render().

    //хук hook_views_pre_render позволяет поработать с результатами
    //представления, когда они уже готовы, но их вывод еще не начался
    function mymodule_views_pre_render(&$view) {
      //убедимся, что это наше представление
      if ($view->name == 'product_categories') {
        $tids = array();
        foreach ($view->result as $position => $result) {
          //сохраним все id терминов, для которых
          //нужно уточнить число материалов
          $tids[$result->tid] = $position; 
        }

        //найдем id словаря
        //у меня он берется из поля taxonomy_term_data_taxonomy_term_hierarchy_1_vid, 
        //но в вашем представлении поле может (и будет) называться иначе
        $voc = $view->result[0]->taxonomy_term_data_taxonomy_term_hierarchy_1_vid;
        //загрузим все дерево терминов
        $tree = taxonomy_get_tree($voc);

        //пробежимся по дереву терминов и сохраним только те из них, у которых есть дочерние.
        $tid_chains = array();
        foreach ($tree as $index => $term) {
          $i = $index+1;
          $ctid = array();
          $ctid[] = $term->tid;
          //пробежимся по дереву вниз, начиная с текущего термина
          //(taxonomy_get_tree сортирует термины по родителям)
          //будем сохранять цепочки вида термин => array(его дети)
          $children = array();
          while ($i < count($tree)) {
            //заметим, что использование parents[0] не позволит
            //работать с несколькими родителями одного термина
            //но нам и не надо
            if (!in_array($tree[$i]->parents[0], $ctid)) {
              break; //термин не дочерний для цепочки
            }
            //сохраним id термина для следующей итерации
            //(вдруг для него дочерний есть?)
            $ctid[] = $tree[$i]->tid;
            //добавим текущий термин в массив дочерних
            $children[] = $tree[$i]->tid;
            $i ++;
          }
          //если дети нашлись, сохраним
          if (count($children)) {
            $tid_chains[$term->tid] = $children;
          }
        }

        //теперь осталось только подсчитать материалы
        //для всех детей каждого термина
        foreach ($tid_chains as $parent => $children) {
          //считаем
          $count = _mymodule_count_terms_nodes($children);
          //уточняем данные представления на полученную величину
          $view->result[$tids[$parent]]->node_taxonomy_index_nid += $count;
        }
      }
    }

    function _mymodule_count_terms_nodes($tids) {
      //EntityFieldQuery позволяет строить запросы к БД
      //для поиска сущностей по значению их свойств и полей.
      //Хорошая вещь! 
      //Гораздо лучше, чем ковыряться в таблицах поля напрямую.
      $query = new EntityFieldQuery();
      $count = $query
        ->entityCondition('entity_type', 'node')
        ->entityCondition('bundle', 'regional_product')
        ->propertyCondition('status', 1)
        //field_locality - это ссылка на термин таксономии
        //именно она связывает материал с каталогом
        ->fieldCondition('field_locality', 'tid', $tids, 'IN')
        ->count()
        ->execute();

      return $count;
    }

Собственно, вот и все. Нужно, однако, заметить, что если словарь с каталогом — достаточно ветвистый, то не стоит пренебрегать кэшированием Views. Впрочем, им вообще не стоит пренебрегать без веских причин.

Комментарии