Поддержать Проект

Обратная связь

[MODX] Guru
  • Информация
  • Разработчикам
  • Дополнения
    • DocLister
    • DLMenu
    • DLCrumbs
    • DLBuildMenu
    • DLLastViews
    • DLSiblings
    • DLRequest
    • DLglossary
    • DLSitemap
    • DocInfo
    • FormLister
    • Wayfinder
    • phpthumb
    • LikeDislike
    • eForm
    • Ditto
    • multiTV
    • AjaxMegaSearch
    • AjaxSearch
    • WebLoginPE
    • Breadcrumbs
    • CodeMirror
    • AnythingRating
    • Easy Newsletter
    • FirstChildRedirect
    • OpenGraphTags
    • ddTypograph
    • TagSaver
    • BlackList
    • CfgTv
    • ModxAccount
    • Forgot Manager Login
    • GetField
    • if
    • Jot
    • ListChild
    • ListIndexer
    • ManagerManager
    • ddMMEditor
    • MaxiGallery
    • MemberCheck
    • ddGetMultipleField
    • MetaX
    • MODxBB и phpBB
    • Yams
    • Personalize
    • PHx
    • Reflect
    • tagLinks
    • TransAlias
    • TvTagCloud
    • UltimateParent
    • WebSignup
    • WebLogin
    • countViews
    • thumb
    • imageCaptor
    • optimizeJPG
    • Preview Next
    • Shopkeeper
    • SiteMap
    • Sass
    • Selector
    • SimpleGallery
    • SimpleTube
    • SimpleFiles
    • Star Rating
    • MinifyX
    • adminNav
    • SimplePolls
    • CResource
    • MODxAPI
    • customTables
    • HtmlInLine
    • HtmlMinModxEvo
    • SHKUserProfile
    • PickDocsInTree
    • evoSearch
    • editDocs
    • PageBuilder
    • HybridAuth
    • Compare
    • alterTitle
  • Виджеты
  • Уроки
  • Разработчики
  • Готовые примеры
  • Блог
  • Конфиги
  • HTML коды
© [MODX] Guru
  • Дополнения
  • avatar
    • Автор: AgelxNash
    • HtmlInLine
    • MODxAPI
    • CResource
    • MinifyX
    • DLglossary
    • DLBuildMenu
    • DocLister
    • DocInfo
    • DLCrumbs

TagSaver: Плагин для работы с тегами

  • Дополнения
  • TagSaver
1491

TagSaver: Плагин для работы с тегами

Вообще, фильтрация по тегам в MDOX реализуется довольно кисло. Представьте, у вас есть какой-то TV параметр, в котором у вас к каждой статье прописано по 10 тегов.

А в базе все эти теги для документа хранятся в одной строке. Т.е. Выглядит это примерно так

modx, теги, работа с тегами, фильтрация по тегам

Как обычно происходит фильтрация? Создается SQL запрос в таблицу site_tmplvar_contentvalues с применением WHERE value LIKE %тег%

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

  • и, р
  • ми, фи
  • льтра
  • бот
  • фил
  • mod

Более того, если документов очень много, то поиск происходит нереально долго. Как вообще идеально должна выглядеть работа с тегами?

Таблица с документами вида: 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;

Если разделителя нет, то можно оставить пустым.

Что делать если мне нужно 2 поля с тегами?

Создавайте копию плагина под другим именем с аналогичными параметрами. Только не забудьте у новой версии плагина указать разделитель и ID другого TV параметра.

Почему плагин делает SELECT за значениями TV, а не принимает $_POST['tv'.$tv]?

  1. Мы работаем в админке и ± 1 SQL запрос особого вреда не принесет
  2. Могут стоять другие плагины которые вызываются раньше и изменят значение сохраняемого TV параметра
  3. Значение TV параметра из $_POST может отличаться от сохраненного в базу

Как вывести на странице теги привязаные к текущей стрнице?

Выводить их можете как и раньше – через сниппеты 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 параметра:

  • tag – если этот параметр передан, то сниппет использует его значение. В противном случа пытается взять значение $_GET переменной tag
  • id – Идентификатор TV переменной в которой хранятся теги

Получается примерно так

[[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 параметра

  • count – сколько тегов выводить (по умолчанию 10)
  • tv – Идентификатор TV переменной в которой хранятся теги
  • tpl – чанк в который будут подставляться теги. У меня он выглядит так
    <a href="/[~11~]?tag=[[urlencode? &input=`[+name+]`]]" title="Статьи с тегом [+name+]" class="label">[+name+] ([+count+])</a>
    	

Как видно, этот сниппет не только теги подставляет, но еще и считает сколько раз они используются. Таким образом вызов сниппета для облака тегов выглядит так:

[[TagCloud? &tpl=`TagCloudItem` &tv=`7` &count=`20`]]

А что за сниппет urlencode?

return isset($input) ? urlencode($input) : '';

Для чего он нужен найдете в гугле

В силу того, что мне довольно часто приходится работать с сайтами сделаными другими программистами, то могу сказать вам одно – таких решений еще нигде небыло. Все фильтруют по старинке средствамми Ditto и изобретают свои велосипеды в виде экстендеров для него. Мое решение повзоляет вам работать с видимой частью так же, как и раньше. Зато существенно снимает нагрузку на сервер при фильтрациях. И облегчает выборку даже если вы используете знаменитый сниппет DropDownDocs для привязки статей к нескольким разделам (ведь по сути это тоже теги, только вид с боку). Соответственно, если вам нужны теги и фильтрации по TV параметрам в значении которых может быть несколько данных – используйте TagSaver.