TagSaver

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

    Плагін 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.html?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.

    Автор: Agel_Nash