Во вступлении к статье о фильтрации контента по материалам, категориям и компонентам я упомянул, что для избирательной привязки модуля последних комментариев требуется создавать некоторое количество его копий. На сайте, достаточно разветвленном по структуре и тематике, а особенно - в случае многоязычности, это количество может исчисляться десятками. Каждая копия имеет свои настройки, устанавливаемые в менеджере модулей. Если нужно изменить какие-либо параметры во всех или даже нескольких копиях модуля, предстоит кропотливая и нудная работа в админке сайта - открывать страницу каждой копии и вручную вносить изменения. Можно, конечно, объединять комментарии к нескольким смежным темам в один общий модуль, но не лучше ли найти гибкое решение, позволяющее и рыбку съесть - четко отфильтровать комменты к каждой теме или даже подтеме, и манту не замочить - свести количество копий модуля к минимуму, а то и к одной-единственной. Я, как мне кажется, таковое решение нашел, и помогло мне в этом знание структуры внутренних ссылок страниц приложений, формируемых движком Джумлы.

Как известно, модули Joomla привязываются к пунктам меню. В случае mod_jcomments_latest введена также привязка к компонентам. Есть еще и дополнительная фильтрация по категориям, но лишь для случая, когда в списке компонентов выбран ТОЛЬКО com_content. Соответственно, нужны отдельные копии модуля для каждой категории основного компонента Joomla, а также для каждого дополнительного приложения. В пределах копии можно объединять категории com_content или несколько компонентов. Нет возможности объединить, например, комментарии к категориям и комментарии к приложениям, отличным от com_content, в один блок. Мой метод позволяет в пределах блока фильтровать комментарии к категориям и отдельным материалам приложения com_content и одновременно - к другим компонентам и их структурным элементам. Подробно и с примерами объясню принцип такой гибкой фильтрации.

Кастомизация кода модуля mod_jcomments_latest

Изменения требуется внести в код класса помощника данного модуля, который стандартно расположен в файле [корень_сайта]\modules\mod_jcomments_latest\helper.php. Рассматривается билд 3.0.4 - новейший на время написания статьи. (Кстати, на русскоязычной части сайта производителя пакета JComments на странице закачки модулей - http://www.joomlatune.ru/jcomments-modules.html - почему-то последний билд - 3.0.3, тогда как на англоязычной - http://www.joomlatune.com/jcomments-modules.html - 3.0.4. Имейте это в виду и не удивляйтесь.)

Все кастомизации вносятся в код ключевого и самого первого метода класса modJCommentsLatestHelper. Метод этот - getList. Вы можете скачать архив, содержащий две версии файла помощника - оригинальную и мною отредактированную, - и сравнить содержимое каким-нибудь инструментом (например, WinMerge):

Original & Customised helper.php of mod_jcomments_latest
Дата 2016-06-23 Система  Windows Размер файла 7.5 KB Закачек 63 Скачать

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

Измененный код функции getList

public static function getList(&$params) {
/*
  cut 1
*/
 
//Customization #1 ==BEGIN== (lines 27 - 36 in original file)
  switch ($params->get('ordering', '')) {
    case 'vote':
      $orderBy = '(isgood-ispoor) DESC';
      break;
 
    case 'date':
    default:
      $orderBy = 'date DESC';
      break;
  }
//Customization #1 --END--
 
/*
  cut 2
*/
 
//Customization #2 ==BEGIN== (lines 100 - 134 in original file)
  $categories = '';
  $components = array();
  getCommentsFilter($categories, $components);
 
  $query = '';
  $union = false;
  for ($i = 0; $i <= 1; $i++) { //1st for com_content, 2nd for other components
    $joins = array();
    $where_i = $where; //resets $where_i for each iteration
    if ($i == 0 && !empty($categories)) { //1st iteration
      $joins[] = 'JOIN #__content AS cc ON cc.id = o.object_id';
      if ($categories != 'all')
        $joins[] = 'LEFT JOIN #__categories AS ct ON ct.id = cc.catid';
 
      $where_i[] = "(cc.publish_up = '0000-00-00 00:00:00' OR cc.publish_up <= '$now')";
      $where_i[] = "(cc.publish_down = '0000-00-00 00:00:00' OR cc.publish_down >= '$now')";
      $where_i[] = "(c.object_group = " . $db->Quote('com_content') . ($categories != 'all' ? " AND cc.catid IN (" . $categories . ")" : "") . ")";
    }
    elseif ($i == 1 && !empty($components)) { //2nd iteration
      $where_cmp = array();
      for ($j = 0, $nj = sizeof($components); $j < $nj; $j++)
        $where_cmp[] = '(c.object_group LIKE ' . $db->Quote($components[$j] . '%') . ')';
      $where_i[] = '(' . implode(' OR ', $where_cmp) . ')';
    }
    else
      continue;
 
    $union = $i == 1 && !empty($query); //2nd iteration, 1st query filtering by categories was built
    if ($union)
      $query = '(' . $query . ') UNION (';
 
    $query .= "SELECT c.id, c.userid, c.comment, c.title, c.name, c.username, c.email, c.date, c.object_id, c.object_group, '' as avatar" . ", o.title AS object_title, o.link AS object_link, o.access AS object_access, o.userid AS object_owner" . " FROM #__jcomments AS c" . " JOIN #__jcomments_objects AS o ON c.object_id = o.object_id AND c.object_group = o.object_group AND c.lang = o.lang" . (count($joins) ? ' ' . implode(' ', $joins) : '') . (count($where_i) ? ' WHERE ' . implode(' AND ', $where_i) : '');
  }
 
  $list = array();
  if (!empty($query)) {
    $query .= ($union ? ")" : "") . " ORDER BY " . $orderBy; //adds common ordering for both queries in union (if any) of just for one quey
    $db->setQuery($query, 0, $params->get('count'));
    $list = $db->loadObjectList();
 
    if (!is_array($list)) {
      $list = array();
    }
  }
//Customization #2 --END--
 
/*
  cut 3
*/
}

end faq

Изменение 1:

Единая фильтрация вывода модуля mod_jcomments_latest - кастомизация 1

Как видим, здесь просто убраны все вхождения префикса алиаса таблицы для предложения сортировки ORDER BY sql-запроса (query). Зачем - будет ясно далее.

Изменение 2 - глобальное:

Единая фильтрация вывода модуля mod_jcomments_latest - кастомизация 2

Не буду подробно разбирать код строка за строкой; кому интересно - может разобраться сам, в чем помогут комментарии внутри кода. Я же изложу идею и реализацию.

В коде формируется запрос к таблицам комментариев, долженствующий выдавать список комментариев по условиям привязки. Условия - строковая переменная $categories и массив $components, заполняемые в функции getCommentsFilter, о которой ниже. В оригинале результат фильтрует альтернативно либо комменты к статьям и категориям компонента com_content, либо ко всем страницам отличных от него приложений. В моем варианте эти два результата могут быть скомбинированы в единый общий, а до комплекту - для ВСЕХ, в том числе сторонних, приложений становится возможной привязка к отдельным страницам этих компонентов по более избирательному фильтру.

Для этого реализованы два прохода (итерации): первая - для com_content, вторая - для прочих компонентов. Результаты запросов затем объединены с помощью оператора UNION, после чего уже совокупный результат сортируется через ORDER BY. И вот тут и становится ясен смысл первой кастомизации: при наличии псевдонимов (алиасов) таблиц MySQL выдает ошибку, поскольку они определены только внутри каждого из объединяемых запросов и недоступны для объединенного запроса.

Также предусмотрен вариант отсутствия в условии привязки либо по com_content, либо по прочим компонентам. В этом случае соответствующая итерация пропускается и объединение не производится.

Функция getCommentsFilter

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

  • $cf_option - имя компонента (с префиксом com_);
  • $cf_view - вид (представление) конкретной страницы компонента;
  • $cf_art_cat_id - идентификатор материала или категории в зависимости от текущего представления (см. здесь);
  • $cf_cat_id - идентификатор категории, в которую входит выводимый материал (в случае, когда cf_view==article);
  • $cf_lang - язык страницы (для мультиязычного сайта).

Как описано здесь, код функции можно разместить в одной из существующих библиотек, лучше всего - в файле functions.php активного шаблона. Код инициализации глобальных переменных лучше всего поместить в файл индекса шаблона templates\[active_template]\index.php ниже строки подключения библиотеки. Начало кода в моем файле index.php выглядит следующим образом:

defined('_JEXEC') or die;
 
require_once dirname(__FILE__) . DIRECTORY_SEPARATOR . 'functions.php';
 
$app = JFactory::getApplication();
$jinput = $app->input;
 
global $cf_option, $cf_view, $cf_art_cat_id, $cf_cat_id, $cf_lang;
 
$cf_option   = $jinput->getCmd('option');
$cf_view   = $jinput->getCmd('view');
 
$temp   = explode(':', $jinput->getCmd('id'));
$cf_art_cat_id   = $temp[0] + 0;
 
$temp   = explode(':', $jinput->getCmd('catid'));
$cf_cat_id = $temp[0] + 0;
 
$cf_lang = substr(JFactory::getLanguage()->getTag(),0,2);

Определение функции getCommentsFilter имеет следующий вид:

function getCommentsFilter(&$categories, &$components)

Аргументами функции являются две вышеупомянутые переменные - строковая $categories и массив $components, передаваемые из кастомизированного нами метода getList класса помощника модуля mod_jcomments_latest по ссылке, о чем свидетельствуют знаки амперсанд (&) перед их именами. В функции они заполняются значениями: первый - списком идентификаторов категорий, разделенных запятой, второй - списком имен компонентов; затем передаются обратно в метод getList и используются как условия выборки в предложении WHERE соответствующего sql-запроса.

Ниже под катом - код функции getCommentsFilter, используемый на момент написания статьи на этом сайте:

Код функции getCommentsFilter

function getCommentsFilter(&$categories, &$components) {
  global $cf_option, $cf_view, $cf_art_cat_id, $cf_cat_id, $cf_lang;
 
  if ($cf_lang == 'ru') {
    $hebro_cats = array(69, 71, 72, 73, 75, 76, 77, 83, 84, 85);
    $reg_cats = array(25, 26, 27, 48, 86);
    $tech_cats_str = '26,27';
 
    if ($cf_option == 'com_content') {
      if ($cf_view == 'article') {
        if (in_array($cf_art_cat_id, array(783, 307))) // Glavnaya, obratnaya-svyaz
          $categories = 'all';
        elseif (in_array($cf_cat_id, $reg_cats)) // Blog, chinim-kompy, stroim-sajty, trudoustrojstvo-zapiski-posvyashchennogo, sovety-doktora-aleny
          $categories = $cf_cat_id;
        elseif ($cf_cat_id == 28) // guitar
          $categories = 'guitar';
        elseif (in_array($cf_art_cat_id, array(614, 454, 603, 694)) || in_array($cf_cat_id, $hebro_cats)) // Hebroman
          $categories = 'hebroman';
        elseif (in_array($cf_art_cat_id, array(433, 434, 724, 957))) //video
          $categories = 'all';
      } elseif ($cf_view == 'category') {
        if (in_array($cf_art_cat_id, $reg_cats)) // Blog, chinim-kompy, stroim-sajty, trudoustrojstvo-zapiski-posvyashchennogo, sovety-doktora-aleny
          $categories = $cf_art_cat_id;
        elseif ($cf_art_cat_id == 28) // guitar
          $categories = 'guitar';
        elseif (in_array($cf_art_cat_id, $hebro_cats)) // Hebroman
          $categories = 'hebroman';
      }
    } elseif ($cf_option == 'com_muscol') // TiALP
      $categories = 'guitar';
    elseif ($cf_option == 'com_bt_portfolio') { // portfolio
      $categories = $tech_cats_str; // fixing-comps, building-sites
      $components[] = 'com_bt_portfolio';
    } elseif ($cf_option == 'com_joomgallery') // fotogalereya
      $components[] = 'com_joomgallery';
    elseif ($cf_option == 'com_jdownloads') {
      if (in_array($cf_cat_id, array(8, 9, 11))) // Hebroman
        $categories = 'hebroman';
      elseif ($cf_cat_id == 3) // Attachments
        $categories = $tech_cats_str; // chinim-kompy, stroim-sajty
      elseif ($cf_cat_id == 4) // Comp
        $categories = '26'; // chinim-kompy
      elseif ($cf_cat_id == 5) // Joomla
        $categories = '27'; // stroim-sajty
    } elseif ($cf_option == 'com_xmap') {
      if ($cf_art_cat_id == 1) // karta-sajta
        $categories = 'all';
      elseif ($cf_art_cat_id == 4) // karta-hebroman
        $categories = 'hebroman';
    }
 
    if ($categories == 'guitar') {
      $categories = '28';
      $components[] = 'com_muscol';
    } elseif ($categories == 'hebroman')
      $categories = implode(',', $hebro_cats);
  } elseif ($cf_lang == 'en') {
    $reg_cats = array(42, 43, 54);
    $tech_cats_str = '42,43';
 
    if ($cf_option == 'com_content') {
      if ($cf_view == 'article') {
        if (in_array($cf_art_cat_id, array(784, 306))) // home, feedback
          $categories = 'all';
        elseif (in_array($cf_cat_id, $reg_cats)) // fixing-comps, building-sites, employment-notes-of-the-initiate
          $categories = $cf_cat_id;
      } elseif ($cf_view == 'category') {
        if (in_array($cf_art_cat_id, $reg_cats)) // fixing-comps, building-sites, employment-notes-of-the-initiate
          $categories = $cf_art_cat_id;
      }
    } elseif ($cf_option == 'com_muscol') // TiALP
      $components[] = 'com_muscol';
    elseif ($cf_option == 'com_bt_portfolio') { // portfolio
      $categories = $tech_cats_str; // fixing-comps, building-sites
      $components[] = 'com_bt_portfolio';
    } elseif ($cf_option == 'com_joomgallery') // photogallery
      $components[] = 'com_joomgallery';
    elseif ($cf_option == 'com_jdownloads') {
      if ($cf_cat_id == 3) // Attachments
        $categories = $tech_cats_str; // fixing-comps, building-sites
      elseif ($cf_cat_id == 4) // Comp
        $categories = '42'; // fixing-comps
      elseif ($cf_cat_id == 5) // Joomla
        $categories = '43'; // building-sites
    } elseif ($cf_option == 'com_xmap') {
      if ($cf_art_cat_id == 2) // site-map
        $categories = 'all';
    }
  }
}

end faq

Как из него видно, первым делом идет привязка к языкам. Для каждого языка фильтруются комменты к статьям и категориям компонента com_content, а затем - к остальным компонентам. Некоторые из этих компонентов (в частности, com_jdownloads и com_xmap) также имеют категории, и привязка осуществляется отдельно к каждой из них в соответствии с ее тематикой. В случае многоуровневой (древовидной) структуры категорий комментарии дочерних категорий объединяются для привязки к категории-родителю. Пример - обработка страниц категорий компонента com_jdownloads. В случае гитарной директории объединяются комментарии к страницам самой директории (компонент com_muscol) и категории 'Гитара' (id==28) компонента com_content - в представлениях как блога категории, так и каждого материала, к ней относящегося.

Для случая, когда выводятся последние комменты ко всем страницам компонента com_content, функция передает в вызывающий ее код значение 'all' переменной $categories. В моем случае, например - для Главной страницы, формы обратной связи, страниц категории 'Видео' в обоих представлениях, а также всех карт сайта. Когда же нет необходимости в фильтрации комментариев к страницам прочих приложений, массив $components остается пустым.

Таким образом, в моем случае обработаны все разделы сайта для каждого языка:

  • Отдельные страницы - Главная, Обратная связь.
  • 8 разделов в русской и 3 в английской частях сайта. Каждый раздел включает страницы в представлении материалов и блогов одной или нескольких категорий.
  • По 5 прочих приложений для каждого языка:
    • Гитарная директория com_muscol - комменты к ней объединены с таковыми к категории 'Гитара' компонента com_content;
    • Компонент портфолио com_bt_portfolio - объединены комменты к страницам самого приложения и к страницам двух технических категорий;
    • Компонент для скачивания файлов com_jdownloads - обработаны 4 категории в русской части, 3 - в английской;
    • Карта сайта com_xmap - обработаны 2 карты в русской, 1 - в английской;
    • Галерея изображений com_joomgallery - обычная привязка ко всем страницам приложения.

Итого: для привязки последних комментариев до кастомизации я использовал около 20 (двадцати!) копий модуля mod_jcomments_latest. При этом отсутствовала возможность объединять комменты к страницам компонента com_content и других приложений. Теперь же я могу объединить комменты к ЛЮБЫМ страницам ЛЮБЫХ приложений. На данный момент у меня всего две копии модуля - для каждого из языков сайта, привязанные в админке по умолчанию - реальная привязка осуществляется в измененном коде.

Какой минус? При изменении структуры и условий привязки требуется вносить изменения в код модуля. Лично для меня это давно не является проблемой, как не является ею и сохранение кастомизаций при обновлении расширений разработчиками. Можно, конечно, усовершенствовать административную часть модуля, добавив функционал для подобной гибкой привязки и получая на фронт-энде условия фильтрации из параметров модуля. Но вот это уже - целый проект, игра не стоит свеч. А вот разобраться в структуре внутренних ссылок Joomla и утилизировать приобретенные знания для повышения гибкости привязки кода к избранным страницам сайта - дело весьма полезное. Эта статья, как и предыдущие (и, надеюсь, последующие) статьи цикла "Внутренние ссылки Joomla и примеры их применения", призвана помочь вам в этом.

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