[pdoTools] 1.9.0 - улучшения парсера и фильтрации по ТВ

Финальная версия pdoTools 1.9.0 добавляет несколько очень интересных изменений.

Сниппеты

Первое может показаться спорным, но оно довольно логичное: в сниппете pdoMenu родитель теперь определяется наличием потомков, а не параметром isfolder.

То есть, чанк tplParentRowRow будет применяться только к тем ресурсам, у которых в текущей выборке есть дети. Если нет — то это обычный ресурс, не родитель. То же касается и параметра parentClass.

Если вам принципиальна проверка по isfolder, то можно использовать фильтры вывода, или tplConditions.

Сниппет pdoNeighbors в очередной раз переписан, и снова стал быстрее. Дальше, наверное, уже не получится, если мы хотим сохранить возможность произвольной сортировки.

Теперь сначала выбираются одни только id соседей, отсекаются лишние, и дальше уже идёт работа с оставшимися. Скорость увеличилась раза в 1.5 — 2. Заметно это будет на больших каталогах, где в одном родителе от 100 ресурсов.

Ядро

По просьбе Bruno17 (автора MIGX) добавлена возможность заменять класс pdoFetch собственным. Делается это через системную настройку pdoFetch.class — в ней нужно указать новый FQN путь к классу, и он будет использоваться вместо pdoFetch.

Теперь вы можете заменить класс pdoFetch своим собственным, и все сниппеты pdoTools будут его использовать. То есть, можно расширить стандартный класс, изменить в нём какие-то методы и поменять работу pdoResources. При этом всё будет работать хорошо и после обновлений. А если что-то не так — просто верните системную настройку как было.

Можно даже динамически её менять в modX::config — чтобы использовать свой класс не постоянно, а только во время выполнения своего сниппета!

Это открывает новые возможности по написанию дополнений, например Bruno17 химичит что-то с мультиязыками через ТВ параметры.

pdoParser научился обрабатывать фильтры вывода. Выигрыша в скорости это особо не даст, но зато можно использовать условия с fastField тегами:
[[!#GET.key:is=`1`:then=`1111`:else=`2222`]]

Ну и наконец я переписал метод pdoFetch::addTVFilters(), и вы можете использовать параметр tvFilters без чудовищной деградации скорости.

Вместо генерации эпичной SQL портянки по методу getResources, запрос аккуратно разбирается и добавляется в where и includeTVs, после чего участвует в работе наравне с остальными условиями.

Например:
[[!pdoResources?
	&parents=`0`
	&tvFilters=`file!==,image!==||image==%img-0042.jpg%`
	&showLog=`1`
	&where=`{"id:>":0}`
]]
Даёт такой SQL:
WHERE  ( `modResource`.`id` > 0 AND ((`TVfile`.`value` != '' AND `TVimage`.`value` != '') OR (`TVimage`.`value` LIKE '%img-0042.jpg%')) AND `modResource`.`published` = 1 AND `modResource`.`deleted` = 0 ) 
Напоминаю, что раньше было:
WHERE  (  (  ( (EXISTS (SELECT 1 FROM `modx_site_tmplvar_contentvalues` tvr JOIN `modx_site_tmplvars` tv ON tvr.value != '' AND tv.name = 'file' AND tv.id = tvr.tmplvarid WHERE tvr.contentid = modResource.id) OR EXISTS (SELECT 1 FROM `modx_site_tmplvars` tv WHERE tv.name = 'file' AND tv.default_text != '' AND tv.id NOT IN (SELECT tmplvarid FROM `modx_site_tmplvar_contentvalues` WHERE contentid = modResource.id)) ) AND (EXISTS (SELECT 1 FROM `modx_site_tmplvar_contentvalues` tvr JOIN `modx_site_tmplvars` tv ON tvr.value != '' AND tv.name = 'image' AND tv.id = tvr.tmplvarid WHERE tvr.contentid = modResource.id) OR EXISTS (SELECT 1 FROM `modx_site_tmplvars` tv WHERE tv.name = 'image' AND tv.default_text != '' AND tv.id NOT IN (SELECT tmplvarid FROM `modx_site_tmplvar_contentvalues` WHERE contentid = modResource.id)) ) )  OR (EXISTS (SELECT 1 FROM `modx_site_tmplvar_contentvalues` tvr JOIN `modx_site_tmplvars` tv ON tvr.value LIKE '%img-0042.jpg%' AND tv.name = 'image' AND tv.id = tvr.tmplvarid WHERE tvr.contentid = modResource.id) OR EXISTS (SELECT 1 FROM `modx_site_tmplvars` tv WHERE tv.name = 'image' AND tv.default_text LIKE '%img-0042.jpg%\'' AND tv.id NOT IN (SELECT tmplvarid FROM `modx_site_tmplvar_contentvalues` WHERE contentid = modResource.id)) ) )  AND  ( `modResource`.`id` > 0 AND `modResource`.`published` = 1 AND `modResource`.`deleted` = 0 )  )
Это не я написал, так работает getResources!

В общем, как обычно, pdoTools стал еще быстрее и удобней. Новая версия уже в репозитории, можно обновляться.

Следующая заметка
[PageBreaker] Разбивка длинных текстов
Предыдущая заметка
[Markdown] Упрощаем жизнь менеджерам


Комментарии ()

  1. Иван Брежнев 11 января 2014, 22:29 # 0
    Учитывая, что новый класс легко может (и, по хорошему, должен) наследовать pdoFetch — вы можете изменить любые имеющиеся методы и дописать новые.
    А не будет ли правильнее написать интерфейс pdoFetchInterface (как вариант) или абстрактный класс, но плюс интерфейсов в том что класс может реализовывать несколько интерфейсов чего не сделать с абстрактным классом?
    1. Василий Наумкин 11 января 2014, 22:34 # 0
      Да дело не в этом.

      Дело в том, что при нынешней системе все родные сниппеты загрузят этот другой класс и будут работать с ним, как с родным.

      То есть, ты можешь изменить работу pdoResources без его изменения.
      1. Василий Наумкин 11 января 2014, 22:46 # 0
        Переписал этот момент, чтобы было понятнее, в чем идея.
      2. Евгений 13 января 2014, 02:41 # 0
        Здравствуйте. Столкнулся с такой проблемой, что pdoSitemap не хочет выводить TV-параметры.
        Вызов:
        [[!pdoSitemap? 
            &tpl=`tpl.sitemap`
            &parents=`0` 
            &showHidden=`1` 
            &showUnpublished=`0` 
            &depth=`10`
            &limit=`all`     
            &includeTVs=`tv.SEO.ChangeFreq` 
            &processTVs=`1`
            &prepareTVs=`1`
        ]]
        Чанк tpl.sitemap:
        <url>        
          <loc>[[+url]]</loc>
          <lastmod>[[+date]]</lastmod>
          <changefreq>[[+tv.SEO.ChangeFreq]]</changefreq>
          <priority>0.8</priority>
        </url>
        
        Вывод получается примерно таким:
        <url>
          <loc>http://pum-pum.ru/contacts.html</loc>
          <lastmod>2013-10-17</lastmod>
          <changefreq>[[+tv.SEO.ChangeFreq]]</changefreq>
          <priority>0.8</priority>
        </url>
        
        1. Василий Наумкин 13 января 2014, 06:12 # 0
          Точки в имени ТВ нужно заменить на что-то другое — скорее всего это ломает SQL запрос.
          1. Евгений 13 января 2014, 07:08 # 0
            Спасибо за ответ.
            Убрал точки в название шаблона и TV-параметра не помогло.
            [pdoTools 1.9.0]
            1. Василий Наумкин 13 января 2014, 07:29 # 0
              Давай еще раз:
              1. Как точно называется ТВ параметр,
              2. Покажи, что выдает сниппет с такими параметрами:
              [[!pdoSitemap?
              	&forceXML=`0`
              	&showLog=`1`
              	&includeTVs=`Твой ТВ параметр`
              ]]
              
              3. Есть ли ошибки в системном журнале?

              P.S. Ничего не нужно, нашел ошибку. Сейчас будет обновление.
              1. Василий Наумкин 13 января 2014, 07:37 # 0
                Обновляйся, починил.

                С точками в имени ТВ нормально работает, кстати.
                1. Евгений 13 января 2014, 07:52 # 0
                  Обновился, но работает как-то не так…
                  Вызов:
                  [[!pdoSitemap?
                      &tpl=`tpl.Sitemap`
                      &forceXML=`1`
                      &showLog=`1`
                      &includeTVs=`tv.SEO.ChangeFreq,tv.SEO.Priority`
                      &processTVs=`1`
                  ]]
                  Результат:
                  <url>
                      <loc>http://it-job4you.ru/index/site-tour.html</loc>
                      <lastmod>2013-10-18</lastmod>
                      <changefreq>[[+tv.SEO.ChangeFreq]]</changefreq>
                      <priority>[[+tv.SEO.Priority]]</priority>
                  </url>
                  Вызов:
                  [[!pdoSitemap?
                      &tpl=`tpl.Sitemap`
                      &forceXML=`0`
                      &showLog=`1`
                      &includeTVs=`tv.SEO.ChangeFreq,tv.SEO.Priority`
                      &processTVs=`1`
                  ]]
                  
                  Результат:
                  <url>
                      <loc>http://it-job4you.ru/</loc>
                      <lastmod>2014-01-13</lastmod>
                      <changefreq/>
                      <priority/>
                  </url>
                  
                  Если не указывать шаблон — он выдается все хорошо, но не понятно откуда он берет значения частоты и приоритета.
                  Журнал ошибок пустой.
                  1. Василий Наумкин 13 января 2014, 08:04 # 0
                    А, ясно что ты хочешь сделать.

                    pdoSitemap работает по логике GoogleSitemap, а там можно указать только один &priorityTV, который будет показан в теге priority.
                    То есть, ТВ там не поддерживаются.

                    Сейчас поменяю.
                    1. Василий Наумкин 13 января 2014, 08:30 # 0
                      Обновляйся еще раз, теперь ТВ можно выводить где угодно.
                      1. Евгений 13 января 2014, 08:34 # 0
                        Спасибо, отлично работает :)
            2. Павел Левин 13 января 2014, 11:55 # 0
              Хорошая работа, давно не был тут, Василий молодец, как всегда).
              1. Пётр Молчанов 13 января 2014, 16:48 # 0
                У меня после сегодняшнего обновления почему-то стали выводиться не все записи. Вызов такой: [[pdoResources? &parents=`13` &tpl=`my.ShablonRowTpl` &limit=`100` &includeTVs=`shap_type` &tvFilters=`shab_type==книга` &sortby=`{«publishedon»:«ASC»,«createdon»:«DESC»}`]]
                Вот &showLog=`1`:
                0.0001380: pdoTools loaded
                0.0000410: xPDO query object created
                0.0000539: Added TVs filters
                0.0006568: Included list of tvs: shab_type
                0.0004289: leftJoined modTemplateVarResource as TVshab_type
                0.0005732: Added selection of modResource: SQL_CALC_FOUND_ROWS `id`, `type`, `contentType`, `pagetitle`, `longtitle`, `description`, `alias`, `link_attributes`, `published`, `pub_date`, `unpub_date`, `parent`, `isfolder`, `introtext`, `richtext`, `template`, `menuindex`, `searchable`, `cacheable`, `createdby`, `createdon`, `editedby`, `editedon`, `deleted`, `deletedon`, `deletedby`, `publishedon`, `publishedby`, `menutitle`, `donthit`, `privateweb`, `privatemgr`, `content_dispo`, `hidemenu`, `class_key`, `context_key`, `content_type`, `uri`, `uri_override`, `hide_children_in_tree`, `show_in_tree`, `properties`
                0.0000179: Added selection of TVshab_type: IFNULL(`value`, 'книга') AS `tv.shab_type`
                0.0000761: Replaced TV conditions
                0.0009339: Processed additional conditions
                0.0014889: Added where condition: 0=`TVshab_type`.`value` LIKE 'книга', modResource.parent:IN(13,14,15,16,19,20,21,22,23,24,25,26,27,28,29,30,42,43,102,105,107,118,132,136,143,152,278,284,330,345,348,360,364), modResource.published=1, modResource.deleted=0
                0.0000651: Replaced TV conditions
                0.0001099: Sorted by publishedon, ASC
                0.0000060: Sorted by createdon, DESC
                0.0000081: Limited to 100, offset 0
                0.0003979: SQL prepared "SELECT SQL_CALC_FOUND_ROWS `modResource`.`id`, `modResource`.`type`, `modResource`.`contentType`, `modResource`.`pagetitle`, `modResource`.`longtitle`, `modResource`.`description`, `modResource`.`alias`, `modResource`.`link_attributes`, `modResource`.`published`, `modResource`.`pub_date`, `modResource`.`unpub_date`, `modResource`.`parent`, `modResource`.`isfolder`, `modResource`.`introtext`, `modResource`.`richtext`, `modResource`.`template`, `modResource`.`menuindex`, `modResource`.`searchable`, `modResource`.`cacheable`, `modResource`.`createdby`, `modResource`.`createdon`, `modResource`.`editedby`, `modResource`.`editedon`, `modResource`.`deleted`, `modResource`.`deletedon`, `modResource`.`deletedby`, `modResource`.`publishedon`, `modResource`.`publishedby`, `modResource`.`menutitle`, `modResource`.`donthit`, `modResource`.`privateweb`, `modResource`.`privatemgr`, `modResource`.`content_dispo`, `modResource`.`hidemenu`, `modResource`.`class_key`, `modResource`.`context_key`, `modResource`.`content_type`, `modResource`.`uri`, `modResource`.`uri_override`, `modResource`.`hide_children_in_tree`, `modResource`.`show_in_tree`, `modResource`.`properties`, IFNULL(`TVshab_type`.`value`, 'книга') AS `tv.shab_type` FROM `hpmo_site_content` AS `modResource` LEFT JOIN `hpmo_site_tmplvar_contentvalues` `TVshab_type` ON `TVshab_type`.`contentid` = `modResource`.`id` AND `TVshab_type`.`tmplvarid` = 4 WHERE  ( `TVshab_type`.`value` LIKE 'книга' AND `modResource`.`parent` IN (13,14,15,16,19,20,21,22,23,24,25,26,27,28,29,30,42,43,102,105,107,118,132,136,143,152,278,284,330,345,348,360,364) AND `modResource`.`published` = 1 AND `modResource`.`deleted` = 0 )  ORDER BY publishedon ASC, createdon DESC LIMIT 100 "
                0.0018771: SQL executed
                0.0001860: Total rows: 14
                0.0001800: Rows fetched
                0.0022120: Prepared and processed TVs
                0.0013540: Loaded chunk "my.ShablonRowTpl"
                0.0176990: Returning processed chunks
                0.0264568: Total time
                11 796 480: Memory usage
                че-то много парентов цепляет…
                1. Василий Наумкин 13 января 2014, 16:58 # 0
                  Потому что теперь ТВ параметры должны физически находиться в таблице, а не быть «по умолчанию.»

                  Родителей ограничивает параметр depth.
                  1. Пётр Молчанов 13 января 2014, 17:02 # 0
                    Ага, похоже понял. А псевдоним теперь всегда будет заменяться на айдишник?
                    1. Василий Наумкин 13 января 2014, 17:05 # 0
                      Это ты про что?
                      1. Пётр Молчанов 13 января 2014, 17:07 # 0
                        при сохранении поле «псевдоним (alias)» у меня всегда перезаписывается на id ресурса. всегда
                        1. Василий Наумкин 13 января 2014, 17:23 # 0
                          Это может быть только у тикетов или товаров, отключается в нсистемных астройках.
                          1. Пётр Молчанов 13 января 2014, 17:50 # 0
                            А, ок. Спасибо. Раньше такого не было
                            1. Василий Наумкин 13 января 2014, 18:06 # 0
                              Именно для этого я и пишу список изменений. bezumkin.ru/sections/components/2409/
                              1. Пётр Молчанов 14 января 2014, 10:15 # 0
                                Из них совсем не понятно как это отразится на текущей ситуации :) Еще такой вопрос, включив твой парсер можно смело удалить дополнение fastField?
                                1. Василий Наумкин 14 января 2014, 10:19 # 0
                                  Да.

                                  Система может использовать только один парсер.
                2. Kirill Bedin 15 января 2014, 12:41 # 0
                  То есть, чанк tplParentRowRow будет применяться только к тем ресурсам, у которых в текущей выборке есть дети
                  А как сделать, чтобы этот чанк нормально работал с параметром &hideSubMenus=`1`? Я так понял, что потомки выбираются только у текущего ресурса, следовательно все остальные ресурсы обрабатываются сниппетом без потомков и к ним применяется чанк &tpl.
                  1. Василий Наумкин 16 января 2014, 09:54 # 0
                    Если вам принципиальна проверка по isfolder, то можно использовать фильтры вывода, или tplConditions.
                    Можно прямо в чанке &tplRow проверять:
                    <li class="[[+classnames]] [[+isfolder:is=`1`:then=`folder`:else=``]]">...</li>
                    Ну а дальше на css оформляешь, как нужно.
                  2. Евгений 21 января 2014, 05:23 # 0
                    У Вас ошибка в описании:
                    [[!GET.key:is=`1`:then=`1111`:else=`2222`]]
                    Должно:
                    [[#GET.key:is=`1`:then=`1111`:else=`2222`]]
                    1. Андрей Рябченко 24 марта 2014, 21:27 # 0
                      есть такой пакет SEOTab
                      он создает красивые удобные переключатели для управления SEO параметрами документов на отдельной закладке.

                      в таблице modx_site_content
                      к ресурсам добавляет поле:
                      properties
                      {"stercseo":{"index":"1","follow":"1","sitemap":"1","priority":"0.5","changefreq":"","urls":""}}
                      прошу помощи в составлении запроса для вывода только документов с «sitemap»:«1» через pdoSitemap
                      делаю так, но не выбирает:
                      $sitemap = $modx->runSnippet('pdoSitemap',array(
                         'forceXML' => '0',
                         'parents' => '0',
                         'depth' => '20',
                         'limit' => '0',
                         'showHidden' => '1',
                         'showUnpublished' => '0',
                         'where' => '{"stercseo":{"sitemap":"1"}}'
                      ));
                      
                      1. Василий Наумкин 24 марта 2014, 21:36 # 0
                        И не выберет. Дополнительные свойства хранятся в БД в виде JSON строки с массивами.

                        Можно попробовать так:
                        'where' => '{"properties:LIKE":"%\"sitemap\":\"1\"%"}'
                        1. Андрей Рябченко 24 марта 2014, 21:40 # 0
                          да так отбирает правильно, но показывает всего один документ.
                          насколько я вижу выводит первый попавшийся с таким параметром и всё.
                          1. Андрей Рябченко 24 марта 2014, 22:01 # 0
                            увидел косяк. в значении не всегда единица. еще :null было.

                            работает так:
                            'where' => '{"properties:NOT LIKE":"%\"sitemap\":\"0\"%"}'
                            благодарю, Василий
                      2. Stanislav Krezz 29 мая 2014, 12:29 # 0
                        Решил заменить сниппет renderResources на pdoResources. Проверил параметры, заменил, получил ошибку следующего вида:
                        Fatal error: Allowed memory size of 33554432 bytes exhausted (tried to allocate 1403 bytes) in /home/u37207/u37207.netangels.ru/www/core/xpdo/xpdo.class.php on line 2489
                        
                        Fatal error: Undefined class constant 'LOG_LEVEL_ERROR' in /home/u37207/u37207.netangels.ru/www/core/model/modx/error/moderrorhandler.class.php on line 111
                        Задача: вывод дочерних ресурсов на одной странице в шаблоне, который присвоен каждому из ресурсов.
                        Пробовал переустановить pdoTools — не помогло. Логи пустые.

                        Вызов сниппета:
                        [[!pdoResources? &parents=`23` &includeContent=`1` &showLog=`1` &tpl=`@TEMPLATE`]]
                        Что я делаю не так?
                        1. Василий Наумкин 29 мая 2014, 12:41 # 0
                          Скорее всего у тебя в шаблоне какого-то ресурса есть вызов сниппета, который выбирает этот ресурс, и вся конструкция уходит в рекурсию, отчего заканчивается память.

                          Проверяй шаблоны ресурсов.
                          1. Stanislav Krezz 29 мая 2014, 12:49 # 0
                            Спасибо за ответ!
                            <div style="background: white">
                            <h1>[[*pagetitle]]</h1>
                            <p>[[*content]]</p>
                            </div>
                            Я думал об этом, но шаблон примитивный, тестовый. Про контроль за рекурсией уяснил из документации.
                            Вот так выглядит шаблон ресурса:
                            <div style="background: white">
                            <h1>[[*pagetitle]]</h1>
                            <p>[[*content]]</p>
                            </div>
                            1. Василий Наумкин 29 мая 2014, 12:57 # 0
                              Попробуй убрать content — наверняка в одном из документов что-то там и вызывается.

                              Ну и звездочка означает вывод полей текущего документа, а не выбираемого.
                              1. Stanislav Krezz 29 мая 2014, 13:13 # 0
                                Действительно: стоило изменить звездочки на плюсы и все получилось. Получалось, что контент родительского ресурса есть сниппет, таким образом рекурсивный вызов сниппета pdoResources. Прошу извинить за глупый вопрос(
                        2. Володя 10 июня 2014, 19:34 # 0
                          вопрос по pdoMenu.
                          если указать &context=`web` то указание &scheme игнорируется…
                          так и должно быть возможно.
                          Но как то можно формировать ссылки с одного контекста, но чтоб они были относительные?
                          пробовал обработать link с помощью prepareSnippet — но как то странно, только ссылка на главную обрабатывается…
                          1. Володя 10 июня 2014, 19:53 # 0
                            c prepareSnippet я тупанул — с ним все нормально…
                          2. le-genda 24 июля 2014, 12:12 # 0
                            Василий, скажите пожалуйста, можно ли в сниппете pdoField использовать параметр &toSeparatePlaceholders? Он у вас значится общим для всего pdoTools, но у меня не работает. У меня на входе в tv — Param есть такой список
                            название 1==значение 1||название 2==значение 2||название 3==значение 3
                            В итоге хочу получить
                            [[+param1]] - название 1==значение 1
                            [[+param2]] - название 2==значение 2
                            [[+param3]] - название 3==значение 3
                            
                            мой вызов сниппета
                            [[pdoField?
                            	&id=`[[*id]]`
                            	&field=`param`
                            	&toSeparatePlaceholders=`param`
                            ]]
                            
                            Что я не поняла и неправильно делаю?
                            1. Василий Наумкин 24 июля 2014, 12:33 # 0
                              pdoField возвращает значение одного поля документа. Какие separate placeholders могут быть?

                              Тебе здесь нужно разбирать значение своего ТВ отдельным сниппетом, pdoTools это за тебя не сделает.
                              $modx->setPlacehlders(explode('||', $input), 'param.');
                              1. le-genda 24 июля 2014, 12:42 # 0
                                а почему тогда &toSeparatePlaceholders написан как общий параметр для всего pdoTools?
                                и в каком случае и с каким сниппетом можно использовать &toSeparatePlaceholders?
                                или может быть вы знаете готовое решение, которое разбивает tv список на несколько плэйсхолдеров?

                                и спасибо за ответ.

                                Тебе здесь нужно разбирать значение своего ТВ
                                я бы с радостью ), только бы знать еще чем разбирать. в php не понимаю ничего, я верстальщик, могу только пользоваться готовым.
                                1. Василий Наумкин 24 июля 2014, 12:56 # 0
                                  Нужно добавить свой фильтр вывода для обработки получаемой строки:
                                  [[pdoField:explodeTV?
                                  	&id=`[[*id]]`
                                  	&field=`param`
                                  ]]
                                  
                                  [[+param.0]]
                                  [[+param.1]]
                                  [[+param.2]]

                                  Сниппет explodeTV:
                                  <?php
                                  $modx->setPlaceholders(explode('||', $input), 'param.');
                                  return ' ';
                                  1. le-genda 24 июля 2014, 13:08 # 0
                                    Офигеть! Работает )!!! Спасибо большое )!
                            Добавление новых комментариев отключено.