[pdoTools] Версия 1.8.0-beta: Условия выборки

Компонент pdoTools постоянно развивается и дополняется. После каждой обновки мне, как правило, нужно обновлять и сниппеты, которые с ним работают — потому что пользователи уже ожидают там новые функции.

А компонентов со сниппетами несколько: Tickets, miniShop2, mSearch2, да и в самом pdoTools уже 6 сниппетов (и планируется еще 2).

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

Поэтому, с версии 1.8.0 всё построение условия where, сортировка и выборка значений вшита прямо в pdoFetch.

Вшита аккуратно, мешать имеющимся сниппетам не должна, но вдруг? Поэтому версия 1.8.0-beta. Обновляться нужно осторожно, и в случае проблем сразу писать в комментарии.

На всякий случай предусмотрен и предохранитель. Если ваш сниппет барахлит с новой версией pdoTools — попробуйте указать:
&disableConditions=`1`
это отключит встроенную обработку условий.

Новый метод pdoFetch::additionalConditions()

Весь класс pdoFetch разбит на методы, каждый отвечает за свой этап работы и имеет говорящее название:
  • loadModels
  • makeQuery
  • addTVs
  • addJoins
  • addGrouping
  • addSelects
  • addWhere
  • prepareQuery
  • addSort
Попробуйте угадать, что зачем =)

Так вот, теперь добавился новый метод additionalConditions, в котором собраны все правила построения условий из параметров сниппета.
На данный момент это:
  • resources — ресурсы для выборки, можно указывать отрицательные значения, чтобы кого-то исключить.
  • parents — родители для поиска ресурсов. Можно указывать положительные и отрицательные значения для включения или исключения родителей. Для выборки ресурсов из корня указывайте -0 или +0. Если параметр пуст, или равен 0 — то он отключен.
  • templates — Шаблоны ресурсов, все тоже самое, что и у parents.
  • showUnpublished — Показывать неопубликованные ресурсы.
  • showHidden — Показывать ресурсы, скрытые в меню.
  • showDeleted — Удалённые ресурсы.
  • hideContainers — Прятать ресурсы контейнеры, то есть isfolder = 1.
  • hideUnsearchable — Прятать ресурсы, у которых не установлен флаг «участвует в поиске».
  • context — Список контекстов для уточнения выборки. В принципе, не нужен — так как &parents работает с пониманием контекстов.
Обработка этих условий включена по умолчанию, и дружит только с modResource и его наследниками: Ticket, msProduct и т.д. При этом проверяются уже имеющиеся условия в where, и если они касаются полей, для которых прописаны правила — то оно исключается.

Это сделано для сохранения работы старых сниппетов, которые сами генерируют условия выборки. Со временем, я их тоже обновлю.

Наверное, стало немного скучно, поэтому вот код для примера:
<?php
$pdo = $modx->getService('pdoFetch');
$pdo->setConfig($scriptProperties);

return $pdo->run();

В параметрах этого сниппета можно смело указывать и &parents, и &resources, и &tpl, и &tplConditions, и &limit, и &includeTVs, и еще с десяток разных условий (которые я опишу позже в документации).

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

В новой версии pdoResources состоит всего из 43 строк, при этом 29 из них — вывод готовых результатов и поддержка &toPlaceholder.
Так что, писать собственные сниппеты стало еще проще, в который уже раз.

Еще примерчик, теперь уже с getCollection:
$pdo = $modx->getService('pdoFetch');

$parameters = array(
	'showUnpublished' => 1,
	'parents' => 5,
	'depth' => 0,
	'resources' => '6,8,10,11,-7'
);
// Второй параметр - null, потому что нам теперь лень писать where самостоятельно
$res = $pdo->getCollection('modResource', null, $parameters);

echo '<pre>';
print_r($res);
echo $pdo->getTime();

Вот такой выходит лог:
0.0005920: xPDO query object created
0.0149138: 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`, `content`, `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.0018830: Processed additional conditions
0.0010650: Added where condition: modResource.id:IN(6,8,10,11), modResource.id:NOT IN(7), modResource.parent:IN(5)
0.0000410: Sorted by find_in_set(`modResource`.`id`,'6,8,10,11'), 
0.0004210: 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`.`content`, `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` FROM `modx_site_content` AS `modResource` WHERE  ( `modResource`.`id` IN (6,8,10,11) AND `modResource`.`id` NOT IN (7) AND `modResource`.`parent` IN (5) )  ORDER BY find_in_set(`modResource`.`id`,'6,8,10,11') "
0.0009050: Total rows: 4
0.0198208: Total time
По моему, прикольно.

Улучшение метода pdoFetch::addSort()

Этот метод заведует правильным разбором и указанием полей для сортировки.

Раньше он просто смотрел в параметры &sortby и &sortdir, а сейчас стало интереснее:
Если параметр &sortby пуст, и при этом в выборке указан &resources, то вывод будет именно в порядке указания. Внимательные читатели заметили это в предыдущем примере:
Sorted by find_in_set(`modResource`.`id`,'6,8,10,11')


Если указан JSON или обычный массив — он будет разобран и применен. Ключами массива должно быть поле для сортировки, а массивом — направление.
&sortby=`{
	"publishedon":"asc",
	"menuindex":"desc"
}`

Конечно, никуда не делось и старое доброе указание поля и направления в &sortby и &sortdir.
Честно говоря, поддержка массивов и раньше была, а вот вывод ресурсов в порядке указания — хорошая такая новинка.

Улучшение метода pdoFetch::addSelect()

А этот метод указывает поля для выборки. Раньше можно было указывать только массив:
&select=`{
	"modResource":"id,pagetitle,longtitle"
}`

Если вы присоединяете другие таблицы — без массива никак не обойтись. Но join используется далеко не всегда, поэтому теперь есть и поддержка указания полей строкой:
&select=`id,pagetitle,longtitle`
На выходе получаем такое:
Added selection of modResource: SQL_CALC_FOUND_ROWS `id`, `pagetitle`, `longtitle`
Понятно, что в запросе к этим полям приставляется имя выбираемого класса.

Заключение

Еще в новой версии есть исправления default_text ТВ параметра (теперь там можно использовать ковычки) и метода pdoTools::buildTree(), который припасен для будущего сниппета pdoMenu.
$pdo = $modx->getService('pdoFetch');
$res = $pdo->getCollection('modResource');
$tree = $pdo->buildTree($res);
echo '<pre>';
print_r($tree);

В скором времени я планирую описать весь имеющийся базовый функционал pdoTools в соответствующем разделе. Информации накопилось уже на небольшую брошюру.

Обновляемся и не забываем про то, что это бета и она может немного поломать ваши имеющиеся сниппеты.
Если что — пишите, буду исправлять.

Следующая заметка
[msDiscount] Система скидок для miniShop2
Предыдущая заметка
[pdoTools] Версия 1.7.2 - методы getObject и getCollection


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

  1. Павел Левин 09 октября 2013, 10:14 # 0
    т.е. с этим можно делать сортировку, контента в категориях?

    к примеру: по дате, по алфавиту, по популярности
    1. Алексей Карташов 10 октября 2013, 03:41 # 0
      В который раз повоторюсь — это мега крутой комбайн!
      Который с каждым разом всё круче и всё проще в использовании. Вась, спасибо :-)

      Одно только пока не понятно:
      Для выборки ресурсов из корня указывайте -0 или +0. Если параметр пуст, или равен 0 — то он отключен.
      "+0" и "-0" — это как? По логике "+0" должен показывать все документы в корне, а "-0" — исключать все в корне, т.е. в итоге исключать вообще все? Или и то, и то даёт одинаковый эффект? Раскроешь тему поподробнее?
      1. Василий Наумкин 10 октября 2013, 06:30 # 0
        Раскрываю.

        У getResources, на который я ориентировался изначально, в параметре &parents можно указывать id родителей для ограничения выборки. При этом указание &parents=`0` означает, что нужно выбрать ресурсы из корня, то есть те, у которых parent = 0.
        Так как 0 уже занято, для отключения параметра там используется "-1".

        В pdoResources можно исключать родителей, поэтому "-1" — это не выводить ресурсы, у которых parent = 1 (и их потомков). Поэтому отключение параметра — это просто его не указать, то есть &parents=``.

        Но в pdoResources, при таком указании, родителем станет текущий ресурс, то есть будут выбраны все его потомки. Возможно, это немного неочевидное поведение, но так уж сложилось.
        Значит, чтобы отключить параметр &parents в pdoResources — нужно указать ему «0»:
        &parents=`0`

        А что делать, если нужно выбрать ресурсы из корня, то есть parent = 0? Вот тут я и придумал +0 и -0.

        Так что, можно задавать вот такие условия:
        &parents=`+0,-2,-3`
        Выбрать все ресурсы, кроме потомков 2 и 3, на глубину &depth, которая равна 10, по умолчанию.

        А вот и выборка чисто корневых ресурсов контекста catalog:
        [[!pdoResources?
        	&parents=`+0`
        	&depth=`0`
        	&context=`catalog`
        ]]

        Немного навороченно, зато и позволяет о-го-го! Особенно, если вспомнить еще про &resources и &templates =)
        1. Сергей Шлоков 10 октября 2013, 10:13 # 0
          А может доп. параметр использовать &parents_off? А то с нулями как-то эксцентрично получается =)
          Документация очень нужна. pdoTools — вещь навороченная, без инструкции непросто разобраться со всеми возможностями.
      2. Лисоченко Кирилл 10 октября 2013, 12:49 # 0
        modx 2.2.10, pdoTools — 1.8.0 beta1

        на рабочем сайте такой вызов getResources:
        [[!getResources?
        &resources=`[[*parent]]`
        &tpl=`tplFullContent`
        &sortby=`{"menuindex":"asc"}`
        &includeTVs=`BigCarPicture`
        ]]
        
        чанк tplFullContent:
        <div class="content">
        	<h1> [[*parent:myId=`pagetitle`]] - [[*pagetitle]]</h1>
            <!-- **Blog Post** -->
            <div class="blog-post">
                <div class="post-thumb">
                    <img src="[[+tv.BigCarPicture:phpthumbof=`w=670&h=370&zc=1`]]" alt="[[+pagetitle]]">
                </div> <div class="hr"> </div>
            </div>
        </div>
        <div class="clear"> </div>
        
        <div class="content content-full-width">
        	[[*content]]
        </div>
        
        ничего необычного.
        при данном вызове getResources цепляет картинку из родительского ресурса (1), а контент выводит текущего (2)

        SX4 Hatchback (1)
        Комплектации и цены (2)

        При таком же вызове pdoResources
        [[!pdoResources?
        &resources=`[[*parent]]`
        &tpl=`tplFullContent`
        &sortby=`{"menuindex":"asc"}`
        &includeTVs=`BigCarPicture`
        ]]
        
        вообще ничего не выводит, ни картинки, ни контента, а если вначале поставить &disableConditions=`1` — выводит только контент, картинку нет.
        Я не исключаю, что я ошибаюсь в синтаксисе и параметрах, но возможно это проблема.

        1. Алексей 14 ноября 2013, 12:42 # 0
          Столкнулся с нетривиальной задачей — приджойнить (leftjoin) 2 таблицы, с одинаковыми полями (id).
          (причем поле id нужно как у первой приджойненной таблицы, так и у второй таблицы)
          есть ли префикс для leftjoin таблицы?
          что-то на подобии: 'tvPrefix', только 'leftJoinPrefix'
          1. Василий Наумкин 14 ноября 2013, 12:48 # 0
            Что же тут нетривиального?

            Смотри рабочий пример.
          2. Алексей 12 декабря 2013, 15:38 # 0
            было бы круто сортировать результаты вот так:
            &sortby=`{
            	"parent.menuindex":"desc",
            	"menuindex":"desc"
            }`
            1. Василий Наумкин 12 декабря 2013, 16:01 # 0
              Сортируй, кто мешает?

              [[!msProducts?
              	&parents=`0`
              	&leftJoin=`{"Parent":{"class":"msCategory","alias":"Parent","on":"Parent.id = msProduct.parent"}}`
              	&sortby=`{"Parent.menuindex":"desc","menuindex":"desc"}`
              	&showLog=`1`
              ]]
              
              1. Алексей 12 декабря 2013, 18:00 # 0
                А для mFilter как?

                PS: сделал аж 2 уровневую сортировку -))
                &leftJoin=`
                  {"PParent":{"class":"msCategory","alias":"PParent","on":"PParent.id = msProduct.parent"},
                  "{PPParent":{"class":"msCategory","alias":"PPParent","on":"PPParent.id = PParent.parent"}}`
                  &sortby=`{"PPParent.menuindex":"asc","PParent.menuindex":"asc","menuindex":"asc"}`
                
                1. Василий Наумкин 12 декабря 2013, 18:05 # 0
                  А mFilter что, сам выбирает и выводит документы?

                  Я думал, он использует другой сниппет, типа pdoResources.
                  1. Алексей 12 декабря 2013, 18:47 # 0
                    Вобщем в mfilter сниппет нужно вбабахать вот эти строки:
                    $paginatorProperties = array_merge(
                    			$paginator->getProperties()
                    			,$scriptProperties
                    			,array(
                    				'resources' => implode(',',$ids)
                    				,'parents' => '0'
                    				,'element' => $scriptProperties['element']
                    				,'defaultSort' => $start_sort
                    				,'toPlaceholder' => false
                    				,'limit' => $limit
                            ,'leftJoin' =>  '{"PParent":{"class":"msCategory","alias":"PParent","on":"PParent.id = msProduct.parent"},
                                            "{PPParent":{"class":"msCategory","alias":"PPParent","on":"PPParent.id = PParent.parent"}}'
                            ,'sortby' => '{"PPParent.menuindex":"asc","PParent.menuindex":"asc","menuindex":"asc"}'
                    			)
                    		);
                    на 231 строке.
                    но сразу предупреждаю, пагинация javascript при этом не фурычит — сортирует как вздумается
                    а так конечно шикарно — разместил самые топовые товары на самом видном месте, без лишних телодвижений
                    1. Василий Наумкин 12 декабря 2013, 18:48 # 0
                      Гениально!

                      Ты не видишь, что там чуть выше уже вбабахан $scriptProperties?
              Добавление новых комментариев отключено.