Modx Evo Ditto

Ditto - перебирает страницы или у "заданного родителя(ей)", или из списка "ID" страниц. И применяет к каждому какой-нибудь шаблон для отображения. В принципе это же можно делать и в Wayfinder. Но...

У Ditto - своя ниша. Если Waifinder хорош для меню и деревьев, то Ditto, в частности, для списка товаров как ведомая выборка. Т.е., например, когда слева есть дерево каталогов и при выборе каталога уже с помощью Ditto выводятся все товары как этого каталога, так и подкаталогов(если задана соответствующая глубина выборка "depth").

Постраничный вывод("пагинация") с набором параметров для этого. Ограничение числа выводимых страниц. И много еще чего. Это все прекрасно описано в доках.

А не ясна была работа extenders. Это реально бесит, когда читаешь доки, а тебе дают куски кода. Типа вставь себе и получишь такой то результат. Потом туманно было с кастомизацией выборки. Фильтры, конечно, хорошо. Но когда у тебя десяток тысяч товаров и обрабатывать каждую запись фильтром как то не айс. Ну тем, кто понимает, конечно. Почитав неделю кучу статей, как всегда пришлось лезть в исходник и многое сразу прояснилось.

Extenders - как можно догадаться, расширения. Почитав родные доки и посмотрев примеры - ничего не понял. И поначалу просто проигнорировал. Но потом столкнулся с необходимостью повлиять на выборку и вспомнил про эту штуку.

  • Параметр - "extenders". Лежат в папке "extenders". Можно из произвольной папки, если задать через "@File ........."
  • Вызывается до выборки. Можно влиять на:
    • $placeholders = array(); - можно добавить в массив свои плейсхолдеры через параметры или тут. Но это не плейсхолдеры для подстановки. Это функции обратного вызова. В качестве параметра передается массив с данными обрабатываемого в цикле (не текущего) ресурса(страницы). И там уже назначаются плейсхолдеры для tpl.
    • $filters = array("custom"=>array(),"parsed"=>array()); - в недрах Ditto есть два вида фильтров:
      • $filters["parsed"][] = array("name" => array("source"=>$source,"value"=>$value,"mode"=>$mode)); - это то, что задается в параметрах.
      • $filters["custom"][] = array("source","callback_function"); - вот сюда можно добавить свой. "source" - поле для фильтрации.
    • $orderBy = array('parsed'=>array(),'custom'=>array(),'unparsed'=>$orderBy); Содержит все критерии сортировки. Если тут назначит свою - это отменит текущую сортировку ('unparsed'=>$orderBy).
      • $orderBy["parsed"][] = array("sortBy","sortDir"); - можно добавить поля и порядок
      • $orderBy["custom"][] = array("sortBy","callback_function"); - для сортировки вызывается функция
  • "tagging" - этот extender загружается сразу, если есть переменная "$tagData"
  • Если имя начинается с "@FILE", то грузится так - $extender_path = $modx->config['base_path'].trim(substr($extender, 5)); иначе $extender_path = $ditto_base."extenders/".$extender.".extender.inc.php";
  • Найденный extender просто подключается - include($extender_path); До итерации по страницам. Смысл - определить функции обратного вызова и передать их имена в итератор через глоабльный массив $placeholders. Зто круто, конечно. Но понял я смысл этого через несколько месяцев. Изучая исходники. Ну нигде нет внятного объяснения - как все работает.

Про фильтрацию через extenders лучше сразу забыть и не вспоминать. Ну это мое мнение. А вот плейсходеры это здорово. Позволяет вклиниться в обработку шаблона единицы выборки. Механизм такой(на примере "summary.extender.inc.php"):

  • $placeholders['summary'] = array("introtext,content","determineSummary","@GLOBAL ditto_summary_type"); - "determineSummary" - имя функции, которая будет вызвана в итерации перед обработкой шаблона. В ditto->render(...) эти функции обрабатываются вот так:
    $customPlaceholders = $ph;
    foreach ($ph as $name=>$value) {
    	if ($name != "*") {
    		$placeholders[$name] = call_user_func($value[1],$resource);
    		unset($customPlaceholders[$name]);
    	}
    }
    где $ph- это наш $placeholders
    в итоге имеем $placeholders['summary'] = call_user_func("determineSummary",$resource);
        
    Потом запускаются другие(оставшиеся) обратки
    foreach ($customPlaceholders as $name=>$value) {
    	$placeholders = call_user_func($value,$placeholders);
    }
        
    т.е. если в экстендере написать:
    $placeholders['srfdfdfsdfs'] = "myFunk";
        
    то вызовется(если определена) функция myFunk и как параметры получит $placeholders. Трудно сказать зачем это. Причем ключ не важен.
  • $placeholders - это "локальные" плейсхолдеры ditto, никак не связанные с $modx->placeholders
  • $output = $this->template->replace($placeholders,$template); - т.е. просто заменяет свои (дитовские) плейсхолдеры через str_replace('[+.....+]',$values,$tpl);
  • Функция determineSummary($resource) делает свое дела и возвращает кусок текста. Проще говоря html. В итоге в итерации до обработки шаблона имеем:
            
    $placeholders['summary']="Я из лесу вышел ...";        
    
  • Итого (при использовании конфига):
    1. В конфигурации - $extenders[]='@FILE /assets/...../dittoExt.php';
            Именно так, потому что на момент загрузки конфига $extenders уже массив, а не строка. 
            Т.е. это ошибка ditto, не смертельная.
    2. В /assets/...../dittoExt.php -
    $placeholders['imgRealPH'] = array("","imgReal");
    function imgReal($r) {
     $out="";
     // $r - массив переменных ресурса. В котором не все поля, а те что нужно. 
     //Дитто их вычисляет, сканируя шаблон. Напр. $r['pagetitle']. 
     //А tv 'price' -  $r['tvprice']
      ...... ..... ....
     return $out; 
    }	
    3. В шаблоне - [+imgRealPH+]
    Т.е. можно назначить свой вычисляемый плейсхолдер. 
    
    Хоть все и работает, но надо признать - сделано через "задницу". Молчу уж про "прозрачность" реализации))). Так мне понадобились в экстендере поля, отсутствующие в шаблоне. Пришлось писать в шаблоне в любом месте:
    [[-
       [+нужноДляОбработкиПоле+]
    ]]    
    
    Только так оно появится в массиве передаваемого ресурса. Т.к. ditto сканирует шаблон и оттуда добавляет нужные поля для выборки. Горе от ума. И об этом ни слова в доках.

На SQL можно повлиять только через параметр where. Недостаток: применяется только к полям ресурса. Невозможно использовать TV.

Сама выборка - $resource = $ditto->getDocuments($documentIDs, $dbFields, $TVs, $orderBy, $showPublishedOnly, 0, $hidePrivate, $where, $queryLimit, $keywords, $randomize, $dateSource);

    
    
    $where= ($where == "") ? "" : 'AND sc.' . implode('AND sc.', preg_replace("/^\s/i", "", explode('AND', $where)));
    
    
Т.е. сначала парсит все переданные $where в массив по "AND", удаляет пробел в начале и удаляет пробел в начале у каждого элемента. И снова преобразует в строку но прежние "AND" заменяются на "AND sc.". Мне это загубило последнюю надежду. Надеялся, что можно впихнуть что-то типа:
    
   
   [[Ditto? 
   .......
   &where=`and id in (select idcontent from ..... where ..... and .....)`
   .......
   ]]
   
   может и возможно, но избегая "AND" в подзапросе.
    

Очень забавно реализовано выборка TV.

  • Выбираются все ID ресурсов.
  • Выбираются все TV по этим id. Форматируются
  • Выбираются сами ресурсы
  • Потом в цикле по ресурсам в массиве ищутся TV и присоединяются к выборке

Ditto, конечно, суров. Но я ожидал большего. Явно не хватает возможности подставить свой SQL. Потом этот "where", в котором запрещены TV. А там как раз и хранится цена и прочие важные параметры, по которым и нужна выборка. Вот и пишут дизайнеры вызовы типа(если надо выбрать товары в интервале цен от 45 до 102) :

    
[[Ditto?
&id=`im[*id*]`
&parents=`1`
&depth=`4`
&hideFolders=`1`
&outerTpl=`image-menu-wrapper`
&tpl=`image-menu-row`
&noResults=` `
&orderBy=`menuindex ASC`
&filter=`price,45,5|price,102,6`
]]
В итоге ditto выберет все 10 000 товаров, найдет и пропарсит все 10 000 TV, привязанных к ресурсу. Потом к каждой записи применит фильтр и отбросит из выборки совпавшие. И этот принцип фильтрации часто ставил меня в тупик. Надо выбрать все с шаблоном 5. Логично назначить фильтр "шаблон=5". Но не для Ditto. Он в этом случа просто отбросит все ресурсы с шаблоном 5. Для него надо "шаблон НЕ= 5". Лишний напряг для мозга. Ну да ладно.

Как представлю, как вся эта выборка обрабатывается ради десятка товаров... Хичхок отдыхает.

Extenders - это очень хорошо, но реализовано очень неочевидно. Чтоб понять - пришлось "грызть" исходники. Нет чтоб прямо назначить обратки, как в eForm, тут же через "$placeholders", которые не $modx->placeholders.

Еслиб не пагинация и прочие удобства - посоветовал бы забить на Ditto. Но попробовав Doclister(ужас просто) - вернулся к Ditto. Спасает возможность указать перечень ID. T.e., если приспичит - можно сначала выбрать через обычный SQL все нужные ID и передать как параметр в Ditto. А вообще - лучше написать итератор на чистом PHP. Получите и эффективность, и скорость, и еще море удовольствия))).


Комментарии 0






Разрешённые теги: <b><i><br>Добавить новый комментарий: