[pdoTools] Версия 1.7.0-pl - 4 новых сниппета

Стабильная версия 1.7.0 реализует хитрый план по захвату рынка ускорению всех сайтов на MODX Revolution.

Помимо двух уже привычных сниппетов pdoResources и pdoUsers, у нас появляется 4 новых:
  • pdoCrumbs — вывод хлебных крошек, заменяет BreadCrumb.
  • pdoField — получение любого поля произвольного ресурса, или его родителя. Заменяет getResourceField и UltimateParent.
  • pdoSitemap — скоростная генерация карты сайта. Заменяет GoogleSitemap.
  • pdoNeighbors — вывод соседних документов, от указанного ресурса. Не знаю, может что и заменяет, но делалось без оглядки на другие решения.
Все сниппеты отличаются высокой скоростью работы, поддержкой ТВ и других примочек pdoTools. Заодно презентую новый раздел на сайте с документацией по компоненту — уже пора.

Проверка прав на доступ к документам

pdoTools работает с базой данных напрямую, без объектов xPDO. Помимо очевидных достоинств (гибкость и скорость) этот метод таит в себе и серьёзный недостаток — отсутствие проверки прав юзера на работу с документами.

Новая версия решает этот вопрос, добавляя параметр &checkPermissions=``, в котором вы можете перечислять нужные права:
[[!pdoResources?
	&checkPermissions=`list`
]]
Обратите внимание на 2 особенности включения этого режима:
  • Он замедляет работу. Каждый результат выборки превращается в объект и запускает метод modResource::checkPolicy(). Конечно, работает вся равно быстрее аналогов, но замедление ощутимо.
  • Права проверяются после выборки, а это значит, что при указанном лимите = 10, на странице может быть меньше документов, потому что несколько из них не прошли проверку.
    Проверять до выборки или во время нее пока не получается. Немного успокаивает тот факт, что getResources работает ровно так же, но в отличии от pdoTools — даже не меняет плейсхолдер [[+total]] при исключении.
В общем, возможность есть, а использовать её или нет — решайте сами.

Подготовка данных перед выводом

Новый параметр, доступный всем снипптам, которые используют pdoTools::getChunk() — prepareSnippet.

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

Внутри сниппета можно использовать:
  • $modx — класс modX со всеми методами
  • $pdoTools — класс pdoTools со всеми методами. Для кэшировании данных при работе можно использовать методы
    $pdoTools->setStore('имя', 'значение');
    // и
    $pdoTools->getStore('имя');
  • $row — массив с текущим документом, который готовится на оформление в чанк.

Сниппет обязан вернуть массив данных, закодированный в строку:
  • serialize($row)
  • или json_encode($row)

Пример вывода ресурсов с изменением pagetitle случайным образом:
[[!pdoResources?
	&tpl=`@INLINE <a href="/[[+uri]]">[[+pagetitle]]</a>`
	&parents=`0`
	&prepareSnippet=`mySnippet`
]]
и сниппет подготовки mySnippet:
$row['pagetitle'] .= rand();

return json_encode($row);

Думаю понятно, какие это открывает возможности. За идеи по реализации данного метода большое спасибо Евгению Борисову и его DocLister.

Новый биндинг @TEMPLATE

После релиза версии 1.6.0 поступила просьба "сделать как у сниппета renderResources" — то есть, вывод документов, оформленных не в чанки, а в собственные шаблоны.

Не проблема, встречайте новый биндинг — @TEMPLATE. Можно указывать id шаблона или его имя или вообще ничего, тогда использоваться будет шаблон ресурса.

Оформляем все ресурсы шаблоном с id = 5
[[!pdoResources?
	&parents=`0`
	&tpl=`@TEMPLATE 5`
]]
Оформляем ресурсы тем шаблоном, который указан у них в поле template:
[[!pdoResources?
	&parents=`0`
	&tpl=`@TEMPLATE`
]]
Внимание, так как в шаблонах бывает дофига всяких чанков и сниппетов, которые могут рекурсивно вызывать и pdoResources — используйте этот биндинг осторожно, может быть fatal error.

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

Другие изменения

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

[[!pdoResources?
	&tpl=`@INLINE <div class="row [[+myclass]]">[[+pagetitle]]</div>`
	&myclass=`title`
]]

Появился метод pdoTools::parseChunk, для простой замены плейсхолдеров на значения, понимает fastMode. Разница по сравнению с getChunk заметна на больших объемах — например при генерации карты сайта.

Поэтому, pdoSitemap использует именно parseChunk, что позволяет ему выводить карту моего сайта из > 1700 страниц за 0.7 сек.

В сниппете pdoResources поправлен параметр &context. Теперь, если он не указан, ограничений по выборке в контекстах нет.

Заключение

Новая версия уже в репозитории, можно обновляться.

Ядро pdoTools изменено не сильно, старые сниппеты тоже почти не трогал — багов быть не должно. Зато в новых сниппетах они вполне могут быть.

Пишите, буду готовить версию 1.7.1 =)

Из наполеоновских планов остался сниппет pdoMenu, который заменит Wayfinder, но он будет позже — на этой неделе мне нужно поработать за деньги.

Следующая заметка
[miniShop2] Версия 2.1.0-pl
Предыдущая заметка
[HybridAuth] Версия 0.8.0-pl работа с контекстами

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

  1. Володя 23 сентября 2013, 09:20 # 0
    Василий привет!
    пробую pdoNeighbors, ссылки на предыдущий и следующий генерирует отлично, пожелание есть одно — сделай по возможности при отсутствии ссылки на следующий документ, чтоб была ссылка на первый. То есть зациклить)
    И что то у меня ссылка на родителя не отображается, вернее она выводится, но там сам документ, а не родитель.
    спасибо!!!
    1. Алексей Карташов 23 сентября 2013, 21:28 # 0
      Да, поддерживаю — зацикливать нужно! Чтобы внутреннее ссылочное pr нагоняло).

      И спасибо за релиз) Теперь это просто нереально крутой комбайн =)
    2. Виктор Банев 23 сентября 2013, 10:20 # 0
      ой, таки спасибо вам большое за @TEMPLATE :)
      благодаря pdoTools количество пакетов в моих установках за последнюю неделю сократилось на четверть
      1. Сергей Росоловский 23 сентября 2013, 12:37 # 0
        Здравствуйте, а вот примеры и документация по использованию pdoTools есть? Или эта информация в платном доступе?
        1. Воеводский Михаил 23 сентября 2013, 12:39 # 0
          Ответ в самой первой части заметки.
          1. Сергей Росоловский 23 сентября 2013, 12:44 # 0
            Собственно, я хотел узнать как через pdoTools работать с пользовательськими таблицами в БД?
            1. Василий Наумкин 23 сентября 2013, 12:52 # 0
              Для подписчиков есть старая заметка с основами работы.

              Для всех остальных есть исходный код библиотеки и 6 сниппетов на github.
        2. Комментарий был удален.
          1. Василий Наумкин 23 сентября 2013, 16:34 # 0
            Паша, постить картинки на 5000px по ширине в нормальном обществе не принято.

            Особенно, если есть удобный сервис для их подготовки.
          2. Сергей Колоней 23 сентября 2013, 17:44 # 0
            Здравствуйте Василий,
            Во-первых, хотел сказать спасибо за ту ошеломительную работу которую вы проделали для улучшения и популяризации ModX, и все прекрасные инструменты, которые вы разработали :)
            Во-вторых, небольшой багрепорт: если указать parents=`0` в сниппете getResources, то сниппет считает что parents не был указан, и показывает все подряд. Однако parents=`0` — это валидное значение, когда мы хотим получить список документов внутри контекста web, начиная с самых верхних. Заменил
            if (!empty($parents)) { ...
            на
            if (isset($parents)) { ...
            — заработало как надо, хотя по идее эту проверку вообще можно убрать, учитывая строку выше
            if (!isset($parents) || $parents == '') {$parents = $modx->resource->id;}
            1. Василий Наумкин 23 сентября 2013, 18:17 # 0
              Да, это мой косяк, тянется уже давно.

              По идее, надо перейти на &parents=`-1`, но это поломает работу текущих сайтов. Пока думаю, как бы сделать.
              1. Евгений Борисов 23 сентября 2013, 19:38 # 0
                Я после аналогичного багрепорта ввел ignoreEmpty. Т.е. Если parents=0 и ignoreEmpty=0, то ничего не показываем. Если parents=0 и ignoreEmpty = 1, то показываем все из корня.
                1. Василий Наумкин 23 сентября 2013, 19:43 # 0
                  Вариант, спасибо.
            2. Сергей Шлоков 23 сентября 2013, 18:16 # 0
              Круто. У меня из дополнений остались FormIt, Login и Wayfinder. Так скоро весь MODX будет на твоих компонентах, по крайней мере, в рунете. Спасибо за клевые вещи.
              1. Василий Наумкин 23 сентября 2013, 18:18 # 0
                Login можно смело заменить на Office + HybridAuth =)

                Ну и Wayfinderу недолго осталось.
                1. Сергей Шлоков 23 сентября 2013, 19:16 # 0
                  HybridAuth у меня есть (для любителей комментировать). А Login нужен для регистрации, смены паролей и т.п. Office вроде на это не заточен. Может я уже ошибаюсь. :)
              2. Сергей Лелеко 23 сентября 2013, 19:54 # 0
                У меня такой вопрос, согласно документации что есть по основным параметрам pdoResources в его состав так же как и в get Resources входят такие ключевые параметры как includeContent, resources и прочее. Таким образом, простеший вызов [[pdoResources? &resources=`110,111,112,113,114` &tpl=`sliderTpl` &includeContent=`1`]] должен работать? по сути был аналогичный но с getResources (рабочий). Внутри чанка у меня только [[+content]].
                1. Василий Наумкин 23 сентября 2013, 20:08 # 0
                  В чем вопрос?

                  Возьми да проверь.
                  1. Сергей Лелеко 23 сентября 2013, 20:12 # 0
                    Проверил, не работает. Пробовал кстати убирать чанк вообще, должны вывестись по идее все параметры, но не выводятся.
                    1. Василий Наумкин 23 сентября 2013, 20:14 # 0
                      Не судьба.
                2. Владимир 23 сентября 2013, 22:24 # 0
                  pdoTools бесплатное но уж очень важное расширение, потому примите очередные благодарности на счет по «программе поощрения автора» :))
                  Спасибо Василий, и за помощь с галереей (вывод отдельными плейсхолдерами картинок) то же.
                  1. Василий Наумкин 24 сентября 2013, 04:38 # 0
                    На здоровье!

                    А pdoTools незаметно стал моим самым важным вкладом в развитие MODX.
                  2. Сергей Шлоков 08 октября 2013, 10:39 # 0
                    Василий, а pdoMenu будет работать только с таблицей ресурсов или как pdo::getObject/getCollection с любыми?
                    1. Василий Наумкин 08 октября 2013, 11:33 # 0
                      Скорее всего только с ресурсами — так проще и надежнее.

                      Делать универсально нет необходимости:
                      1. Это не нужно 99% пользователей
                      2. В pdoTools довольно просто писать свои сниппеты для любых таблиц.
                      1. Сергей Шлоков 08 октября 2013, 12:07 # 0
                        Понял. Спросил, чтобы не делать лишнюю работу. Значит буду свое ваять. Нужно многоуровневое дерево ресурсов строить.
                        1. Василий Наумкин 08 октября 2013, 12:43 # 0
                          Нужно строить многоуровневое дерево ресурсов не из таблицы ресурсов?

                          Оригинально.

                          В любом случае, посмотри на метод построения иерархического дерева — может пригодиться.
                          1. Сергей Шлоков 08 октября 2013, 13:18 # 0
                            Шютник ви, батенька. А я, к сожалению, не столь оригинален. У меня свои собственные ресурсы есть и храню я их в сберкассе в своей табличке. =)
                            За ссылку спасибо. Очень даже пригодится.
                            1. Василий Наумкин 08 октября 2013, 13:21 # 0
                              Я про то, что тогда это не ресурсы. В том смысле, что они не наследники modResource.

                              А для своих таблиц подойдет сочетание pdoFetch::getCollection() + pdoTools::buildTree(), а дальше только оформить.

                              По такому принципу будет и pdoMenu работать.
                              1. Сергей Шлоков 08 октября 2013, 13:25 # 0
                                Как раз так и хотел только что добавить, что собственно нужна то всего одна строчка — подать массив в pdoTools::buildTree() и получить дерево. Мне больше и не надо. Во-истину, нужная вещь pdoTools. =)
                                П.С. Мои ресурсы совсем не модРесурсы. Просто обзываю по привычке. :)
                                1. Василий Наумкин 08 октября 2013, 13:26 # 0
                                  Я скоро еще добавлю в этот метод параметры, чтобы указывать имена ключей в массиве.

                                  Не у всех же везде же id и parent.
                                  1. Василий Наумкин 08 октября 2013, 14:00 # 0
                                    Проверил и исправил метод.

                                    Код для проверки:
                                    $pdo = $modx->getService('pdoFetch');
                                    $res = $pdo->getCollection('modResource');
                                    $tree = $pdo->buildTree($res);
                                    
                                    print_r($tree);

                                    Выберет и выведет все ресурсы сайта иерархическим деревом.
                                    1. Сергей Шлоков 08 октября 2013, 19:57 # 0
                                      Подтверждаю — выбирает и выводит. Забираю.
                                      1. Сергей Шлоков 09 октября 2013, 18:17 # 0
                                        В силу своей неопытности (стаж программирования чуть больше полугода :) не смог воспользоваться
                                        $pdo->buildTree($res);
                                        Не знаю, как вывести подуровни. Чуть-чуть не умею. :)
                                        Создал свой вариант на базе pdoTools
                                            public function get_tree($tmp=array(),$config=array()) {
                                                if (!isset($config['level'])) {
                                                    $config['level']=0;
                                                }
                                                $this->config = array_merge($config,$this->config);
                                                $cats = array();
                                                foreach($tmp as $cat){
                                                    $cats[$cat['parent']][] =  $cat;
                                                }
                                                $tree= $this->build_tree($cats,0,$this->config['level']);
                                                return $tree;
                                            }
                                        
                                            public function build_tree($cats,$parent=0,$level=0){
                                                if(is_array($cats) and isset($cats[$parent])){
                                                  if ($this->config['level']==0 || $level <= $this->config['level']) {
                                                    $outerTpl= !empty($this->config['outerTpl']) ? $this->config['outerTpl'] : "@INLINE <ul>[[+wrapper]]</ul>";
                                                    $innerTpl= !empty($this->config['innerTpl']) ? $this->config['innerTpl'] : "@INLINE <li>[[+name]] [[+wrapper]]</li>";
                                                    $row['wrapper']=$output='';
                                                    foreach($cats[$parent] as $cat){
                                                        $cat['wrapper'] .= $this->build_tree($cats,$cat['id'],++$level);
                                                        $row['wrapper'] .= $this->parseChunk($innerTpl,$cat);
                                                    }
                                                    $output .= $this->parseChunk($outerTpl,$row);
                                                  } else {
                                                      return '';
                                                  }
                                                }
                                                else {
                                                    return '';
                                                }
                                                return $output;
                                            }
                                        Вызываю в сниппете
                                        $config['outerTpl']=$modx->getOption('outerTpl',$scriptProperties,'@INLINE <ul>[[+wrapper]]</ul>');
                                        $config['innerTpl']=$modx->getOption('innerTpl',$scriptProperties,'spr_goods_cat.row');
                                        $pdo = $modx->getService('pdoFetch');
                                        $res = $pdo->getCollection('GoodsCategories');
                                        $tree = $pdo->get_tree($res,$config);
                                        return $tree;
                                        
                                        Может кому пригодится до того, как Василий pdoMenu сделает.
                        2. Марина 29 июля 2014, 00:40 # 0
                          Доброго времени суток! Помогите, пожалуйста, с подготовкой данных перед выводом pdoPage. Написала сниппет, который работает с pdoResources. Но никак не могу сообразить, как его использовать с pdoPage. И вообще, можно ли в pdoPage использовать параметр &prepareSnippet ????
                          Делаю так:
                          				&select=`id,pagetitle,introtext,img,skidki,price`
                          				&includeTVs=`img,skidki,price`
                          				&tvPrefix=``
                          				&prepareSnippet=`mySnippet`
                          И код самого сниппета:
                          if ($row['skidki'] == 9)
                              $row['skidki'] = '<div class="lenta1"></div>';
                          elseif ($row['skidki'] == 1)
                              $row['skidki'] ='<div class="lenta2"></div>';
                          elseif ($row['skidki'] == 2)
                              $row['skidki'] = '<div class="lenta3"></div>';
                          elseif ($row['skidki'] == 3)
                              $row['skidki']='<div class="lenta4"></div>';
                          elseif($row['skidki'] == 0)
                              $row['skidki'] = '';
                          $row['price'] = number_format($row['price'], 0, ',', ' ');
                          return serialize($row);
                          Если вызывать pdoResources с параметром prepareSnippet все отображается, как надо. Хотела сделать пангинацию, но в pdoPage ничего не выходит. В логах пишет Preparation snippet must return an array, instead of «boolean»
                          Что я делаю не так? Спасибо!
                          Добавление новых комментариев отключено.