[pdoTools] Версия 1.7.2 - методы getObject и getCollection

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

getChunk и parseChunk мы уже заменили. Сделали выборку через PDO, с полдключением ТВ и проверкой прав. Что осталось?

Верно — еще больше упростить работу с библиотекой. Встречайте 2 новых метода: pdoFetch::getObject и pdoFetch::getCollection().

Чтобы вам было удобно работать, классы pdoFetch и pdoTools теперь всегда находятся в системе, и вы можете получить их через modX::getService(); когда угодно.

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

Так что, если вам нужно быстро обернуть какие то данные в чанки, то pdoFetch не нужен, можно вызвать сразу основной класс. А если хотите что-то получить из БД — вызывайте pdoFetch, он унаследует родителя и вы сможете работать и с таблицами и с чанками.

Ну а теперь, подробнее.

getObject и getCollection

Эти методы работают точно так же, как их предки из xPDO, но только возвращают массивы вместо объектов.
Вы не можете их сохранить, изменить или удалить — только вывести на страницу.

Простейший пример:
$pdo = $modx->getService('pdoFetch');
if ($resource = $pdo->getObject('modResource', 1)) {
	print_r($resource);
}

Как видите, метод точно так же принимает имя объекта xPDO и условие. Можно указать id или массив &where.
$pdo = $modx->getService('pdoFetch');
if ($resource = $pdo->getObject('modResource', array('pagetitle' => 'Тест'))) {
	print_r($resource);
}

Конечно, можно работать не только с ресурсами, но и с любыми объектами, которые загружены в память:
$pdo = $modx->getService('pdoFetch');
if ($resource = $pdo->getObject('modTemplate', 1)) {
	print_r($resource);
}

А что делать, если нам нужен объект, который еще не объявлен? Тогда на выручку приходит третий параметр метода, куда можно передать конфиг pdoTools, включая все возможные параметры: loadModels, includeTVs, processTVs и т.д.
$pdo = $modx->getService('pdoFetch');
if ($resource = $pdo->getObject('msResourceFile', 1, array('loadModels' => 'ms2Gallery'))) {
	print_r($resource);
}

Точно так же работает и pdoFetch::getCollection(), но в этом методе возвращается несколько объектов и можно проверять права параметром checkPermissions.

Давайте выберем 50 ресурсов сайта и быстро оформим в чанк?
$pdo = $modx->getService('pdoFetch');
$rows = $pdo->getCollection('modResource', array('published' => 1), array('limit' => 50));
$output = '';
foreach ($rows as $row) {
	$row['idx'] = $pdo->idx++;
	$output .= $pdo->getChunk('@INLINE <p>[[+idx]]. [[+pagetitle]]</p>', $row);
}
return $output;
После работы можно и лог посмотреть:
print_r($pdo->getTime());

По моему, неплохо получилось.
Новые методы открывают широчайшие возможности для быстрой и удобной работы с объектами MODX без объектов =)

Улучшение сниппета pdoField

Ну, во-первых я его переписал на новый метод getObject. Собственно, отсюда идея и пришла.

А во-вторых, добавил 2 приятных параметра:
  • &default — Сюда можно указать дополнительное поле (включая ТВ), которое будет показано, если &field оказался пустым.
  • &output — Строка, указанная в этом параметре, вернётся вам, если и &field и &default не содержат значений.
Таким образом можно ко-где сократить кол-во фильтров вывода:
[[!pdoField?
	&id=`15`
	&field=`longtitle`
	&default=`pagetitle`
	&ouput=`Какой то странный ресурс, без pagetitle?`
]]

Если у ресурса не заполнен longtitle, вы получите pagetitle. Ну и можно так же проверять сразу 2 ТВ у ресурса.

Также поменялась логика работы &top. Я раньше думал, что он у меня постоянно глючит, а оказывается, я просто не знал, что он иначе работает. Вот описание из UltimateParent:
* @example [[UltimateParent? &id=`45` &top=`6`]]
* Will find the ultimate parent of document 45 if it is a child of document 6;
* otherwise it will return 45.
Вернёт родителя документа 45, если он потомок документа 6.

Совершенно не понимаю, зачем это может быть нужно, поэтому сделал так, как всегда пытался использовать параметр &top — теперь он возвращает n непосредственного родителя сверху от указанного id.

Кто не согласен — прошу аргументировать в комментариях.

Заключение

На моём тестовом сайте один pdoFetch::getObject() отрабатывает за 0.015483.
Один modX::getObject() — за 0.020988.

Разницу на сотнях и тысячах ресурсов можете прикинуть. Хотя, зачем вас напрягать?
Я проверил у себя на сайте, вот скрипт, класть в корень сайта (потом не забудьте прибить!):
<?php
define('MODX_API_MODE', true);
require('index.php');

$time = microtime(true);
$pdo = $modx->getService('pdoFetch');
//$collection = $modx->getCollection('modResource');
$collection = $pdo->getCollection('modResource');

echo 'Выбрано результатов: '.count($collection).'
За время: ' . (microtime(true) - $time);

pdoFetch::getCollection()
Выбрано результатов: 1855
За время: 0.14443612098694

modX::getCollection()
Выбрано результатов: 1855
За время: 5.9065659046173

Как вам разница в 42 раза? Даже если включить checkPermissions:
$collection = $pdo->getCollection('modResource', '', array('checkPermissions' => 'list'));
То результат
Выбрано результатов: 1855
За время: 1.8788080215454
Все равно быстрее в 3 раза. Хотя тут тоже создаются объекты xPDO и проверяются права.

Ну и чтобы совсем уже вас добить, оформляем эти 1855 ресурсов в чанки.
$output = '';
foreach ($collection as $row) {
	$row['idx'] = $pdo->idx++;
	$output .= $pdo->getChunk('@INLINE <p>[[+idx]]. [[+pagetitle]]</p>', $row);
	//$output .= $pdo->parseChunk('@INLINE <p>[[+idx]]. [[+pagetitle]]</p>', $row);
}
Методом pdoTools::getChunk() (с запуском парcера MODX)
Выбрано результатов: 1855
За время: 2.7783949375153

Методом pdoTools::parseChunk() — без парсера, только замена плейсхолдеров:
Выбрано результатов: 1855
За время: 0.42111110687256

В общем, обновляем, ищем ошибки, комментируем. Надеюсь, ничего не сломал.

Следующая заметка
[pdoTools] Версия 1.8.0-beta: Условия выборки
Предыдущая заметка
[miniShop2] Версия 2.1.0-pl1 Модификация цены и веса товаров


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

  1. Виталий Батушев 27 сентября 2013, 22:29 # 0
    Очень хорошо.
    Об одном жалею — надо было две послать :)
    Ну и «убийцу» Wayfinder-а не я один, думаю, жду :)
    1. Василий Наумкин 27 сентября 2013, 22:32 # 0
      За второй я сам приеду, когда-нибудь.
      А пока бы первую дождаться =)

      Это мне Виталий отправил бутылочку из Венесуэлы:
      1. Виталий Батушев 27 сентября 2013, 22:37 # 0
        Надеюсь, довезли :) Они с приключениями возвращались :)
        1. Alex Vakhitov 28 сентября 2013, 00:24 # 0
          Эх, увидел фотографию и сразу захотелось на Маргариту, вспомнил что Чавес умер и захотелось вдвойне посмотреть что изменилось, то были серьезные планы переехать туда. (:

          А вообще думаю разница в 42 раза не спроста, Василий ты на правильном пути (:
          1. Александр Котлов 28 сентября 2013, 00:57 # 0
            42!
            я подозревал!)))
            1. Виталий Батушев 28 сентября 2013, 03:19 # 0
              А на какие перемены, интересно, рассчитываешь? :)
              1. Alex Vakhitov 29 сентября 2013, 08:09 # 0
                Наоборот на отсутствие перемен рассчитываю (:
        2. Сергей Шлоков 28 сентября 2013, 06:50 # 0
          Супер. Меняю getObejct + toArray на pdoTools::getObject.с доплатой. :)
          1. Василий Наумкин 28 сентября 2013, 08:15 # 0
            И ведь правда, с доплатой поменял, молодец!
            1. Сергей Шлоков 28 сентября 2013, 08:17 # 0
              Хорошее дело не должно оставаться безнаказанным. :)
          2. Сергей Шлоков 28 сентября 2013, 09:15 # 0
            Отличная задумка с loadModels. Но работает только c префиксом modx_. Было бы неплохо добавить возможность с любыми пакетами работать. Может префикс через JSON передавать?
            {'prefix':'package'}
            1. Василий Наумкин 28 сентября 2013, 09:59 # 0
              А может избавиться от префикса в модели?

              Перечислить компоненты через запятую все-таки проще, чем писать JSON, да еще помнить, у кого какой префикс.
              1. Сергей Шлоков 28 сентября 2013, 10:34 # 0
                Дак не по собственной воле. Вроде разработчики рекомендуют. Bob Ray вот пишет:
                The final argument to addPackage() ($prefix) allows you to specify a table prefix. This is necessary if your class represents a database object and its DB table has a different prefix than the MODX classes. Using a different table prefix for your own classes is strongly recommended.
                Ежели это ни на что не влияет, то я бы с радостью убрал все префиксы.
                1. Василий Наумкин 28 сентября 2013, 10:49 # 0
                  Мое авторитетное мнение — это полная фигня.

                  Префиксы отлично пишутся сразу в схему. При этом, для инициализации модели ничего указывать не нужно. Вот, смотри схему miniShop2.
                  Видишь, у всех таблиц указан префикс ms2_, а объекты начинаются с ms? Вот это и есть префиксы, чтобы не было конфликтов.

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

                  Сейчас я пишу схему и по ней генерирую таблицы. Проблем нет вообще, всем рекомендую.
                  1. Сергей Шлоков 28 сентября 2013, 11:59 # 0
                    Переделал схему.
                    Вызываю $resource = $pdo->getObject('Class', 1, array('loadModels' => 'class'))
                    Получаю ошибку:
                    Unknown column 'Class.' in 'where clause'
                    getTime() пишет:
                    0.... Loaded model "Class" from "/core/components/class/model/"
                    0.... added where condition: =1 
                    Походу теряется первичный ключ (id).
                    P.S. Если выполнить перед getObject
                    $modx->addPackage('class', $modx->getOption('core_path').'components/class/model/')
                    , то все работает.
                    1. Василий Наумкин 28 сентября 2013, 12:20 # 0
                      У тебя, видимо, первичный ключ не определен в модели. В логе же пишет, что модель загрузил, значит дальше уже заморочки в ней. Загружается она так же, через addPackage.

                      Попробуй так:
                      $resource = $pdo->getObject('Class', array('id' => 1), array('loadModels' => 'class'))
                      
                      То есть, указать конкретно поле, по которому выбираешь.

                      1. Сергей Шлоков 28 сентября 2013, 12:58 # 0
                        В базе в таблице Class указано:
                        ...
                        PRIMARY KEY (`id`),
                        В схеме нет поля id — класс наследуется от xPDOSimpleObject.
                        Если указать array('id' => 1), то выводит:
                        0.... added where condition: 0=id, 1=1 
                        Вызов
                        $modx->getPK('Class')
                        выдает 'id'
                        1. Василий Наумкин 28 сентября 2013, 13:15 # 0
                          Все, нашел ошибку, обновляйся.
                          1. Сергей Шлоков 28 сентября 2013, 13:33 # 0
                            Нажал в FF кнопку обновить. Ничего не изменилось. :))))))
                            Усё работает.
                            1. Василий Наумкин 28 сентября 2013, 13:39 # 0
                              Весельчак =)
                            2. Сергей Шлоков 28 сентября 2013, 17:45 # 0
                              Кстати, если указать where параметр через массив
                              array('id' => 1)
                              то возникает та же ошибка
                              0.... added where condition: 0=id, 1=1 
                              А если циферку, то работает.
                              И еще, было бы удобнее и правильнее, если бы ключи объекта были по имени таблицы (id, name), а не Class_id, Class_name и т.д.
                              1. Василий Наумкин 28 сентября 2013, 18:05 # 0
                                У меня нет такого.

                                Вот тестовый файл:
                                <?php
                                define('MODX_API_MODE', true);
                                require 'index.php';
                                echo '<pre>'; 
                                
                                $pdoFetch = $modx->getService('pdoFetch');
                                
                                $res = $pdoFetch->getObject('msResourceFile', 1, array('loadModels' => 'ms2Gallery'));
                                print_r($res);
                                print_r($pdoFetch->getTime());
                                
                                $rows = $pdoFetch->getCollection('msResourceFile', array('id:>' => 0), array('loadModels' => 'ms2Gallery', 'limit' => 10));
                                $output = '';
                                foreach ($rows as $row) {
                                	print_r($row);
                                }
                                print_r($pdoFetch->getTime());
                                

                                Вот, что выдаёт:


                                Может, у тя закэшировалось что-то?
                                1. Сергей Шлоков 28 сентября 2013, 18:13 # 0
                                  Почистил все кеши. Даже мусор сходил выбросил. Теперь все работает. Сорри за беспокойство.=)
                                  1. Василий Наумкин 28 сентября 2013, 18:55 # 0
                                    Явно мусорное ведро на кухне мешалось =)
                                    1. Сергей Шлоков 30 сентября 2013, 10:24 # 0
                                      Василий, не будет наглостью попросить содержимое файла index.php. А то все проверяю через консоль твоего «друга» философа. Не всегда удобно.
                                      1. Василий Наумкин 30 сентября 2013, 10:47 # 0
                                        Index.php лежит в корне сайта.
                                        Мой скриптик нужно положить туда же и он подключит через него класс modX.

                                        Это обычный запуск в MODX_API_MODE.
                                        1. Сергей Шлоков 30 сентября 2013, 10:48 # 0
                                          Отвечаю за тебя: — Это суперсекретный файл, который лежит в корне. Продается за большие деньги.
                                          Я: — А, понятно.
                                          :))
                                          1. Сергей Шлоков 30 сентября 2013, 10:50 # 0
                                            Уже догадался. Спасибо. Сам понимаешь — сначала надо спросить, а потом подумать. :))
                                            И за ссылочку спасибо.
                        2. Ilya Ev 29 сентября 2013, 17:24 # 0
                          Небольшой баг pdoCrumbs у себя заметил.
                          цепочка навигации в товаре (minishop2) принимает значение товара
                          т.е. должно быть главная — раздел — раздел — товар
                          а выходит главная — товар — товар — товар
                          причем только имя ссылки корректные.
                          только на странице товара в разделах все ок.
                          похоже неверно берет значение [[+menutitle]], значение [[+link]] корректно.
                          пример вызова
                          &tpl=`@INLINE [[+menutitle]]`
                          1. Василий Наумкин 29 сентября 2013, 18:17 # 0
                            Это баг MS2.

                            Обнови его, плюс можно обновить и pdoTools — там тоже парочка исправлений.
                            1. Ilya Ev 29 сентября 2013, 18:23 # 0
                              Да баг пропал, здорово)
                          2. Павел Левин 30 сентября 2013, 22:12 # 0
                            Баг в pdoCrumbs, шаблон tplUp не понимает вызов контекста.

                            Пример:
                            @INLINE <span class="link-up">↑ <a href="[[++base_url]][[+uri]]">[[+pagetitle]]</a></span>
                            соответственно [[++base_url]] игнорируется, хотя остальные шаблоны с успехом понимают его.

                            — Ведь ошибка? или так и задумано?
                            1. De Ribaskin 01 октября 2013, 15:50 # 0
                              Какой то непонятный баг с параметром &resources. Если я его задаю в наборе параметров — он не выполняется.
                              Если задаю вот так:
                              [[!pdoResources@Список_Участников? &resources=`[[*spisok_uchastnikov?]]`]]
                              то работает
                              1. Воеводский Михаил 01 октября 2013, 15:54 # 0
                                Это не баг компонента, а работа движка. Сталкивался с тем же самым в &startId от Wayfinder. Как я понимаю, наборы параметров просто не могут иметь никаких динамических данных.
                                1. De Ribaskin 01 октября 2013, 16:00 # 0
                                  Ясно, буду знать :)
                              2. Peter Zenin 03 октября 2013, 12:40 # 0
                                Приветствую, Василий!
                                Есть ли возможность сгруппировать результаты, чтобы показывал только результаты с разными значениями тв?
                                Мне надо взять список разных тв, которые есть у ресурсов.

                                Что-то вроде:
                                'groupby' => 'TemplateVars.material',
                                Хотя это не работает…

                                Вот код, который берет все, что нужно по LIKE, но еще хочется сгруппировать чтобы material — были только разные:

                                $currentId = 18933;
                                $tvName = 'material';
                                
                                $class = 'modResource';
                                
                                define('MODX_API_MODE', true);
                                require dirname(dirname(dirname(dirname(dirname(__FILE__))))) . '/index.php';
                                
                                // include pdoFetch
                                if (!$modx->loadClass('pdofetch', MODX_CORE_PATH . 'components/pdotools/model/pdotools/', false, true)) {return false;}
                                $pdoFetch = new pdoFetch($modx);
                                
                                $where = array(
                                    'category:LIKE' => "%#$currentId#%",
                                );
                                
                                // Fields to select
                                /*$resourceColumns = array_keys($modx->getFieldMeta($class));
                                if (empty($includeContent)) {
                                    $key = array_search('content', $resourceColumns);
                                    unset($resourceColumns[$key]);
                                }*/
                                $resourceColumns = array('id','pagetitle');
                                $select = array($class => implode(',',$resourceColumns));
                                
                                // Default parameters
                                $options = array(
                                    'class' => $class,
                                    'includeTVs' => 'material,category',
                                    'where' => $modx->toJSON($where),
                                    'select' => $modx->toJSON($select),
                                    //'groupby' => 'material',
                                    //'groupby' => 'TemplateVars.material',
                                    'return' => 'data',
                                    'limit' => 270
                                );
                                
                                //var_dump($options); die;
                                
                                $pdoFetch->setConfig($options, false);
                                
                                $output = $pdoFetch->run();
                                
                                var_dump($output);
                                1. Peter Zenin 03 октября 2013, 12:56 # 0
                                  Извиняюсь, разобрался…

                                  'groupby' => 'TVmaterial.value',
                                2. Evgeny Epifanov 03 октября 2013, 19:03 # 0
                                  Приветствую.
                                  Сегодня обновился, все вроде нормально работает, но на сервере полно ошибок:
                                  PHP Notice: Array to string conversion in /site/core/components/pdotools/model/pdotools/pdotools.class.php on line 235
                                  Что это? Или просто забить?
                                  1. Peter Zenin 03 октября 2013, 21:58 # 0
                                    Это PHP 5.4 :-)
                                  2. Пётр Молчанов 04 октября 2013, 09:45 # 0
                                    А у меня в логах полно такой ошибки:
                                    Could not load package metadata for package pdotools.
                                    Пробовал переустановить пакет, но не помогло…
                                    1. Василий Наумкин 04 октября 2013, 10:15 # 0
                                      А у меня нет ошибок в логе.
                                    2. Сергей Шлоков 21 октября 2013, 11:51 # 0
                                      Василий, подскажи, пожалуйста, как правильно написать join? Осеня плёха бес аписания, насяльника :)
                                      1. Василий Наумкин 21 октября 2013, 12:47 # 0
                                        Вот здесь в конце есть пример.
                                      2. Руслан Кундиус 01 ноября 2013, 17:05 # 0
                                        А можно leftJoin присоединить просто таблицу из базы, например site_tmplvar_contentvalues?
                                        1. Василий Наумкин 01 ноября 2013, 17:06 # 0
                                          Можно.
                                          1. Руслан Кундиус 01 ноября 2013, 18:38 # 0
                                            чёт не соображу ка как это делается, может мануалы какие то есть?
                                              1. Руслан Кундиус 01 ноября 2013, 22:15 # 0
                                                спасибо, собственно я и заметку эту читал и сам innerjoin использовал при выводе фоток Gallery, просто не сразу дошло где смотреть название объекта нужной мне таблицы..)
                                        Добавление новых комментариев отключено.