Плагин TagSaver для работы с тегами.
Вообще, фильтрация по тегам в MDOX реализуется довольно кисло. Представьте, у вас есть какой-то TV параметр, в котором у вас к каждой статье прописано по 10 тегов.
А в базе все эти теги для документа хранятся в одной строке. Т.е. Выглядит это примерно так
modx, теги, работа с тегами, фильтрация по тегам
Как обычно происходит фильтрация? Создается SQL запрос в таблицу site_tmplvar_contentvalues с применением WHERE value LIKE %тег%
Таким образом, мы можем искать не только по тем тегам, которые задумывались автором сайта, но и по тегам
Более того, если документов очень много, то поиск происходит нереально долго. Как вообще идеально должна выглядеть работа с тегами?
Таблица с документами вида: id, content, pagetitle
Таблица с тегами: id, tag
Таблица связей с тегами: doc_id, tag_id
Можно конечно долго спорить о необходимости таблицы связи, т.к. придется делать лишний JOIN. Но за счет правильно расставленых индексов этот джоин вообще незаметен. Более того, немного доработав таблицу мы можем забивать теги в нескольких TV-шках, а хранить в одной таблице по вышеописаному стандарту.
На основании всего вышесказанного у нас получаются вот такие 2 таблицы
DROP TABLE IF EXISTS `modx_site_content_tags`; CREATE TABLE `modx_site_content_tags` ( `doc_id` int(11) NOT NULL, `tag_id` int(11) NOT NULL, `tv_id` int(11) NOT NULL DEFAULT '0', PRIMARY KEY (`tag_id`,`doc_id`,`tv_id`), UNIQUE KEY `dtt` (`doc_id`,`tag_id`,`tv_id`) USING BTREE, KEY `doc_id` (`doc_id`), KEY `tag_id` (`tag_id`), KEY `tv_id` (`tv_id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; DROP TABLE IF EXISTS `modx_tags`; CREATE TABLE `modx_tags` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(50) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `name` (`name`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
Как можно заметить, формат у таблиц MyISAM. Но у такого выбора есть своя причина. Одна из них это auto_increment. А вторая – уникальный ключ по нескольким полям. Не буду долго задерживаться на этом пункте. Скажу одно – если у вас MODX установлен с префиксом таблиц отличным от modx, то измените его на свой;-)
Все очень просто. Создаете как обычно TV параметр. Можете даже добавить виджет mm_widget_tags от ManagerManager. После этого создаете плагин TagSaver с параметрами
&tv=ID TV-параметра;input; &sep=Разделитель тегов;input;
Если разделителя нет, то можно оставить пустым.
Создавайте копию плагина под другим именем с аналогичными параметрами. Только не забудьте у новой версии плагина указать разделитель и ID другого TV параметра.
Выводить их можете как и раньше – через сниппеты DocInfo, просто подставляя tv параметр на странице .
Очень просто. Создаете сниппет примерно такого содержания
$tag = ((isset($tag) && is_scalar($tag))? $tag : (isset($_GET['tag']) && !is_array($_GET['tag']) ? $_GET['tag'] : '')); $id = isset($id) ? (int)$id : 0; $out = array(); if($id>0 && $tag!=''){ $sql=$modx->db->query("SELECT doc_id FROM ".$modx->getFullTableName("tags")." as t LEFT JOIN ".$modx->getFullTableName("site_content_tags")." as ct ON ct.tag_id = id WHERE t.`name`='".$modx->db->escape($tag)."' AND ct.tv_id={$id}"); $sql=$modx->db->makeArray($sql); foreach($sql as $item){ $out[]=$item['doc_id']; } } return implode(",",$out);
И результат работы этого сниппета передаете в параметр documents от Ditto. У сниппета указаного выше есть 2 параметра:
Получается примерно так
[[Ditto? &documents=`[!GetTag? &id=`2`!]`]]
$count = isset($count) ? (int)$count : 10; $tv = isset($tv) ? (int)$tv : ''; $out = array(); $sql = $modx->db->query( "SELECT ct.tv_id,t.name,count(ct.tag_id) as count FROM ".$modx->getFullTableName("tags")." as t LEFT JOIN ".$modx->getFullTableName("site_content_tags")." as ct ON ct.tag_id=t.id LEFT JOIN ".$modx->getFullTableName("site_content")." as c on c.id=ct.doc_id WHERE deleted=0 AND published=1 ".(($tv!='') ? ("AND ct.tv_id=".$tv) : "")." GROUP BY tag_id ORDER BY count(ct.tag_id) DESC LIMIT 0,".$count ); $sql = $modx->db->makeArray($sql); foreach($sql as $item){ $out[]=$modx->parseChunk($tpl,$item,"[+","+]"); } return implode((isset($outSep) ? $outSep : ""),$out);
У сниппета 3 параметра
<a href="/11.html?tag=[[urlencode? &input=`[+name+]`]]" title="Статьи с тегом [+name+]" class="label">[+name+] ([+count+])</a>
Как видно, этот сниппет не только теги подставляет, но еще и считает сколько раз они используются. Таким образом вызов сниппета для облака тегов выглядит так:
[[TagCloud? &tpl=`TagCloudItem` &tv=`7` &count=`20`]]
return isset($input) ? urlencode($input) : '';
Для чего он нужен найдете в гугле
В силу того, что мне довольно часто приходится работать с сайтами сделаными другими программистами, то могу сказать вам одно – таких решений еще нигде небыло. Все фильтруют по старинке средствамми Ditto и изобретают свои велосипеды в виде экстендеров для него. Мое решение повзоляет вам работать с видимой частью так же, как и раньше. Зато существенно снимает нагрузку на сервер при фильтрациях. И облегчает выборку даже если вы используете знаменитый сниппет DropDownDocs для привязки статей к нескольким разделам (ведь по сути это тоже теги, только вид с боку). Соответственно, если вам нужны теги и фильтрации по TV параметрам в значении которых может быть несколько данных – используйте TagSaver.
Автор: Agel_Nash