[pdoTools] pdoResources 1.4.1

Продолжаю догонять и перегонять getResources. Со скоростью, понятно, вопросов нет, а вот функционал реализован еще не весь.

Сегодняшнее обновление привносит следующие параметры:
  • conditionalTpls — то есть, выбор чанков в зависимости от определённых условий.
  • toSeparatePlaceholders — выставление всех результатов в разные плейсхолдеры.
  • select — этого в аналогах нет, возможность указать список полей для выборки.
Далее подробности, по порядку.

conditionalTpls

Не все знают, что в getResources есть возможность использовать разные чанки, в зависимости от значения в каком-то одном поле ресурса.

Например, если не заполнен introtext, можно указать чанк без его использования. Проверять можно не только на пустоту, но на равенство, больше, меньше и null.

Для этого используются 3 параметра:
&tplCondition — Поле ресурса, из которого будет получено значение для выбора чанка по условию в «conditionalTpls». Например: introtext или published.
&tplOperator — Оператор сравнения, возможный список:
==, !=, < , > , <=, >=, empty, !empty, isnull
&conditionalTpls — JSON строка с массивом, у которого в ключах указано то, с чем будет сравниваться «tplCondition», а в значениях — чанки, которые будут использованы для вывода, если сравнение будет успешно.

Как это работает? Рассмотрим на примере:
[[!pdoResources?
	&parents=`2`
	&tpl=`chunk1`
	&tplCondition=`template`
	&tplOperator=`==`
	&conditionalTpls=`{"2":"chunk2", "5":"chunk5"}`
]]

В результате работы, все ресурсы с шаблонами 3 и 5 будут вывеедены в собственных чанках, а все остальные — в chunk1.

Для проверки на пустоту можно указывать массивы без ключей:
[[!pdoResources?
	&parents=`2`
	&tpl=`chunk1`
	&tplCondition=`introtext`
	&tplOperator=`empty`
	&conditionalTpls=`["no_intro"]`
]]

Для ресурсов с пустым introtext будет задействован чанк no_intro.

toSeparatePlaceholders

Этот параметр указывает, что результаты нужно выставить в несколько плейсхолдеров. Плейсхолдеры именуются из значения этого поля и индекса строки, начиная с нуля.

Пример:
[[!pdoResources?
	&parents=`2`
	&toSeparatePlaceholders=`row`
]]
В результате мы получим на странице возможные плейсхолдеры [[+row0]], [[+row1]] и т.д.

Не знаю, кому это понадобится, но для совместимости с getResources — сделал.

select

Этого функционала в getResources нет, но я решил добавить, для удобства.

Теперь можно указывать, какие именно поля участвуют в выборке. Простой пример:
[[!pdoResources?
	&select=`id,pagetitle,content`
]]

Учитывая, что мы работаем с pdoTools, можно указывать и посложнее:
[[!pdoResources?
	&leftJoin=`{
		"120x90": {
			"class":"msResourceFile"
			,"alias":"120x90"
			, "on": "120x90.resource_id = modResource.id AND 120x90.path LIKE '%/120x90/' AND 120x90.rank=0"
		}
	}`
	&select=`{
		"modResource":"id,pagetitle,content"
		,"120x90":"120x90.url as 120x90"
	}`
]]

То есть, вы можете присоединять к ресурсам любые таблицы, и указывать, что именно из них выбирать. Такого вообще нигде нет, только в pdoTools.

Заключение

По сравнению с getResources теперь нет следующих параметров:
  • @FILE и @INLINE чанки — не уверен, что кто-то этим пользуется
  • &tvFilters — не реализовано, но работает при указании ТВ в &where, например
    [[!pdoResources? &includeTVs=`test` &where=`{"test:!=":""}`]]
  • &sortbyTV, &sortdirTV и &sortbyTVType — не реализовано, но работает при указании сортировки в обычном &sortby, например:
    [[!pdoResources? &includeTVs=`test` &sortby=`{"test":"desc"}`]]
  • &prepareTVs, &prepareTVList, &processTVs, &processTVList — этого нет, и скорее всего, не будет.
  • &debug — есть гораздо более мощный и удобный &showLog=`1`
  • &sortbyAlias, &sortbyEscaped — не знаю, зачем они.

Возможно (возможно) сделаю позже параметры &tvFilters, &sortbyTV, &sortdirTV и &sortbyTVType, для еще более плавной замены getResources на pdoResources.

Следующая заметка
[Tickets] Подсчет популярности
Предыдущая заметка
[jsLock] Скрипт блокировки сайта


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

  1. Владимир 18 июля 2013, 15:08 # 0
    Доброго дня!
    Обновил на одном из сайтов, в скорости есть малюююююююсенький проигрыш. Откатывал дважды назад что бы убедиться. Было из кэша 0,5182 s (стабильно, сразу после перезагрузи страницы), теперь 0,5579 s
    Но в сравнении с тем что было на getResources и getProducts это даже смешно обсуждать.
    Спасибо, успехов!
    ЗЫ Не прими за брюзжание:))
    1. Василий Наумкин 18 июля 2013, 18:48 # 0
      Может быть.

      Ведь я добавляю новые плюшки — они могут немного замедлять работу.
      1. Владимир 19 июля 2013, 18:57 # 0
        Вот что то даже откатив назад не пойму +tv.img:rezimgcrop=`r-150x0` в чанке tpl должно работать? Почему то мне кажется что работало, но сейчас точно нет.
          1. Владимир 19 июля 2013, 23:00 # 0
            Ой, ..)
            Благодарю, Василий!
            Префикс, да, конечно же, совсем упустил из виду.
            1. Владимир 21 августа 2013, 16:20 # 0
              кроме префикаса
              как оказалось, фильтр rezimgcrop не будет работать в чанках с pdoResources если тивишка изображения связанна с источником файлов в котором путь отличается от корня
              1. Василий Наумкин 21 августа 2013, 16:22 # 0
                Будет, если ты в чанке дополнишь путь до файла.
                1. Владимир 21 августа 2013, 16:33 # 0
                  Я, конечно же, добавлял путь до полного, и pdoResources прекрасно справляется в выводом изображения и т.п, но дело в том что именно фильтр rezimgcrop в этом случае не реагирует (т.е. вообще ни как) если только в самом tv указан источник файлов с путем отличающимся от корня, т.е. изображения выводятся, но исходные. Стоит только вернуть пути от корня или дефолтный источник файлов для тивишки — все работает, создается папка со сгенерированными изображениями и т.п. Многократно перепроверил, прежде чем коммент сюда написать, чеснслово))
                  1. Василий Наумкин 21 августа 2013, 16:39 # 0
                    Да я не про то.

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

                    Я, конечно, не разбирался, но по идее так.
                    Проверить ты можешь очень просто: убери вызов rezimgcrop и выводи ТВ просто текстом — увидишь реальные пути.
      2. Василий Краковецкий 18 июля 2013, 15:29 # 0
        @FILE никогда не пользовался, а вот @INLINE часто полезно, потому что считаю излишним создавать целый чанк ради конструкции
        <div>[[+pagetitle]]</div>

        еще из перечисленного пользовался processTVList, тоже стоило бы добавить в отдаленные перспективы.
        1. Василий Наумкин 18 июля 2013, 18:49 # 0
          Это работает только при указании в наборах параметров, иначе парсер туда раньше подставит значения.

          Но, наверное надо сделать, чисто для порядка.
        2. Суренков Савва 18 июля 2013, 16:09 # 0
          Пришлось (возможно, лишь пока) вернуться на getResources:
          [[!getPage:default="К сожалению, раздел пока не заполнен."?
               &element=`pdoResources`
               &parents=`[[*id]]`
               &tpl=`tpl.msContainers.row`
               &limit=`24`
               &depth=`0`
               &includeTVs=`category.new,category.thumb`
               &sortby=`{"category.new": "desc", "publishedon": "asc"}`
               &showLog=`1`
          ]]
          
          category.new может быть 0 или 1. Сортироваться должно: сначала category.new == 1, затем по дате публикации по убыванию. Но в итоге всё оказывается отсортированным по дате. в getResources лечилось указанием &sortbyTVType=`integer`. Как лечить здесь — пока не знаю. (P.S.: от порядка сортировки не зависит, всё сортируется по дате в любом случае).
          PS
          <li class="product yui3-u-5-24">
              <a href="[[~[[+id]]]]">
                  [[+tv.category.new:is=`1`:then=`<span class="new-product">Новинка</span>`]]
              	<img alt="[[+pagetitle]]" src="[[+tv.category.thumb]]" />
          	</a>
          	<div class="form">
          		<a class="prod" href="[[~[[+id]]]]">
          			<span class="description">[[Jevix?input=`[[+pagetitle]]`]]</span>
          		</a>
          	</div>
          </li>
          
          [[+tv.category.thumb]] (тип — изображение, вывод — по умолчанию) — вместо полного пути до файла выводит лишь его имя.
          Вот как-то так…
          1. Владимир 18 июля 2013, 17:59 # 0
            кстати, так &sortby=`TVимятиви.value` сортировка работает
            1. Василий Наумкин 18 июля 2013, 18:49 # 0
              Возможно, это из-за точек в именах ТВ.

              Включи &showLog=`1` и посмотри, какой SQL получается.
            2. Виктор Долгий 18 июля 2013, 16:54 # 0
              После установки отваливается вывод фильтров и результатов mFilter2 и вывод результатов mSearch2…
              1. Виктор Долгий 18 июля 2013, 18:01 # 0
                [2013-07-18 15:53:16] (ERROR @ /index.php) Intro::getSelectColumns() is not a valid static method.
                [2013-07-18 15:53:16] (ERROR @ /index.php) [pdoTools] Error 42S22: Unknown column '' in 'field list'

                В логах :)
                1. Василий Наумкин 18 июля 2013, 18:50 # 0
                  Ага, поправил, обновись.
                  1. Виктор Долгий 18 июля 2013, 19:13 # 0
                    Поиск заработал, фильтр — нет. На всякий удалил старые, почистил кэш, теперь остался без магазина 0о.
                    1. Виктор Долгий 18 июля 2013, 19:14 # 0
                      Лог: [2013-07-18 18:13:00] (ERROR @ /index.php) Could not load class: Intro from mysql.intro.
                      Лог не от этого :)
                      1. Василий Наумкин 18 июля 2013, 19:34 # 0
                        От этого тоже.
                        В общем, еще разок обновись, у меня на тесте все работает: и поиск и фильтры.

                        Если нет — завтра разберемся, сейчас уже сил нет.
                        1. Виктор Долгий 18 июля 2013, 21:01 # 0
                          Безрезультатно :(
                          [2013-07-18 18:18:07] (ERROR @ /assets/components/msearch2/action.php) Could not load class: Intro from mysql.intro.
                          1. Виктор Долгий 19 июля 2013, 11:49 # 0
                            На 1.4.0 работает.
                            1. Василий Наумкин 19 июля 2013, 12:59 # 0
                              Пришли логин\пароль на bezumkin@ya.ru, посмотрю, что там у тебя.
                              1. Виктор Долгий 19 июля 2013, 13:12 # 0
                                Отправил.
                                1. Василий Наумкин 19 июля 2013, 13:41 # 0
                                  Проблема была в returnIds — пофиксил и обновил pdoTools.

                                  Теперь всё работает, проверяй.
                                  1. Виктор Долгий 19 июля 2013, 13:42 # 0
                                    Маэстро, спасибо ;)
                2. Yana V 18 июля 2013, 17:18 # 0
                  delete
                  1. Daniel Gibson 28 июля 2013, 17:54 # 0
                    I'm having trouble getting pdoResources conditional templates to work with tplCondition set to 'template'

                    For example:

                    &tplCondition=`template`
                     &conditionalTpls=`{"1":"tplA","2":"tplB","3":"tplC"}`
                    This feature was only introduced to getResources in v1.5.0

                    Can you please confirm 'template' is also a supported condition in pdoResources?

                    Thanks.
                    1. Василий Наумкин 28 июля 2013, 19:48 # 0
                      Yes, it works for me okay.

                      «Template» and any other field of resource is working and no matter when it was introduced in getResources.

                      You may look into source code of defineChunk function, that used in pdoTools. Here is conditionalTpls block.
                      1. Daniel Gibson 28 июля 2013, 20:09 # 0
                        I'll have to look into it further, I can't it work even though it works fine through getResources for me.

                        The call I'm using is:

                        [[!getPage@NewNav?
                           &elementClass=`modSnippet`
                           &element=`pdoResources`
                           &depth=`0`
                           &limit=`20`
                           &pageVarKey=`page`
                           &includeTVs=`exhibition_start_date,exhibition_end_date,featured_image,weblink,vimeo`
                           &includeContent=`1`
                           &sortby=`{"menuindex":"ASC"}` 
                           &tpl=`news_list_Tpl` 
                           &tplCondition=`template`
                           &conditionalTpls=`{"7":"news_list_Tpl","8":"news_list_video_Tpl","19":"news_list_quote_Tpl","23":"news_list_link_Tpl"}`
                        ]]
                        1. Василий Наумкин 28 июля 2013, 20:16 # 0
                          And what do you see?

                          All resources with template id = 7 must use chunk news_list_Tpl
                          If they have template id = 8, then chunk is news_list_video_Tpl
                          and so on.

                          If it also not works in getResources — so the problem not in snippet.
                          1. Daniel Gibson 28 июля 2013, 20:18 # 0
                            It works in getResouces.

                            With pdoResouces they all use &tpl=`news_list_Tpl` and ignore the &conditionalTpls
                            1. Василий Наумкин 28 июля 2013, 20:21 # 0
                              Can you email me credentials for your site, i want to look at?

                              bezumkin@ya.ru
                              1. Василий Наумкин 28 июля 2013, 20:46 # 0
                                The problem was in static property of snippet, and your advanced configuration.

                                It seems that now it works.
                      2. Виталий Серый 31 июля 2013, 01:52 # 0
                        Приветствую.
                        Спасибо за сниппет!
                        При вызове на странице более одного сниппета не «обнуляется» idx.
                        Так же установка данного параметра в вызове для насильного обнуления счетчика ничего не дало.
                        Для себя исправил добавлением в pdofetch.class.php
                        if (!empty($this->config['idx'])) {
                            $this->idx = (integer) $this->config['idx'];
                        }
                        1. Василий Наумкин 31 июля 2013, 07:40 # 0
                          Ага, есть такое.

                          Вроде пофиксил, проверь на досуге.
                          1. Виталий Серый 01 августа 2013, 01:25 # 0
                            Ничего лишнего, все отлично работает.
                            Спасибо.
                        2. Cyrax_02 06 августа 2013, 20:45 # 0
                          Есть множественное поле tv1, значением которого является
                          `1||5||9||23||56||85||115`.
                          Как с помощью стандартных средств фильтрации (без использования &resources=`[[собственныйСниппет]]`) реализовать отбор тех ресурсов, которые имеют значение `5` в этом TV-поле?
                          Параметр where принимает JSON-строку. Но в формате JSON можно состряпать только такое условие:
                          {«tv1=»:"&5&"}
                          Но такое условие вернёт также ресурсы, tv1 которых содержит значения `56`, `85`, `115`.

                          Здесь нужно получить вот такое условие в SQL:
                          '||' & modx_site_tmplvar_contentvalues.value & '||' LIKE '%||' & tv1 & '||%'
                          Но как это «затолкать» в JSON и xPDO?

                          ===============================================================
                          Вот такой вариант почему-то некорректно работает:
                          &where=`{"tv1":"2","OR:tv1:LIKE":"2||%","OR:tv1:LIKE":"%||2","OR:tv1:LIKE":"%||2||%"}`
                          ==============================================
                          Потестировал ещё раз.
                          Четырём ресурсам для TV-параметра testTV задал значения `2||4`, `2||5`, `7`, `8`
                          Далее использую pdoResources с параметром:
                          &where=`{"testTV:LIKE":"%2%","OR:testTV:LIKE":"%5%","OR:testTV:LIKE":"%7%"}`
                          Поэкспериментировал, проверил: все эти 3 условия работают как «ИЛИ». Всё правильно.

                          Далее добавляю 4-е условие (тоже через «ИЛИ»):
                          &where=`{"testTV:LIKE":"%2%","OR:testTV:LIKE":"%5%","OR:testTV:LIKE":"%7%","OR:testTV:LIKE":"%8%"}`
                          В итоге получаем 3 ресурса, которые соответствуют первым 3 условиям. Ресурса с testTV=`8` нет.

                          Если поменять местами 3-е и 4-е условия:
                          &where=`{"testTV:LIKE":"%2%","OR:testTV:LIKE":"%5%","OR:testTV:LIKE":"%8%","OR:testTV:LIKE":"%7%"}`
                          то получим 3 ресурса, которые опять-таки удовлетворяют только 3-м первым условиям. Ресурса с testTV=`7` в выборке нет.

                          ==================================================================================
                          Т.е. сниппет ведёт себя так, как будто 4-го условия нет вообще.
                          Может, имеет место какое-то ограничение на число условий? Т.е. в &where учитываются всего 3 условия?
                          1. Василий Наумкин 06 августа 2013, 23:41 # 0
                            В параметр &where можно писать и чистый SQL:

                            [[!pdoResources?
                            	&where=`["
                            		TVtest.value LIKE %2% OR TVtest.value LIKE %5% OR TVtest.value LIKE %7%
                            	"]`
                            ]]

                            Контролировать правильность составления запроса нужно включая лог — &showLog=`1`
                            1. Василий Наумкин 06 августа 2013, 23:48 # 0
                              Ну а по поводу JSON — у тебя массив с тремя одинаковыми ключами.

                              Вот что выходит, если его проверить вот тут.
                            2. Cyrax_02 07 августа 2013, 00:41 # 0
                              Спасибо, просветили. Значит, фрагмент до `:` (ключ) должен быть уникален и если будут присутствовать одинаковые ключи, в итоге остаётся только последнее условие с таким ключом.

                              Из вики:
                              В JSON используются их следующие формы:
                              Объект — это неупорядоченное множество пар имя/значение, заключённое в фигурные скобки { }. Между именем и значением стоит символ ": ", а пары имя/значение разделяются запятыми.

                              Моё условие
                              &where=`{"tv1":"2","OR:tv1:LIKE":"2||%","OR:tv1:LIKE":"%||2","OR:tv1:LIKE":"%||2||%"}`
                              после вычисления принимает вид:
                              {
                                  "tv1":"2",
                                  "OR:tv1:LIKE":"%||2||%"
                              }
                              
                              Получается, что в формате JSON моё условие никак не записать. Только нативный SQL.
                              1. Василий Наумкин 07 августа 2013, 05:47 # 0
                                ["TVtest.value LIKE %2% OR TVtest.value LIKE %5% OR TVtest.value LIKE %7%"]
                                Строго говоря, это тоже JSON. Просто тут массив с одной строкой без указания ключа.
                                1. Cyrax_02 07 августа 2013, 12:56 # 0
                                  Понятно. Квадратные скобки — признак массива (в терминологии JSON). А массив в JSON — один из возможных вариантов записи значения. В итоге получаем JSON-набор, состоящий из одной пары «ключ-значение», при этом ключ не указан, а значение представлено массивом из 1 элемента.

                                  Таким образом, моё изначальное условие будет выглядеть так:
                                  &where=`["CONCAT('||', TVtv1.value, '||') LIKE CONCAT('%||', '5', '||%')"]`
                                  Это более корректный вариант условия:
                                  &where=`{"tv1:LIKE":"%5%"}`
                                  Надеюсь, что на большом числе ресурсов, этот LIKE+CONCAT по скорости не подведёт. Иначе придётся выносить множественные TV в отдельную таблицу со всеми вытекающими.

                                  P.S. В Вашем примере фрагменты %5%, %7%, %5% нужно взять в квадратные скобки.
                                  P.S2. Данное условие (LIKE+CONCAT) будет нормально отрабатываться и в MS SQL, т.к. указанные SQL-функции в этих СУБД идентичны.
                              2. Cyrax_02 07 августа 2013, 13:17 # 0
                                Ещё пара вопросов:
                                1. Вариант с непосредственным указанием в качестве условия SQL-фрагмента в общем случае не соответствует xPDO. Непосредственно пишется нативный SQL-фрагмент, а не формируется посредством xPDO. И как следствие, в общем случае такой вариант не будет работать в других СУБД. Есть ли возможность построить данное условие в универсальном виде — через xPDO? Т.к. весь JSON полностью отображается (проходит) через xPDO, то если условие удастся корректно «затолкать» в JSON, то задача будет решена. Либо иначе.

                                2. У Вас при формировании итогового запроса формируется, в частности, условие:
                                parent IN [список id ресурсов]
                                И в этот список попадают также и все ресурсы-листья (не имеющие дочерних ресурсов). Почему бы не исключить из этого списка листья, которые ни для кого заведомо не будут являться parent'ом? Ведь этот длинный список в запросе прогоняется по всем записям. А если в списке будет 1000 идентификаторов? А если 1 000 000?

                                3. При написании вышеприведённого условия (LIKE+CONCAT) имют место 3 разных написания имён TV-параметров одновременно:
                                а) в параметре &includeTVs указываем их в формате имяТМ (без всяких префиксов)
                                б) в чанке-шаблоне указываем их в формате tv.имяТМ (префикс — в зависимости от значения параметра &tvPrefix)
                                в) в условии where указываем имя JOIN-таблицы как TVимяТV
                                Т.е. получаем 3 разных написания, в коих можно запутаться. Да и не очень это удобно. Было бы хорошо свести это число до 2 (считаем, что префикс tvPrefix не должен быть пустым во избежание конфликтов имён дополнительных TV-полей и стандартных полей ресурсов в итоговом запросе).
                                1. Василий Наумкин 07 августа 2013, 14:29 # 0
                                  Тебе нужно писать собственные сниппеты, с такими запросами, а не использовать чужие.
                                2. Cyrax_02 07 августа 2013, 14:00 # 0
                                  Кстати, если необходимо комбинировать моё вышеприведённое условие (LIKE+CONCAT) с иными условиями (например, добавляем условие «parent»:«2»), то моему условию необходимо явно указывать ключ. Т.к. вариант без указания ключа при наличии фигурных скобок уже не проходит:
                                  &where=`{ ["CONCAT('||', TVtv1.value, '||') LIKE CONCAT('%||', '5', '||%')"]}` - ошибка синтаксиса JSON
                                  &where=`{"parent":"2", ["CONCAT('||', TVtv1.value, '||') LIKE CONCAT('%||', '5', '||%')"]}` - ошибка синтаксиса JSON
                                  Необходимо записывать так:
                                  &where=`{"0":["CONCAT('||', TVtv1.value, '||') LIKE CONCAT('%||', '5', '||%')"]}`
                                  &where=`{"parent":"2", "0":["CONCAT('||', TVtv1.value, '||') LIKE CONCAT('%||', '5', '||%')"]}`
                                  Вместо нуля можно указать любое число.

                                  Судя по всему, у Вас там проверка: если ключ строковый (включая пустую строку), то он включается в итоговый запрос как имя TV-параметра, иначе в итоговый запрос включается только значение.
                                  1. Володя 08 августа 2013, 15:32 # 0
                                    Василий подскажи, а можно pdoResorses использовать для получения всех родителей (мультикатегорий) товара minishop в определенной категории? Или нужно свой велосипед изобретать…
                                    1. Василий Наумкин 08 августа 2013, 16:05 # 0
                                      В принципе, вывести то он сможет, но ему нужно подать в &resources id этих родителей.

                                      Для этого нужно написать сниппет с использованием getParentIds. Ну и не забыть выбрать id из msCategoryMember.

                                    2. Cyrax_02 08 августа 2013, 21:39 # 0
                                      На что следует обратить внимание:
                                      ===========================================
                                      1. Форматы записи фильтров по TV-параметрам (getResources и getProducts — в &tvFilters, pdoResources — в &where) не совместимы между собой
                                      Формат записи фильтров в pdoResources — чистый JSONxPDO, т.к. для этого используется JSONxPDO-параметр &where:
                                      rtfm.modx.com/display/xPDO20/xPDOQuery.where
                                      `{«tv1»:«5»}`
                                      `{«tv1:=»:«5»}`
                                      `{«tv1:!=»:«5»}`
                                      `{«tv1:>»:«5»}`
                                      `{«tv1:>=»:«5»}`
                                      `{«tv1:<»:«5»}`
                                      `{«tv1:<=»:«5»}`
                                      `{«tv1:LIKE»:"%5%"}`
                                      `{«tv1:NOT LIKE»:"%5%"}`
                                      `{«tv1:LIKE»:"%5%", «OR:tv2»:«8»}`
                                      `{«tv:IN»:[7,8,9,10]}`
                                      `{«tv:NOT IN»:[7,8,9,10]}`
                                      `{«tv1:IS»:null}` // null — в нижнем регистре
                                      `{«tv1:>=»:«5», «AND:tv2»:«8»}`
                                      `{«tv1:<»:«5», «OR:tv2:LIKE»:"%58"}`
                                      `{«tv1:!=»:«5», «OR:tv2:NOT LIKE»:"%8%"}`
                                      `{«tv1:LIKE»:"%5%", «OR:tv2:!=»:«8»}`
                                      `{«tv1:LIKE»:"%5%", «OR:tv2:<»:«8»}`
                                      `{«tv1:LIKE»:"%5%", «AND:tv2:<»:«8», «OR:tv3:<»:«9»}`
                                      и т.д.

                                      Формат записи фильтров в параметре &tvFilters (getResources и GetProducts) — НЕ JSON, но похож на него:
                                      modx.by/docs/modx-add-ons/getresources/
                                      `tv1===%2%` // SQL: =
                                      `tv1==%2%` // SQL: LIKE
                                      `tv1!==5` // SQL: !=
                                      `tv1==%2% || tv2!==100` // ИЛИ
                                      `tv1==%2%, tv2!==100` // И
                                      и т.д.

                                      ===========================================
                                      2. У JSONxPDO-фильтров с любым числовым ключом (знак числа не важен):
                                      &where=`{"0":["CONCAT('||', TVtv1.value, '||') LIKE CONCAT('%||', '5', '||%')"]}`
                                      &where=`{"parent":"2", "0":["CONCAT('||', TVtv1.value, '||') LIKE CONCAT('%||', '5', '||%')"]}`
                                      в ходе формирования xPDO-запроса ключ в конечный запрос НЕ помещается. В конечном запросе остается только значение:
                                      CONCAT('||', TVtv1.value, '||') LIKE CONCAT('%||', '5', '||%')
                                      Замечание: при указании ключа квадратные скобки можно не указывать.

                                      И это НЕ заслуга pdoTools (сниппета pdoResources, в частности), а заслуга метода xPDOQuery->prepare().

                                      ===========================================
                                      3. Если в JSONxPDO-параметр необходимо вписать сложное SQL-условие (в этом случае ключ JSON-пары должен либо отсутствовать, либо быть числом), например:
                                      &where=`["CONCAT('||', TVtv1.value, '||') LIKE CONCAT('%||', '5', '||%')"]`
                                      &where=`{"0":["CONCAT('||', TVtv1.value, '||') LIKE CONCAT('%||', '5', '||%')"]}`
                                      то это условие уже будет невозможно объединить с другим условием логикой «OR».

                                      Т.е. вот такие варианты не прокатят:
                                      &where=`"tv1":"7", ["OR:CONCAT('||', TVtv2.value, '||') LIKE CONCAT('%||', '5', '||%')"]`
                                      &where=`"tv1":"7", "OR:":["CONCAT('||', TVtv2.value, '||') LIKE CONCAT('%||', '5', '||%')"]`
                                      &where=`{"tv1":"7", "OR:0":["OR:CONCAT('||', TVtv1.value, '||') LIKE CONCAT('%||', '5', '||%')"]}`
                                      Замечание: при указании ключа квадратные скобки можно не указывать.

                                      Тогда как при указании классических JSONxPDO-условий объединение их логикой «OR» не составляет проблем:
                                      &where=`"tv1":"7", "OR:tv2:LIKE":"&5%"]`
                                      1. Cyrax_02 09 августа 2013, 21:17 # 0
                                        Василий, ещё одна проблема, так или иначе связанная с вашим сниппетом pdoResources.
                                        Поскольку проблема несколько общего характера, здесь писать не стал — оформил отдельным топиком:
                                        bezumkin.ru/sections/help/1615/
                                        Вопрос заключается в том, как одновременно проверять некоторое поле и на пустое значение, и на null.

                                        ================================================================================
                                        А что касается исключительно вашего сниппета, то в статье Вы написали:
                                        &tvFilters — не реализовано, но работает при указании ТВ в &where, например
                                        [[!pdoResources? &includeTVs=`test` &where=`{«test:!=»:""}`]]

                                        А если нужно проверить наоборот, на равенство пустому значению?
                                        Такие варианты не вернут ни одного ресурса:
                                        [[!pdoResources? &includeTVs=`test` &where=`{"test:=":""}`]]
                                        [[!pdoResources? &includeTVs=`test` &where=`{"test":""}`]]

                                        Если нужно проверить на пустое значение, нужно проверять именно на NULL:
                                        [[!pdoResources? &includeTVs=`test` &where=`{"test:IS":null}`]]
                                        Поскольку:
                                        а) в pdoResources в конечном запросе пользовательские TV-параметры присоединяются через LEFT JOIN
                                        б) в таблице site_tmplvar_contentvalues пустые значения отсутствуют
                                        то в конечном запросе пользовательские TV-параметры, которым не заданы значения, будут иметь значения NULL.

                                        ================================================================================
                                        А ведь мало кто держит в голове этот ньюанс. При проверке на пустое значение стандартных параметров мы так и поступаем:
                                        [[!pdoResources? &where=`{"introtext":""}`]]
                                        И это работает.
                                        1. Cyrax_02 12 августа 2013, 12:48 # 0
                                        2. Cyrax_02 12 августа 2013, 12:49 # 0
                                          Василий, верно ли я понимаю, что MySQL при выполнении запроса с условием
                                          IN [список значений]
                                          вначале в ОП индексирует значения этого списка, затем при сравнении всего лишь проверяет наличие по индексу?
                                          Если так, то скорость выполнения оператора IN будет практически одинаковой, не зависимо от количества значений в списке — что 10, что 1000000.
                                          Именно поэтому вы не заморачиваетесь вот этим:
                                          при формировании итогового запроса формируется, в частности, условие:
                                          parent IN [список id ресурсов]
                                          И в этот список попадают также и все ресурсы-листья (не имеющие дочерних ресурсов). Почему бы не исключить из этого списка листья, которые ни для кого заведомо не будут являться parent'ом? Ведь этот длинный список в запросе прогоняется по всем записям. А если в списке будет 1000 идентификаторов? А если 1 000 000?
                                          ?
                                          1. Василий Наумкин 12 августа 2013, 15:15 # 0
                                            id родителей получает функция modX::getParentIds() — она не выдает никакой информации, кроме id.

                                            Значит, для исключения чего-либо, нужно получить эти ресурсы и проверить, родители они, или нет. А это дополнительные запросы, которые только замедлят общую работу.

                                            Все просто.
                                          2. khme 19 августа 2013, 23:20 # 0
                                            Василий, спасибо за сниппет.

                                            Параметра sortbyTVType все же не хватает. Так как, если применить сортировку desc/asc к TV, которое имеет числовое значение, получим некорректну сортировку.
                                            Пример:
                                            &sortby=`{"HitsPage":"asc"}`
                                            В HitsPage хранится количество просмотров страницы. Получим порядок, например: 1, 10, 2, 3, 4…

                                            Делаю пока так, чтобы получить значение value в site_tmplvar_contentvalues как в числовом типе:
                                            &sortby=`{"(HitsPage+0)":"asc"}`
                                            Получаем: 1, 2, 3, 4…

                                            1. Василий Наумкин 20 августа 2013, 13:16 # 0
                                              Даже если я и добавлю этот параметр, то он будет просто приписывать CAST (или как то иначе приводить тип) в условии.

                                              Ты и сейчас можешь это писать и в &sort=`` и в &where=`` самостоятельно.
                                            2. Антон Соловьёв 29 августа 2013, 16:44 # 0
                                              Никак не пойму, как прописать в where чтобы TVпараметр1 = значение1, TVпараметр2 = 3начение2, TVпараметр3>=значение3, TVпараметр3<=значение4
                                              «par1:=»:«1»,«AND:par2:=»:«2»,«AND:par3:>»:«0»,«AND:par3:<»:«4000»
                                              Не работает…
                                              Нужно, чтоб все условия учитывались. Где почитать про синтаксис?
                                              1. Антон Соловьёв 29 августа 2013, 18:58 # 0
                                                что-то ничего не понимаю. В ТВ price есть значения 3200, почти все — 100500.
                                                &where=`{«price:>=»:«1005»}` выдает все документы, а &where=`{«price:>=»:«1006»}` выдаёт только то, где значение 3200, а 100500 не отдаются.
                                                price имеет тип вывода — число, обязателен для заполнения, десятичные и минус не разрешены. в чем может быть проблема?
                                                &where=`{«price:>»:«100499»}` возвращает все ресурсы!
                                                &where=`{«price:>»:«1006»}`
                                                0.0000329: pdoTools loaded
                                                0.0008559: Conditions prepared
                                                0.0001540: Query parameters ready
                                                0.0001030: xPDO query object created
                                                0.0006590: Included list of tvs: price, ws.images, area, metro, countp
                                                0.0019710: leftJoined modTemplateVarResource as TVws.images
                                                0.0004420: leftJoined modTemplateVarResource as TVprice
                                                0.0004621: leftJoined modTemplateVarResource as TVarea
                                                0.0004480: leftJoined modTemplateVarResource as TVmetro
                                                0.0004530: leftJoined modTemplateVarResource as TVcountp
                                                0.0000169: Grouped by modResource.id
                                                0.0007191: Added selection of modResource: 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`
                                                0.0000410: Added selection of TVws.images: `TVws.images`.`value` as `tv.ws.images`
                                                0.0000269: Added selection of TVprice: `TVprice`.`value` as `tv.price`
                                                0.0000231: Added selection of TVarea: `TVarea`.`value` as `tv.area`
                                                0.0000219: Added selection of TVmetro: `TVmetro`.`value` as `tv.metro`
                                                0.0000250: Added selection of TVcountp: `TVcountp`.`value` as `tv.countp`
                                                0.0005860: Added where condition: published=1, deleted=0, isfolder=0, modResource.parent:IN(8,37,38,51,52,39), `TVprice`.`value`:>=1006
                                                0.0000310: Sorted by publishedon, DESC
                                                0.0000129: Limited to 10, offset 0
                                                0.0007341: 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`, `TVws.images`.`value` as `tv.ws.images`, `TVprice`.`value` as `tv.price`, `TVarea`.`value` as `tv.area`, `TVmetro`.`value` as `tv.metro`, `TVcountp`.`value` as `tv.countp` FROM `modx_site_content` AS `modResource` LEFT JOIN `modx_site_tmplvar_contentvalues` `TVws.images` ON `TVws.images`.`contentid` = `modResource`.`id` AND `TVws.images`.`tmplvarid` = 2 LEFT JOIN `modx_site_tmplvar_contentvalues` `TVprice` ON `TVprice`.`contentid` = `modResource`.`id` AND `TVprice`.`tmplvarid` = 9 LEFT JOIN `modx_site_tmplvar_contentvalues` `TVarea` ON `TVarea`.`contentid` = `modResource`.`id` AND `TVarea`.`tmplvarid` = 13 LEFT JOIN `modx_site_tmplvar_contentvalues` `TVmetro` ON `TVmetro`.`contentid` = `modResource`.`id` AND `TVmetro`.`tmplvarid` = 14 LEFT JOIN `modx_site_tmplvar_contentvalues` `TVcountp` ON `TVcountp`.`contentid` = `modResource`.`id` AND `TVcountp`.`tmplvarid` = 15 WHERE  ( `modResource`.`published` = 1 AND `modResource`.`deleted` = 0 AND `modResource`.`isfolder` = 0 AND `modResource`.`parent` IN (8,37,38,51,52,39) AND `TVprice`.`value` > '1006' )  GROUP BY modResource.id ORDER BY publishedon DESC LIMIT 10 "
                                                0.0001490: SQL executed
                                                0.0001180: Total rows: 1
                                                0.0000379: Rows fetched
                                                0.0353801: Returning processed chunks
                                                0.0435038: Total time
                                                15 728 640: Memory usage
                                                
                                              2. Сергей Савельев 09 сентября 2013, 19:19 # 0
                                                Тоже что-то не правильно работает. Не могу вывести ресурсы c стандартным значением tv параметра:
                                                return $modx->runSnippet('pdoResources', array(
                                                    'parents' => '0',
                                                    'sortby' => 'createdon',
                                                    'depth' => '10',
                                                    'limit' => '100',
                                                    'tpl' => 'pplResources',
                                                    'select', 'description,introtext,pagetitle,id,createdon',
                                                    'includeTVs' => 'resource_complit',
                                                    'where' => '{"resource_complit":"not_class"}',
                                                    'outputSeparator' => ', ',
                                                    );
                                                Чанк шаблона pplResources содержит ссылку на ресурс. tv resource_complit содержит значение по умолчанию: not_class, и если его изменить при редактировании ресурса и в параметре where поставить соответствующее значение, то этот ресурс выводится.

                                                Вывод showLog:
                                                0.0000818: pdoTools loaded
                                                0.0003612: Conditions prepared
                                                0.0000930: Query parameters ready
                                                0.0003259: xPDO query object created
                                                0.0025849: Included list of tvs: resource_complit
                                                0.0022521: leftJoined modTemplateVarResource as TVresource_complit
                                                0.0000620: Grouped by modResource.id
                                                0.0034490: Added selection of modResource: 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`
                                                0.0001221: Added selection of TVresource_complit: `TVresource_complit`.`value` as `tv.resource_complit`
                                                0.0028510: Added where condition: published=1, deleted=0, context_key:IN(web), `TVresource_complit`.`value`:==not_class
                                                0.0001168: Sorted by createdon, DESC
                                                0.0000410: Limited to 100, offset 0
                                                0.0021360: 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`, `TVresource_complit`.`value` as `tv.resource_complit` FROM `modx_site_content` AS `modResource` LEFT JOIN `modx_site_tmplvar_contentvalues` `TVresource_complit` ON `TVresource_complit`.`contentid` = `modResource`.`id` AND `TVresource_complit`.`tmplvarid` = 1 WHERE  ( `modResource`.`published` = 1 AND `modResource`.`deleted` = 0 AND `modResource`.`context_key` IN ('web') AND `TVresource_complit`.`value` = 'not_class' )  GROUP BY modResource.id ORDER BY createdon DESC LIMIT 100 "
                                                0.0004301: SQL executed
                                                0.0005040: Total rows: 0
                                                0.0000830: Rows fetched
                                                0.0000410: Returning processed chunks
                                                0.0155349: Total time
                                                16 777 216: Memory usage
                                                1. Василий Наумкин 09 сентября 2013, 19:21 # 0
                                                  Значения по умолчанию в базе данных нет.

                                                  Это настройка ТВ параметра, на которую pdoTools просто начхать — он работает с таблицей напрямую.
                                                  1. Сергей Савельев 09 сентября 2013, 21:31 # 0
                                                    Это я понимаю. Немного не так вы поняли мой вопрос. При сохранении ресурса, если не выбрать значение, автоматически выберется значение по умолчанию, то есть в данном случае not_class, но pdoResorces не выводит этот ресурс с соответствующим условием, как я описывал вызов выше.
                                                    1. Василий Наумкин 09 сентября 2013, 22:18 # 0
                                                      Прежде чем спорить, зайти в БД через phpmyadmin и поищи там значение по умолчанию.
                                                      1. Сергей Савельев 09 сентября 2013, 23:34 # 0
                                                        Извините, спасибо. Действительно, ничего не присваивается автоматически, строка в таблице просто напросто не создаётся, если значение не было выбрано.
                                                        1. Сергей Савельев 09 сентября 2013, 23:58 # 0
                                                          Сдаюсь. Как сделать условие на несуществование строки, связанной с конкретным tv, в таблице?

                                                          Пробовал условия:
                                                              'where' => '{
                                                                  "resource_complit:!=":"resource_complit",
                                                                  "AND:resource_complit:!=":"resource_alert"
                                                                  }'
                                                          То есть все имеющиеся значения, кроме not_class, но, видимо, если ресурс строки не имеет, то он отсеивается первым делом и в итоге ничего не выводится.
                                                          1. Сергей Савельев 10 сентября 2013, 00:16 # 0
                                                            Всё, нашёл, не сдался таки:
                                                            'where' => '{"resource_complit:IS": null}',
                                                            Спасибо! :)
                                                            1. Василий Наумкин 10 сентября 2013, 05:57 # 0
                                                              Молодец!

                                                              Вот еще есть заметка про это.
                                                  2. Ilya Krivenok 02 марта 2014, 00:37 # 0
                                                    Здравствуйте.

                                                    При выводе новостей с помощью pdoPage и pdoResources столкнулся с такой проблемой, для вывода отформатированной даты в чанке использую плейсхолдер с модификатором [[+publishedon:strtotime:date=`%d`]], но вместо числа получаю пустое место. Без модификатора дата выводится, но в совершенно непригодном для меня формате. При использовании getResources подобного не наблюдается.

                                                    Подскажите пожалуйста, в чем может быть загвоздка.

                                                    1. Василий Наумкин 02 марта 2014, 07:02 # 0
                                                      Убери strtotime
                                                      1. Ilya Krivenok 02 марта 2014, 13:56 # 0
                                                        спасибо :)
                                                        1. Andrey Klimov 24 августа 2014, 14:18 # 0
                                                          А что делать в случае [[+pagetitle:ellipsis=`50`]]?
                                                          Тоже выводит пустую строку
                                                      2. Алексей 07 мая 2014, 15:55 # 0
                                                        как эту заметку занести в «избранное»?
                                                        1. Василий Наумкин 07 мая 2014, 15:56 # +1
                                                          Зависит от твоего браузера. В Chrome это делается нажатием на Ctrl+D.
                                                        2. Алексей 09 июня 2014, 10:31 # 0
                                                          Возможно ли указать в сниппете «pdoCrumbs» параметр «conditionalTpls» для «tplCurrent»?
                                                          Как видно из логики
                                                          elseif ($row['id'] == $resource->id && !empty($tplCurrent)) {
                                                          			$tpl = $tplCurrent;
                                                          		}
                                                          — нет.
                                                          В каком направлении копать, чтобы можно было указать «conditionalTplsCurrent» для гибкого выбора чанка оформления текущего элемента навигации?
                                                          1. Константин Ильин 07 ноября 2014, 15:58 # 0
                                                            price>=21500,price<=31798||mass_proiz==YAMAGUCHI
                                                            Поддерживает ли pdoResources в tvFilters такие записи?
                                                            1. Павлик Мышкин 03 декабря 2014, 17:51 # 0
                                                              &conditionalTpls=`{"2":"chunk2", "5":"chunk5"}`
                                                              В результате работы, все ресурсы с шаблонами 3 и 5 будут вывеедены в собственных чанках, а все остальные — в chunk1.
                                                              Опечатка в вызове сниппета. В нем выборка по шаблону 2, а в тексте указано 3.
                                                              Добавление новых комментариев отключено.