Облако тегов для Modx

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

Так выглядит Облако тегов в сайдбаре:


Для реализации потребуется создать:
1. Шаблон Тег — будет использоваться для вывода всех ресурсов с данным тегами.
2. Структуру с тегами в дереве ресурсов
3. TV параметр tags — для возможности назначения тегов ресурсу
4. Вывод облака тегов
5. Сниппеты
5.1 tag_count — выводит кол-во тегов
5.2 tag_size — получает размер тега в пикселях
5.3 findDocumentsFromTag — ищет все документы, содержащие тег

Теперь по порядку
1. Добавляем шаблон Тег. Будем его использовать при созданиии структуры тегов. Само содержимое шаблона опишу ниже, чтобы было последоватлеьное получение информации.
2. Создаем структуру с тегами в дереве ресурсов.
Я создаю в корне документ Теги (alias tags). Внутри заранее создаю документы соответствующие тегам. Т.о. у нас получатся для каждого тега иметь возможность получить ЧПУ адрес, прописывать заголовки, теги, контент. Минусом такого подхода является то, что тег нужно создать заранее, чтобы его назначать документам. Каждому элементу назначаем шаблон «Тег» из первого пункта.
Получаем вот такую структуру

Запомним id документа, который соответствует ресурсу «Теги». В данном случае он равен 79.

3. Добавляем TV параметр tags
Имя: tags
Подпись: Теги
Тип ввода: Список (множественный выбор)
Возможные значения:
@SELECT pagetitle as name, id FROM [[+PREFIX]]site_content WHERE `parent` = 79
Вот тут указываем parent = 79, это номер ресурса с тегами.


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



4. Уже сейчас можно назначить нужным ресурсам теги и оформить вывод облака тегов.
Сделать это можно в сайдбаре например так:
{'pdoResources' | snippet : [
    'parents' => 79,
    'templates' => '13',
    'tpl' => 'tpl.tagCloud.item',
    'sortby' => 'menuindex',
    'sortdir' => 'ASC',
    'limit' => '100',
]}
Чанк tpl.tagCloud.item используется для оформления одной ссылки облака тегов. Его содержимое
{set $tagCount = $id | tag_count}
{if $tagCount > 0}
<a href="{$id | url}" class="tag-link-{$id} tag-link-position-4" title="{$tagCount} {$tagCount | declension : 'запись|записи|записей'}" style="font-size: {$id | tag_size}pt;">{$pagetitle}</a>
{/if}
Нам потребуется определить кол-во тегов — сниппет-модификатор tag_count. В коде есть упоминание id TV параметра tags. В моем случае его id = 18
<?php
$sql = "SELECT `tv`.* FROM {$modx->getTableName('modTemplateVarResource')} as tv
LEFT JOIN {$modx->getTableName('modResource')} as resource
ON (tv.contentid = resource.id)
WHERE resource.published = 1 AND resource.deleted = 0 AND tv.tmplvarid = '18'";

$q = new xPDOCriteria($modx, $sql);
$res = $modx->getCollection('modTemplateVarResource', $q);
$arr = [];
$countTags = 0;
foreach ($res as $v) {
    $value = $v->get('value');
    $valueArr = explode('||', $value);
    foreach ($valueArr as $tag) {
        if (array_key_exists($tag, $arr)) {
            $arr[$tag]++;
        } else {
            $arr[$tag] = 1;
        }
        $countTags++;
    }
}

if (array_key_exists($input, $arr)) {
    return $arr[$input];
}
return 0;
Размер ссылки задается инлайн стилем style=«font-size: {$id | tag_size}pt;». Содежимое сниппета tag_size. В коде есть упоминание id TV параметра tags. В моем случае его id = 18. Также можно указать минимальный размер шрифта и максимальный.
<?php
$fromSize = (!empty($fromSize)) ? $fromSize : 6;
$toSize = (!empty($toSize)) ? $toSize : 30;

$sql = "SELECT * FROM {$modx->getTableName('modTemplateVarResource')} WHERE tmplvarid = '18'";

$q = new xPDOCriteria($modx, $sql);
$res = $modx->getCollection('modTemplateVarResource', $q);
$arr = [];
$countTags = 0;
foreach ($res as $v) {
    $value = $v->get('value');
    $valueArr = explode('||', $value);
    foreach ($valueArr as $tag) {
        if (array_key_exists($tag, $arr)) {
            $arr[$tag]++;
        } else {
            $arr[$tag] = 1;
        }
        $countTags++;
    }
}
И собственно осталось вывести страницу с выбранным тегом, т.е. описать содержиоме шаблона Тег.
<div class="posts">
    <div class="rows">
        {set $resources = 'findDocumentsFromTag' | snippet : ['ids' => 1]}
        
        {$_modx->runSnippet('!pdoPage@styled', [
            'tpl' => 'tpl.tag.item',
            'parents' => '9,10',
            'resources' => $resources,
            'limit' => '30',      
            'includeTVs' => 'image,tags',
            'tvPrefix' => '',
            'templates' => '5,6',
            'ajaxMode' => 'button',
            '-ajaxTplMore' => '@INLINE <div class="news__more"><div class="news__more-row"><a href="#" class="news__more-link">Показать больше новостей</a></div></div>',
            'ajaxElemMore' => '#pdopage .news__more-link',
            'depth' => 10,
            'includeContent' => 1,
        ]) ?: '<p>Нет элементов для отображения</p>'}
    </div>
    {$_modx->getPlaceholder('page.nav')}            
</div>   
Это вызов обычного постраничного вывода с помощью pdoPage, но передаем ему заранее выбранные id документов. Для этого вверху вызывается сниппет findDocumentsFromTag. Снова вилим указание id TV параметра = 18. В вашем случае будет другой id
<?php
$tagId = (!empty($tag_id)) ? $tag_id : $modx->resource->get('id');

$sql = "SELECT * FROM {$modx->getTableName('modTemplateVarResource')} 
WHERE 
    `tmplvarid` = '18'
AND
(
    `value` = '{$tagId}'
OR
    `value` LIKE '{$tagId}||%'
OR  
    `value` LIKE '%||{$tagId}'
OR 
    `value` LIKE '%||{$tagId}||%'
)
";

$q = new xPDOCriteria($modx, $sql);

$res = $modx->getCollection('modTemplateVarResource', $q);

$arr = [];
foreach ($res as $v) {
    $arr[] = $v->get('contentid');
}

if (!empty($ids)) {
    return implode(',', $arr);
} else {
    return $arr;    
}
На это всё.

Нет комментариев