Продолжаем тему с тегами. В прошлой статье мы рассмотрели простую систему тегов — 1 тег для одного тикета (ресурса). В этой статье я покажу как к тикету привязать несколько тегов.

Количество шагов будет такое же, но многое придется изменить.

1. Расширяем таблицу ресурсов

Через phpMyAdmin создаем поле tags в таблице modx_site_content. Длину зададим побольше, так как в нём мы будем хранить несколько тегов.

Тип: varchar
Длина: 200

2. Создаем TV

В дереве ресурсов создаем TV с именем tv.tags и типом ввода «Список (множественный выбор)». Дальше нужно привязать его к шаблону, который используется для тикетов. Добавляем теги для списка в формате TAG==TAG и объединяем их через две вертикальные черты (||).

Можете указать значение по-умолчанию, если нужно.

3. Плагин для сохранения тегов

Создаём плагин с любым именем на события «OnMODXInit» и «OnDocFormSave».

<?php
switch ($modx->event->name) {
    case 'OnMODXInit':
        // Загружаем наше поле в модель ресурса
        $modx->loadClass('modResource');
        $modx->map['modResource']['fields']['tags'] = '';
        $modx->map['modResource']['fieldMeta']['tags'] = array(
            'dbtype' => 'varchar',
            'precision' => 200,
            'phptype' => 'string',
            'null' => false,
            'default' => '',
        );
        break;
    case 'OnDocFormSave':
        // Сохраняем ТВ в поле таблицы ресурса
        $tv = $modx->getObject('modTemplateVar', array('name' => 'tv.tags'));
        $fields = array(
            'tmplvarid' => $tv->get('id'), 
            'contentid' => $resource->get('id'),
        );
        if ($tvr = $modx->getObject('modTemplateVarResource', $fields)) {
            $tv = str_replace('||',',',$tvr->get('value')); 
            $resource->set('tags', $tv);
        } else {
            $resource->set('tags', '');
        }
        $resource->save();
        break;
}

4. Сниппет для вывода тегов

Как и в предыдущей статье создаём сниппет getTags, который будет выводит теги с количеством статей.

<?php
// При вызове сниппета указываем параметр $parent - секция тикетов, в котором считать тикеты.
$parent = $modx->getOption('parent', $scriptProperties, 0);

$q = $modx->newQuery('modResource');
$q->where(array('parent' => $parent,'published'=>1));
$q->select('tags');
if ($q->prepare() && $q->stmt->execute()) {
    $res = $q->stmt->fetchAll(PDO::FETCH_ASSOC);
}
$_tags = $tags = array();
foreach($res as $row){
    if (!empty($row['tags'])) {
        $_tags = array_merge($_tags,explode(',',$row['tags']));
    }
};
// Подсчитываем количество
foreach($_tags as $tag) {
    if (isset($tags[$tag])) {
        $tags[$tag]++;
    } else {
        $tags[$tag] = 1;
    }
}
// Сортируем
ksort($tags);
$pls = '';
// Формируем список
if (!empty($tags)) {
    $pls .= '<ul>';
    foreach($tags as $tag=>$count){
        $url = $modx->makeUrl($parent, '', array('tag' => $tag));
        $pls .= "<li><a href=\"{$url}\"><i class=\"fa fa-tag\"></i> {$tag} ({$count})</a></li>";
    };
    $pls .= '</ul>';
}
return $pls;

Вставляем вызов сниппета в нужное место. Я это делаю в сайдбаре.

// В parent указываем контейнер с тикетами
[[!getTags? &parent=`10`]]

5. Фильтруем тикеты

Теперь поправим сниппет-обертку getFilteredTickets. Так как поиск немного усложнился, то нужно переписать условие where.

<?php
$tag = isset($_GET['tag']) ? $modx->sanitizeString($_GET['tag']) : '';

if ($tag) {
    $where = '["FIND_IN_SET(\''.$tag.'\', `Ticket`.`tags`) > 0"]';
    $scriptProperties = array_merge($scriptProperties,array('where'=>$where));
}

return $modx->runSnippet('getTickets',$scriptProperties);

Теперь в разделе с тикетами в табе «Настройки раздела» (страница с типом ресурса «Раздел с тикетами») нужно указать этот сниппет.

[[!pdoPage?
    &element=`getFilteredTickets`
]]

[[!+page.nav]]

Чтобы вызвать в произвольном месте, используем такой вызов

// Указываем необходимые параметры сниппета getTickets
[[!pdoPage?
    &element=`getFilteredTickets`
    &parents=`5`
]]

[[!+page.nav]]

Всё. Подготовка закончена.Теперь при создании тикета ему можно указывать несколько тегов.

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

<?php
// Сниппет getRelatedTickets
// Сниппет будет работать только для тикетов. 
if ($modx->resource->get('class_key') != 'Ticket') return '';

$tags = $modx->resource->get('tags');
if (empty($tags)) return '';

$tags = explode(',', $tags);
$where = '';
foreach($tags as $tag){
   if (!empty($where)) $where .= ' OR ';
   $where .= 'FIND_IN_SET(\''.$tag.'\', `tags`) > 0';
}
if (!empty($where)) $scriptProperties = array_merge($scriptProperties,array('where'=>$where));
return $modx->runSnippet('pdoResources',$scriptProperties);

Ну а дальше дело за малым — нужно вызвать этот сниппет:

[[!getRelatedTickets? 
    &tpl=`@INLINE <li><a href="[[+uri]]">[[+pagetitle]]</a></li>` 
    &select=`pagetitle,uri` 
    &parents=`0` // Без ограничений
    &resources=`-[[*id]]`  // Исключаем текущий тикет
    &limit=`0`  // Выводим все найденные тикеты
    &class=`Ticket`
]]

Решение, описанное в этой статье, работает на этом сайте. Можно проверить прямо на этой страничке.

21 января 2016, 10:19   2190     12

Комментарии ()

  1. Tim Yusupov 22 января 2016, 09:57 # 0
    Супер! Спасибо огромное за статью!!!
    1. Сергей Шлоков 22 января 2016, 10:16 # 0
      На здоровье! Главное программно не менять ТВшки. Иначе будет рассинхрон.
      1. Tim Yusupov 22 января 2016, 12:44 # 0
        Для вывода схожих статей нужно сниппет наверное написать? Когда один тег – всё просто, а вот с несколькими как, where дописать?
        1. Сергей Шлоков 22 января 2016, 12:56 # 0
          Смотри пункт 5.
          1. Tim Yusupov 26 января 2016, 13:53 # 0
            $modx->sanitizeString – вырезает запятые :) и если несколько тегов, то получается одно сплошное слово)
            Подумал, что можно так, с помощью stripTags:
            $tags = 'мода,бизнес'; //$_GET['tag']
            $tag = !empty($tags) ? $modx->stripTags($tags)  : '';
            $_tags = explode(',', $tag);
            А дальше, как по каждому элементу массива выбрать все подходящие тикеты – не хватает знаний)
            1. Сергей Шлоков 26 января 2016, 15:53 # 0
              Это типа ссылка такая с двумя тегами? Или типа галочки ставишь у нужных тегов, а потом ищешь?
              1. Tim Yusupov 26 января 2016, 16:11 # 0
                Например, на странице Тикета, блок с Похожими статьями. Таким образом, когда у Тикета несколько тегов, они же в БД записываются через запятую, нужно по нескольким тегам найти статьи с такими же тегами.
                Есть пример с использованием TV полей:
                $tpl = $modx->getOption('tpl', $scriptProperties);
                $limit = $modx->getOption('limit', $scriptProperties, 5);
                $tvname = $modx->getOption('tvname', $scriptProperties, "autotag");
                $tvvalue = $modx->resource->getTVValue($tvname);
                $base = $base = $modx->config['base_url'];
                $currentid = $modx->resource->id;
                $output = '';
                
                $q = $modx->newQuery('modResource', array(
                	'context_key' => $modx->resource->context_key,
                	'parent' => $modx->resource->parent
                ));
                $q->select('pagetitle,introtext,publishedon,uri,tvres.value as autotag');
                $q->innerJoin('modTemplateVarResource', 'tvres', "tvres.contentid = modResource.id");
                $q->innerJoin('modTemplateVar', 'tv', "tv.id = tvres.tmplvarid");
                $q->limit($limit);
                $q->where(array(
                		'modResource.id:!=' => $currentid,
                		'tv.name' => $tvname,
                		'tvres.value:REGEXP' => str_replace( ',', '|', $tvvalue)
                	)
                );
                
                if($q->prepare() && $q->stmt->execute()) {
                	while ($row = $q->stmt->fetch(PDO::FETCH_ASSOC)) {
                		$output .= $modx->getChunk($tpl, array(
                			'url' => $base.$row['uri'], 
                			'date' => date("Y-m-d H:i :s", $row['publishedon']),
                			'pagetitle' => $row['pagetitle'],
                			'text' => $row['introtext']
                		));
                	}
                }
                
                return $output;
                Правда не знаю, в тему ли вообще этот пример.)
                1. Сергей Шлоков 26 января 2016, 18:36 # 0
                  Теоретически так. Не тестировал.
                  <?php
                  $tags = $_GET['tag']
                  $tags = !empty($tags) ? $modx->stripTags($tags)  : '';
                  if (empty($tags)) return '';
                  $tags = explode(',', $tag);
                  $where = '';
                  foreach($tags as $tag){
                     if (!empty($where)) $where = ' OR ';
                     $where = 'FIND_IN_SET(\''.$tag.'\', `Ticket`.`tags`) > 0';
                  }
                  
                  $scriptProperties = array_merge($scriptProperties,array('where'=>$where));
                  
                  return $modx->runSnippet('getTickets',$scriptProperties);
                  
                  1. Tim Yusupov 26 января 2016, 19:31 # 0
                    Спасибо, Сергей!!! Вот рабочий вариант:
                    <?php
                    $tags = $_GET['tag'];
                    $tags = !empty($tags) ? $modx->stripTags($tags)  : '';
                    if (empty($tags)) return '';
                    $tags = explode(',', $tags);
                    $where = '';
                    foreach($tags as $tag){
                       if (!empty($where)) $where .= ' OR ';
                       $where .= 'FIND_IN_SET(\''.$tag.'\', `Ticket`.`autotag`) > 0';
                    }
                    
                    $scriptProperties = array_merge($scriptProperties,array('where'=>$where));
                    
                    return $modx->runSnippet('getTickets',$scriptProperties);
                    1. Сергей Шлоков 26 января 2016, 19:44 # 0
                      Ну да, точку пропустил. :) Рад что работает.
    2. Дмитрий 30 октября 2016, 18:43 # 0
      Сергей, спасибо за статью! Все получилось, кроме вывода тегов в мета.
      Например у этой статьи в мета видны теги «MODX, Tickets, Тэги».
      Как это сделать?
      1. Сергей Шлоков 30 октября 2016, 19:29 # +1
        Вставить в чанк
        <i class="fa fa-tags"></i> [[+tags:replace=`,==, `]]	

      Вы должны авторизоваться, чтобы оставлять комментарии.

      Выделите опечатку и нажмите Ctrl + Enter, чтобы отправить сообщение об ошибке.