Оглавление

pdoTools — основной класс компонента, который наследуют все остальные (кроме pdoParser — он наследует modParser).

Инициализация

Простая инициализация класса:

$pdoTools = $modx->getService('pdoTools');

Этот метод всегда вернёт оригинальный pdoTools.

Вы можете указать другой путь к классу в системных настройках, чтобы его подменить и расшить, тогда инициализировать лучше так:

$fqn = $modx->getOption('pdoTools.class', null, 'pdotools.pdotools', true);
if ($pdoClass = $modx->loadClass($fqn, '', false, true)) {
    $pdoTools = new $pdoClass($modx, $scriptProperties);
}
elseif ($pdoClass = $modx->loadClass($fqn, MODX_CORE_PATH . 'components/pdotools/model/', false, true)) {
    $pdoTools = new $pdoClass($modx, $scriptProperties);
}
else {
    $modx->log(modX::LOG_LEVEL_ERROR, 'Could not load pdoTools from "MODX_CORE_PATH/components/pdotools/model/".');
    return false;
}
$pdoTools->addTime('pdoTools loaded');

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

Ведение лога

Важная особенность pdoTools — он умеете вести лог того, что делает. Для этого вам доступны методы

Например, вот этот код:

$pdo = $modx->getService('pdoTools');
$pdo->addTime('pdoTools инициализирован');
print_r($pdo->getTime());

Выведет:

0.0000150: pdoTools инициализирован
0.0000272: Total time
1 572 864: Memory usage

То есть, вы можете подключать pdoTools в своих сниппетах, просто для логирования событий. Понятно дело, что его сниппеты сами всё пишут в лог, и как правило, менеджер может его почитать параметром &showLog=`1`.

Кэширование

pdoTools умеет кэшировать произвольные данные на время выполнения скрипта. Вы тоже можете этим пользоваться.

Например, вам по ходу работы сниппета нужно закэшировать имена юзеров, чтобы не выбирать их каждый раз. Тогда вы можете проверить, есть ли нужный юзер в кэше, и если нет — получить его:

foreach ($users as $id) {
    $user = $pdo->getStore($id, 'user');
    if ($user === null) {
        if (!$user = $modx->getObject('modUser', $id)) {
            $user = false;
        }
        $pdo->setStore($id, $user, 'user');
    }
    elseif ($user === false) {
        echo 'Не могу найти юзера с id = ' . $id;
    }
    else {
        echo $user->get('username');
    }
}

В этом коде мы сохраняем юзеров в отдельный namespace user, чтобы не мешать другим сниппетам, и проверяем наличие юзера в кэше. Обратите внимание, что по условиям примера, кэш может вернуть или null (юзер еще не получался), или false (юзер не найден). В любом случае, запрос в БД будет только один на каждого юзера.

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

Есть и более продвинутое кэширование, методами MODx:

Здесь данные уже сохраняются на диск, время кэширования можно передавать в массиве параметров:

$pdo = $modx->getService('pdoTools');
$options = array(
    'user' => $modx->user->get('id'),
    'page' => @$_REQUEST['page'],
    'cacheTime' => 10,
);
$pdo->addTime('pdoTools загружен');
if (!$data = $pdo->getCache($options)) {
    $pdo->addTime('Кэш не найден, генерируем данные');
    $data = array();
    for ($i = 1; $i <= 100000; $i ++) {
        $data[] = rand();
    }
    $data = md5(implode($data));
    $pdo->setCache($data, $options);
    $pdo->addTime('Данные сохранены в кэш');
}
else {
    $pdo->addTime('Данные загружены из кэша');
}
print_r($data);

Таким образом, в зависимости от юзера и страницы будут получены какие-то данные и сохранены в кэш. Если зайдёт другой юзер — он получит свой кэш.

В первый раз наш код покажет примерно такое

0.0000281: pdoTools загружен
0.0004001: No cached data for key "default/e713939a1827e7934ff0242361c06b4b10c53d97"
0.0000079: Кэш не найден, генерируем данные
0.0581820: Saved data to cache "default/e713939a1827e7934ff0242361c06b4b10c53d97"
0.0000181: Данные сохранены в кэш
0.0586412: Total time
1 835 008: Memory usage

А затем вот такое:

0.0000310: pdoTools загружен
0.0007479: Retrieved data from cache "default/e713939a1827e7934ff0242361c06b4b10c53d97"
0.0000081: Данные загружены из кэша
0.0007918: Total time
1 572 864: Memory usage

Как видите, pdoTools и сам прекрасно пишет работу с кэшем в лог, так что вам можно это не логировать.

Утилиты

Здесь всего два метода.

makePlaceholders

makePlaceholders (array $data, string $plPrefix, string $prefix ['[[+'], string $suffix [']]'], bool $uncacheable [true]) — Принимает массив ключ => значение и возвращает два массива плейсхолдеры => значения, используется для шаблонизации.

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

$data = array(
    'key1' => 'value1',
    'key2' => 'value2',
);

$pls = $pdo->makePlaceholders($data);
print_r($pls);

Результат:

Array
(
    [pl] => Array
        (
            [key1] => [[+key1]]
            [!key1] => [[!+key1]]
            [key2] => [[+key2]]
            [!key2] => [[!+key2]]
        )

    [vl] => Array
        (
            [key1] => value1
            [!key1] => value1
            [key2] => value2
            [!key2] => value2
        )

)

Дальше можно обработать какой-то html шаблон вот так:

$html = str_replace($pls['pl'], $pls['vl'], $html);

buildTree

buildTree (array $resources) — строит иерархическое дерево из массива ресурсов, используется pdoMenu.

$pdo = $modx->getService('pdoFetch');
$resources = $pdo->getCollection('modResource');
$tree = $pdo->buildTree($resources);
print_r($tree);

И вы увидите дерево ресурсов своего сайта. Обратите внимание, что для использования getCollection() нужно загружать pdoFetch.

Шаблонизация (работа с чанками)

Это, наверное, самая интересная часть класса pdoTools.

Метод здесь всего один — это getChunk(), однако вся его реализация рассчитана на максимальную производительность и функциональность.

Все плейсхолдеры в чанки, какие только может, обрабатывает pdoParser. Условие одно — плейсхолдер должен быть без условий и фильтров. То есть:

Еще getChunk в pdoTools умеет работать с разными типами чанков:

Рабочий пример:

$tpl = '@INLINE <p>[[+param]] - [[+value]]</p>';
$res = '';
for ($i = 1; $i <= 10000; $i++) {
    $pls = array('param' =>$i, 'value' => rand());
    $res .= $pdo->getChunk($tpl, $pls);
}
print_r($pdo->getTime());
print_r($res);

Вот вам и простейшая шаблонизация при помощи pdoTools.

Этот код выводит 10 .000 строк всего за 0.17 секунды! Причем, неважно, что чанк @INLINE, обычный работает с той же скоростью. А если заменить $pdo->getChunk() на $modx->getChunk(), то выходит уже 8 секунд!

То есть, в данном конкретном примере парсинг чанков MODx медленнее pdoTools в 3000 раз — 8 секунд, против 0.17. Это говорит о том, что нужно максимально упрощать свои чанки, поменьше использовать условий и подключать pdoTools.

Чем же можно заменить условия? Самые простые «пусто\не пусто» заменяются «быстрыми плейсхолдерами». Работает это так:

<!--pdotools_tag значение, если тег не пуст-->
<!--pdotools_!tag значение, если тег пуст, появилось только в версии 1.9.3-->

Как видите, комментарий именуется исходя из префикса pdotools_ и имени тега. Префикс меняется параметром &nestedChunkPrefix=``.

Почему именно такие условия, зачем держать быстрый плейсхолдер в комментарии? Очень просто — это на случай обработки чанка не pdoTools.

Пример:

$tpl = '@INLINE
    <p>[[+tag]]</p>
    <!--pdotools_tag [[+tag]] - значение, если тег не пуст-->
    <!--pdotools_!tag значение, если тег пуст, появилось только в версии 1.9.3, выпущенной сегодня-->
';

$pls = array('tag' => 1);
echo $pdo->getChunk($tpl, $pls);

$pls = array('tag' => 0);
echo $pdo->getChunk($tpl, $pls);

Получаем

1 - значение, если тег не пуст

значение, если тег пуст, появилось только в версии 1.9.3

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

Есть еще один интересный параметр обработки плейсхолдеров — &fastMode. Он выключает передачу плейсхолдеров в родной парсер MODx, и то, что не смог обработать pdoParser просто будет вырезано.

В последних версиях pdoTools его использовать нет нужды, потому что если pdoParser всё обработал, и в чанке не осталось ни одного [[+tag]], то он сразу отдаёт результат, не трогая modParser. Но вы можете его включить как принудительное требование для тех людей, которые меняют чанки — чтобы они не могли использовать трехэтажные конструкции.

С версии 2.0 pdoTools включает в свой состав шаблонизатор Fenom, что позволяет отказаться от тегов MODx и писать в чанках более продвинутую логику. Про работу с Fenom читайте в разделе pdoParser.