Навигация по автометкам
Хочу поделиться своим способом работы с метками, для дополнительной навигации по ресурсам. Вообще то, для этого уже есть tagLister, но мне нравится контролировать процесс. И вообще, это мой первый пост тут, так что сильно не пинайте :)
Для создания, собственно меток, логично использовать штатный tv параметр с типом autotag (автометка), поэтому вначале, создаю его и как нибудь называю. Не задумываясь особенно, назвал его по названию типа — autotag. Добавляю созданный tv в шаблон для вывода постов и с ним — все.
Для вывода меток, делаю два сниппета, один для создания облака меток — tagCloud, второй для вывода меток в блоге и в самом посте — tagLinks.
tagCloud:
Этот сниппет выводит список уникальных значений меток, в виде ссылок, с GET параметром, который потом будет использоваться, для фильтрации вывода ресурсов:
Что в нем происходит:
Из таблицы с классом «modTemplateVarResource», выбираю все tv с типом «autotag» и именем «autotag», связанные с ресурсами находящимися в текущем контексте, родителем для которых, в случае вывода в блоге, является ресурс где вызывается сниппет, а в случае вывода в статье — родитель статьи. Так как в одной автометке, может быть много значений, перечисленных через запятую, то сначала, на уровне запроса, убираю повторяющиеся множественные значения, затем разбиваю результирующий массив по запятым и из него, так же, убираю повторяющиеся значения. В конце формирую ссылки.
tagLinks:
Этот сниппет, выводит список ссылок, для блога и ресурса, а так же передает GET-параметр в вызов pdoResources, для фильтрации по меткам. В принципе, это разные задачи, но я решил не плодить сниппеты.
Что в нем происходит:
Сниппет может принимать в качестве параметров два значения:
В первом случае, сниппет разбивает полученную автометку на отдельные значения, заворачивает их в ссылки, как в tagCloud и выводит. Для того, чтобы можно было выводить метки и в блоге и в самой статье, в начале сниппета получаю расширение для типа html и проверяю его наличие в адресной строке. Если адрес заканчивается на .html, то в url попадает адрес родителя (для статьи).
Во втором случае, когда передается параметр &get=`1`, сниппет смотрит в адресную строку и если там есть значение метки, то передает его в вывод, оформляя подходящим для фильтрации образом.
А вот вызов pdoResources, для создания блога и фильтрации по автометке.
На уникальность не претендую :)
UPD.
Если захотелось вывести, например в сайдбаре, статьи связанные с текущей, по меткам, то можно использовать вот такой сниппет
tagRelated:
Здесь, я выбираю записи из таблицы с классом modResource, с которыми связана автометка имеющая, хотя бы одно значение, такое же как у текущей.
Вызов:
Чанк RELATED:
Для создания, собственно меток, логично использовать штатный tv параметр с типом autotag (автометка), поэтому вначале, создаю его и как нибудь называю. Не задумываясь особенно, назвал его по названию типа — autotag. Добавляю созданный tv в шаблон для вывода постов и с ним — все.
Для вывода меток, делаю два сниппета, один для создания облака меток — tagCloud, второй для вывода меток в блоге и в самом посте — tagLinks.
tagCloud:
<?php
$base = $modx->config['base_url'];
$tvname = $modx->getOption('tvname', $scriptProperties, "autotag");
$output = "";
$content_type = $modx->getObject('modContentType', array('mime_type' => 'text/html'));
if(substr_count($_SERVER["REQUEST_URI"], $content_type->get('file_extensions'))) {
$parent = $modx->resource->parent;
$url = $modx->makeUrl($modx->resource->parent);
}else{
$parent = $modx->resource->id;
$url = $modx->resource->uri;
}
$q = $modx->newQuery('modTemplateVarResource');
$q->select('DISTINCT(`modTemplateVarResource`.`value`)');
$q->innerJoin('modTemplateVar', 'tv', "tv.id = modTemplateVarResource.tmplvarid");
$q->innerJoin('modResource', 'res', 'res.id=modTemplateVarResource.contentid');
$q->where(array(
'tv.name' => $tvname,
'res.context_key' => $modx->resource->context_key,
'res.parent' => $parent
)
);
if($q->prepare() && $q->stmt->execute()) {
while ($row = $q->stmt->fetch(PDO::FETCH_ASSOC)) {
$result[] = $row['value'];
}
}
if($result) {
$result = implode($result, ',');
$result = array_unique(explode(',', $result));
foreach($result as $value) {
$output .= "<a href='{$base}{$url}?tag={$value}'>{$value}</a> ";
}
}
return $output;
Этот сниппет выводит список уникальных значений меток, в виде ссылок, с GET параметром, который потом будет использоваться, для фильтрации вывода ресурсов:
[[!tagCloud:default=`Пока нет меток`? &tvname=`autotag`]]
<a href="/path/?tag=метка">метка</a>
<a href="/path/?tag=опятьметка">опятьметка</a>
<a href="/path/?tag=ещеметка">ещеметка</a>
Что в нем происходит:
Из таблицы с классом «modTemplateVarResource», выбираю все tv с типом «autotag» и именем «autotag», связанные с ресурсами находящимися в текущем контексте, родителем для которых, в случае вывода в блоге, является ресурс где вызывается сниппет, а в случае вывода в статье — родитель статьи. Так как в одной автометке, может быть много значений, перечисленных через запятую, то сначала, на уровне запроса, убираю повторяющиеся множественные значения, затем разбиваю результирующий массив по запятым и из него, так же, убираю повторяющиеся значения. В конце формирую ссылки.
tagLinks:
<?php
$tags = $modx->getOption('tags', $scriptProperties);
$get = $modx->getOption('get', $scriptProperties, '0');
$base = $modx->config['base_url'];
$content_type = $modx->getObject('modContentType', array('mime_type' => 'text/html'));
if(substr_count($_SERVER["REQUEST_URI"], $content_type->get('file_extensions'))) {
$url = $modx->makeUrl($modx->resource->parent);
}else{
$url = $modx->resource->uri;
}
if(!$get){
if(!$tags) {return '';}
$tags = explode(',',$tags);
foreach($tags as $value) {
$output[] = "<a href='{$base}{$url}?tag={$value}'>{$value}</a>";
}
return implode(' ',$output);
}else{
return (!empty($_GET['tag']))? "autotag==%{$_GET['tag']}%" : '';
}
Этот сниппет, выводит список ссылок, для блога и ресурса, а так же передает GET-параметр в вызов pdoResources, для фильтрации по меткам. В принципе, это разные задачи, но я решил не плодить сниппеты.
Что в нем происходит:
Сниппет может принимать в качестве параметров два значения:
[[!tagLinks? &tags=`[[*autotag]]`]]
и
[[!tagLinks? &get=`1`]]
В первом случае, сниппет разбивает полученную автометку на отдельные значения, заворачивает их в ссылки, как в tagCloud и выводит. Для того, чтобы можно было выводить метки и в блоге и в самой статье, в начале сниппета получаю расширение для типа html и проверяю его наличие в адресной строке. Если адрес заканчивается на .html, то в url попадает адрес родителя (для статьи).
Во втором случае, когда передается параметр &get=`1`, сниппет смотрит в адресную строку и если там есть значение метки, то передает его в вывод, оформляя подходящим для фильтрации образом.
А вот вызов pdoResources, для создания блога и фильтрации по автометке.
[[!pdoResources?
&parents=`[[*id]]`
&tvFilters=`[[!tagLinks? &get=`1`]]`
&tpl=`@INLINE <div class="row">
<div class="cols col-10 intro">
<h2><a href="{{+link}}">{{+pagetitle}}</a> ({{+publishedon}})</h2>
<div>{{!tagLinks? &tags=`{{+tv.autotag}}`}}</div>
{{+introtext}} <a href="{{+link}}">читать дальше..</a>
</div>
</div>`
]]
На уникальность не претендую :)
UPD.
Если захотелось вывести, например в сайдбаре, статьи связанные с текущей, по меткам, то можно использовать вот такой сниппет
tagRelated:
<?php
$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;
Здесь, я выбираю записи из таблицы с классом modResource, с которыми связана автометка имеющая, хотя бы одно значение, такое же как у текущей.
Вызов:
[[!tagRelated:default=`Пока нет связанных постов`?
&tpl=`RELATED`
&limit=`4`
&tvname=`autotag`
]]
Чанк RELATED:
<div>
<a href="[[+url]]">[[+pagetitle]]</a> <span class="date">[[+date]]</span><br />
<div class="sidetext">[[+text:notags:ellipsis=`100`]]</div>
</div>
Комментарии: 23
Потрясающе! Пару дней искал решение, и тут ИДЕАЛЬНО! Прямо то, что нужно. Спасибо!
Один вопрос, можно ли указать контейнер ресурсов, чтобы на странице со статьёй тоже облако тегов отображалось?
Один вопрос, можно ли указать контейнер ресурсов, чтобы на странице со статьёй тоже облако тегов отображалось?
Поправил tagCloud, для вывода в статье тоже.
Спасибо! И ещё малюсенький вопрос. На сайте есть метки и категории, по сути те же метки. Как добавить в tagLinks, чтобы по двум тв параметрам сравнивал и выводил подходящие. Сделал два сниппета, на каждый тв. В вызове pdoResources указал в tvFilters
&tvFilters=`[[!tagLinks? &get=`1`]] || [[!caregoryLinks? &get=`1`]]`
В таком случае все отлично фильтрует, но тогда чистый блог не выводит совсем.
Наверное лучше так:
return (!empty($_GET['tag']))? "tags1==%{$_GET['tag']}%||tags2==%{$_GET['tag']}%" : '';
И ещё один момент: если расширения нет к html
Тоже не показывает в статьях, как и tagLinks, если закомментировать несколько строчек, то всё ок.
Расширение html, как и домен www являются ненужными призраками прошлого — можно смело от них избавляться. ©bezumkin.ru
Тоже не показывает в статьях, как и tagLinks, если закомментировать несколько строчек, то всё ок.
$content_type = $modx->getObject('modContentType', array('mime_type' => 'text/html'));
// if(substr_count($_SERVER["REQUEST_URI"], $content_type->get('file_extensions'))) {
$url = $modx->makeUrl($modx->resource->parent);
/* }else{
$url = $modx->resource->uri;
}
*/
Может лучше делать проверку по наличию .html в строке. А, к примеру, указывать в вызове &parent, а в снимете сравнивать уже:
кусок tagLinks
кусок tagLinks
$parent = $modx->getOption('parent', $scriptProperties, $modx->resource->parent);
if($parent == $modx->resource->parent) {
$url = $modx->makeUrl($modx->resource->parent);
}else{
$parent = $modx->resource->id;
$url = $modx->resource->uri;
}
И аналогично у tagCloud:$parent = $modx->getOption('parent', $scriptProperties, $modx->resource->parent);
if($parent == $modx->resource->parent) {
$url = $modx->makeUrl($modx->resource->parent);
}else{
$parent = $modx->resource->id;
$url = $modx->resource->uri;
}
Тим, это ведь не компонент с инсталлятором, где нужно сразу учитывать все потенциальные нужды. Я думаю, что тут главное хорошо составленный запрос, а танцы вокруг него, выполняются по произвольной программе ;)
Да, верно) В любом случае спасибо огромное за решение! А дорабатывает под свои нужды пусть каждый сам ))
Саша, спасибо за интересный материал!
Для мультиязычности Babel используется?
Настройки как по мануалу?
Для мультиязычности Babel используется?
Настройки как по мануалу?
Да, Babel. Делал, в основном, по Васиной инструкции.
Плагин переключающий контексты:
Сниппет переключающий языки:
Настройки контекстов:
Настройки системы -> Babel
Тег base в начале странички:
Плагин переключающий контексты:
<?php
// Работаем только на фронтенде и только с friendly urls
if ($modx->event->name != 'OnHandleRequest' || $modx->context->key == 'mgr' || !$modx->getOption('friendly_urls')) {return;}
// Получаем запрашиваемый url
$alias = $modx->getOption('request_param_alias', null, 'alias', true);
$request = &$_REQUEST[$alias];
// Выбираем контексты с настройкой base_url
$q = $modx->newQuery('modContextSetting', array('key' => 'base_url', 'value:!=' => ''));
$q->select('context_key,value');
$contexts = array();
$tstart = microtime(true);
if ($q->prepare() && $q->stmt->execute()) {
// Учитываем наш запрос в БД
$modx->queryTime += microtime(true) - $tstart;
$modx->executedQueries++;
// Разбираем результаты
while ($row = $q->stmt->fetch(PDO::FETCH_ASSOC)) {
$base_url = trim($row['value'], '/');
$context = $row['context_key'];
// Если запрос начинается с base_url какого-то контекста
if (preg_match('/^('.$base_url.')\//i', $request)) {
// То переключаемся на этот контекст
// Web инициализируется в index.php - на него переключаться не нужно
if ($context != 'web') {
$modx->switchContext($context);
}
// Вырезаем base_url из запроса, чтобы MODX нашел ресурс по uri
$request = preg_replace('/^'.$base_url.'\//', '', $request);
// Дело сделано - выходим из цикла
break;
}
}
}
Сниппет переключающий языки:
<?php
//error_reporting(E_ALL | E_STRICT);
//ini_set('display_errors', 1);
$class = $modx->getOption('class', $scriptProperties, '');
$divider = PHP_EOL;
$output = '';
function getLink($context = 'web') {
global $modx;
if ($modx->getOption('site_start') != $modx->resource->id && $modx->getCount('modResource', array('uri' => $modx->resource->uri, 'context_key' => $context))) {
return $modx->resource->uri;
}
return '';
}
switch ($modx->context->key) {
case 'en':
$output .= "<a class='".$class."' href='/".getLink('web')."'>RUS</a> {$divider}
<a class='".$class."' href='kz/".getLink('kz')."'>KAZ</a>
<span class='".$class."'>ENG</span> {$divider}";
break;
case 'kz':
$output .= "<a class='".$class."' href='/".getLink('web')."'>RUS</a> {$divider}
<span class='".$class."'>KAZ</span>
<a class='".$class."' href='en/".getLink('en')."'>ENG</a> {$divider}";
break;
default:
$output .= "<span class='".$class."'>RUS</span> {$divider}
<a class='".$class."' href='kz/".getLink('kz')."'>KAZ</a>
<a class='".$class."' href='en/".getLink('web')."'>ENG</a> {$divider}";
}
return $output;
Настройки контекстов:
en
base_url base_url /en/
Язык cultureKey en
http_host http_host rirme.kz
Главная страница сайта site_start 2
site_url site_url http://rirme.kz/
kz
base_url base_url /kz/
Язык cultureKey kz
http_host http_host rirme.kz
Главная страница сайта site_start 3
site_url site_url http://rirme.kz/
web
base_url base_url /
Язык cultureKey ru
http_host http_host rirme.kz
Главная страница сайта site_start 1
site_url site_url http://rirme.kz/
Настройки системы -> Babel
babel.contextKeys babel.contextKeys web,en,kz
Тег base в начале странички:
<base href="[[++site_url]]" />
Спасибо огромное за развернутый ответ!
Полезно, мне как то достался сайт, где было 10-15 ТВшек и все они были с типом АВТОМЕТКА, со временем страничка ресурса в админке перестала грузиться и пришлось убрать там этот тип(
Но повторюсь, кол-во таких ТВ было большое
Но повторюсь, кол-во таких ТВ было большое
В соц сети очень полезно добавлять хэш тэги, типа #тэг1 и т.п.
Само собой, напрашивается: tagLinks урезаем, делаем HashtagLinks
[[!HashtagLinks? &tags=`[[*tags]]`]]
Может у кого есть мысли как реализовать автопостинг и хэштэгов тоже?
Само собой, напрашивается: tagLinks урезаем, делаем HashtagLinks
[[!HashtagLinks? &tags=`[[*tags]]`]]
<?php
$tags = $modx->getOption('tags', $scriptProperties);
if(!$get){
if(!$tags) {return '';}
$tags = explode(',',$tags);
foreach($tags as $value) {
$output[] = "#{$value}";
}
return implode(' ',$output);
}
Одно досадно, предзаполнять поле комментариев по ogp.me/ не предусмотрено, а в <meta property=«og:description» content="[[!HashtagLinks? &tags=`[[*tags]]`]] добавлять бессмысленно, что сводит все на нет, т.е. постинг хэш-тегов в комментарии вручную. Может у кого есть мысли как реализовать автопостинг и хэштэгов тоже?
Помогите новичку пожалуйста.
Я использовал раньше такой вариант taglist
Надеялся что замена
Подскажите как быть?
Я использовал раньше такой вариант taglist
<?php
$tags = $modx->getOption('tags', $scriptProperties);
$get = $modx->getOption('get', $scriptProperties, '0');
$base = $modx->config['base_url'];
$content_type = $modx->getObject('modContentType', array('mime_type' => 'text/html'));
$url = $modx->makeUrl($modx->resource->parent);
if(!$get){
if(!$tags) {return '';}
$tags = explode(',',$tags);
foreach($tags as $value) {
$output[] = "<a class='tag' href='/tags?tag={$value}' rel=\"nofollow\">{$value}</a>";
}
return implode(' ',$output);
}else{
return (!empty($_GET['tag']))? "tags==%{$_GET['tag']}%" : '';
}
Вызывал pdopage и в чанке прописывал так:<p>Тэги: [[!tagLinks? &tags=`[[+tv.tags]]`]]</p>
Все прекрасно работало, но вот сейчас перенес все тэги из TV в поле description.Надеялся что замена
$tags = $modx->getOption('tags', $scriptProperties);
на $tags = $modx->resource->get('description');
поможет, но в чанк<p>Тэги: [[!tagLinks? &tags=`[[+description]]`]]</p>
передается только дескрипшен той странице на которой вызывается, а не того ресурса который передается из pdopage.Подскажите как быть?
Подскажите пожалуйста, а как вывести теги не через pdoResources а если статьи выводятся через getPage?
getPage сам по себе ничего не выводит, он только обеспечивает разбиение на страницы. Выводом, скорее всего, занимается getResources. В таком случае самым простым вариантов будет установка pdoTools и замена в вызове [[getPage]] на [[pdoPage]]. А дальше — по многочисленным здесь руководствам и ответам.
Заменил, все получилось, большое спасибо.
Спасибо интересное решение, возник вопрос, можно ли как-то страницам с тегами задавать свой title и description, т.е например пользователь кликает на тег и попадает на страницу мойсайт/tag где выводятся посты с текущим тегом и на этой странице был свой заголовок и описание для ПС.
Тоже думаю на эту тему, решения не нашел. Есть идея, создать отдельный раздел (секцию) — TEG, там будут ресурсы, по одному ресурсу для вывода статей с этим тегом. Так можно прописывать title и description.
Вывод осуществлять с помощью pdoPage с фильтрацией &tvFilters=`тег`.
Облако тегов будет — вывод всех ресурсов из созданного раздела — TEG.
Встанет проблема как вывести теги (ссылки) принадлежащие одной статье. Можно использовать taglister, но он будет формировать свои ссылки, которые нам не нужны. Можно попробовать подменять их с помощью компонента redirector, направлять на наши созданные ресурсы, вместо ссылок taglister.
Сам пока не реализовывал, но другого варианта пока не нашел. А title и description для страниц вывода материалов по тегам, вещь нужная и полезная, можно получить дополнительных посетителей, поисковики сейчас любят когда у вас подборка материалов по теме, раскрывающая ее (но правильное оформление страницы — обязательно).
Минус решения — трудоемко и страницы, с тегами придется создавать ручками. Как бы автоматизировать…
Вывод осуществлять с помощью pdoPage с фильтрацией &tvFilters=`тег`.
Облако тегов будет — вывод всех ресурсов из созданного раздела — TEG.
Встанет проблема как вывести теги (ссылки) принадлежащие одной статье. Можно использовать taglister, но он будет формировать свои ссылки, которые нам не нужны. Можно попробовать подменять их с помощью компонента redirector, направлять на наши созданные ресурсы, вместо ссылок taglister.
Сам пока не реализовывал, но другого варианта пока не нашел. А title и description для страниц вывода материалов по тегам, вещь нужная и полезная, можно получить дополнительных посетителей, поисковики сейчас любят когда у вас подборка материалов по теме, раскрывающая ее (но правильное оформление страницы — обязательно).
Минус решения — трудоемко и страницы, с тегами придется создавать ручками. Как бы автоматизировать…
Добрый день. Интересует вопрос. А можно как-то сделать чтобы авто-метки подтягивались для каждого языка только свои. Мультиязычность реализована контекстами через Babel?
Вот, вдруг кому-нибудь всё еще интересно. Создаю три tv для каждого контекста и показываю каждый только в соответствующем контексте:
Ну и предыдущую логику тоже придется немного переделать.
if($modx->event->name == 'OnDocFormRender') {
$tvs = [
"tags_web",
"tags_ua",
"tags_en"
];
$context = $resource->get('context_key');
$tohide = "";
foreach ($tvs as $tv) {
if (! $res = $modx->getObject('modTemplateVar', array('name' => $tv)))
continue;
$tv_id = $res->get("id");
$tv_ctx = end(explode("_", $tv));
if ($tv_ctx == $context)
continue;
$tohide .= "'tv".$tv_id."',";
}
$tohide = rtrim($tohide, ',');
// Такие функции смотреть в "manager/assets/modext/core/modx.js"
$hideTVs = "MODx.hideTVs([{$tohide}]);";
$modx->regClientStartupHTMLBlock("<script>
Ext.onReady(function(){
$hideTVs
});
</script>");
}
Ну и предыдущую логику тоже придется немного переделать.
День добрый, Александр.
Посмотреть демо или скачать бекап демо есть возможность??
Не все получилось.
Хотелось бы до конца разобраться.
Посмотреть демо или скачать бекап демо есть возможность??
Не все получилось.
Хотелось бы до конца разобраться.
В скайп стукните.
sasha.tumanov
sasha.tumanov
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.