[EVO] Переползаем с Ditto на Doclister, часть 2
В прошлой статье Grinyaha рассказал о простых примерах использования DocLister — по сути, как вместо Ditto вызвать DocLister. Я покажу пример тоже для новичков, но чуть сложнее — для понимания необходимо минимальное знание PHP.
Рассмотрим такую задачу: есть каталог товаров, некоторые из них отображаются на главной (новые товары, товары со скидкой или что-то в этом духе). Нужно при выводе указать, к какой категории относится товар.
Очевидное решение — в чанке товара написать:
Написали и написали, ничего страшного. Давайте задачу усложним: пусть будет [+longtitle+], если есть, а если нет — [+pagetitle+]:
Получилось какое-то месиво из скобочек, но можно же не писать в одну строку и тоже будет нормально.
Теперь посмотрим с другой стороны. Я взял сайт с каталогом — в каталоге 168 товаров и вывел их все.
Без DocInfo на вывод понадобилось: Запросов: 8. Время: 0,2117 s.
C DocInfo — первый вариант: Запросов: 176. Время: 0,3517 s.
Второй вариант: Запросов: 680. Время: 1,3393 s.
Понятно, что 168 товаров на странице это, скорее всего, необычная ситуация, а еще есть кэш, но все же…
Как можно решить проблему?
Вариантов тоже несколько, например создать TV-параметр и вписывать в него название категории (на каком-то сайте я такое видел) руками. Или написать плагин, чтобы не руками. Это решит проблему, но создаст некоторые неудобства. Можно написать сниппет, который будет получать сразу pagetitle и longtitle и возвращать нужное — так вернемся к 176 запросам.
Но раз появились варианты с «написать», то самое время рассказать о том, как решить такую задачу с помощью DocLister.
В DocLister вся работа по выводу выполняется контроллерами, которые содержат набор необходимых для этого функций: для документов из дерева это контроллер site_content, для внешних таблиц — onetable, для каталога shopkeeper — тоже свой контроллер. Нужный контроллер выбирается с помощью параметра &controller — по умолчанию это site_content.
Можно указать и свой контроллер — если создать по определенным правилам (это есть в документации) соответствующий файл в папке DocLister/core/controller/. Другими словами, можно скопировать файл site_content.php, переименовать, поправить и указать при вызове сниппета. Но можно поступить еще хитрее.
Создадим в указанной папке файл test_site_content.php:
Если указать теперь при вызове &controller=`test_site_content` — то ничего не случится (: Наш контроллер полностью дублирует контроллер site_content.
А в контроллере site_content есть функция (точнее метод) getDocs, эта функция получает список документов в соответствие с параметрами сниппета. Результатом работы функции будет массив: 'id' => 'pagetitle,longtitle, все поля в общем'.
Что это дает — во-первых, мы можем получить список значений parent (то есть id категории), используя который получим, в свою очередь, значения pagetitle,longtitle категории. Во-вторых, можем добавить дополнительное поле (название категории) в массив документов, чтобы DocLister использовал это поле при выводе чанка.
И при этом нет нужды переписывать код функции целиком:
Вместо DocInfo пишем в чанке [+category+] и проверяем — Запросов: 9. Время: 0,2432 s.
В таких вот вещах и раскрывается вся cила DocLister (:
Хотел привести еще пример с SimpleGallery, но там немного сложнее задача, поэтому в другой раз, заодно можно рассмотреть и prepare.
А можно уже и не рассматривать: modx.im/blog/docs/2759.html#comment24208 (:
Рассмотрим такую задачу: есть каталог товаров, некоторые из них отображаются на главной (новые товары, товары со скидкой или что-то в этом духе). Нужно при выводе указать, к какой категории относится товар.
Очевидное решение — в чанке товара написать:
[[DocInfo? &docid=`[+parent+]` &field=`pagetitle`]]
Написали и написали, ничего страшного. Давайте задачу усложним: пусть будет [+longtitle+], если есть, а если нет — [+pagetitle+]:
[[if? &is=`[[DocInfo? &docid=`[+parent+]` &field=`longtitle`]]:isempty` &then=`[[DocInfo? &docid=`[+parent+]` &field=`pagetitle`]]` &else=`[[DocInfo? &docid=`[+parent+]` &field=`longtitle`]]`]]
Получилось какое-то месиво из скобочек, но можно же не писать в одну строку и тоже будет нормально.
Теперь посмотрим с другой стороны. Я взял сайт с каталогом — в каталоге 168 товаров и вывел их все.
Без DocInfo на вывод понадобилось: Запросов: 8. Время: 0,2117 s.
C DocInfo — первый вариант: Запросов: 176. Время: 0,3517 s.
Второй вариант: Запросов: 680. Время: 1,3393 s.
Понятно, что 168 товаров на странице это, скорее всего, необычная ситуация, а еще есть кэш, но все же…
Как можно решить проблему?
Вариантов тоже несколько, например создать TV-параметр и вписывать в него название категории (на каком-то сайте я такое видел) руками. Или написать плагин, чтобы не руками. Это решит проблему, но создаст некоторые неудобства. Можно написать сниппет, который будет получать сразу pagetitle и longtitle и возвращать нужное — так вернемся к 176 запросам.
Но раз появились варианты с «написать», то самое время рассказать о том, как решить такую задачу с помощью DocLister.
В DocLister вся работа по выводу выполняется контроллерами, которые содержат набор необходимых для этого функций: для документов из дерева это контроллер site_content, для внешних таблиц — onetable, для каталога shopkeeper — тоже свой контроллер. Нужный контроллер выбирается с помощью параметра &controller — по умолчанию это site_content.
Можно указать и свой контроллер — если создать по определенным правилам (это есть в документации) соответствующий файл в папке DocLister/core/controller/. Другими словами, можно скопировать файл site_content.php, переименовать, поправить и указать при вызове сниппета. Но можно поступить еще хитрее.
Создадим в указанной папке файл test_site_content.php:
<?php
if (!defined('MODX_BASE_PATH')) die('HACK???');
include_once('site_content.php');
class test_site_contentDocLister extends site_contentDocLister
{
}
Если указать теперь при вызове &controller=`test_site_content` — то ничего не случится (: Наш контроллер полностью дублирует контроллер site_content.
А в контроллере site_content есть функция (точнее метод) getDocs, эта функция получает список документов в соответствие с параметрами сниппета. Результатом работы функции будет массив: 'id' => 'pagetitle,longtitle, все поля в общем'.
Что это дает — во-первых, мы можем получить список значений parent (то есть id категории), используя который получим, в свою очередь, значения pagetitle,longtitle категории. Во-вторых, можем добавить дополнительное поле (название категории) в массив документов, чтобы DocLister использовал это поле при выводе чанка.
И при этом нет нужды переписывать код функции целиком:
public function getDocs($tvlist = '') {
$docs = parent::getDocs($tvlist);
//поскольку мы меняем функцию getDocs, то обращаться к оригинальной функции нужно вот таким вот образом
$parents = array();
foreach ($docs as $doc) $parents[] = $doc['parent'];
//теперь у нас есть массив c id категорий
$parents = array_unique($parents);
//нам нужны только те, которые не повторяются
$parents = implode(',',$parents);
//сделали список
$table = $this->modx->getFullTableName('site_content');
//вместо $modx здесь нужно использовать $this->modx
$sql = "SELECT id,pagetitle,longtitle FROM $table WHERE id IN ($parents)";
$result = $this->modx->db->query($sql);
$result = $this->modx->db->makeArray($result);
//получили массив с id категорий и их названиями
$categories = array();
foreach ($result as $category) {
$categories[$category['id']] = empty($category['longtitle']) ? $category['pagetitle'] : $category['longtitle'];
}
//теперь перестроили этот массив, чтобы у нас было id => название, попутно выбираем что будет названием - pagetitle или longtitle
foreach ($docs as &$doc) {
$doc['category'] = $categories[$doc['parent']];
}
//добавили дополнительное поле category, с названием категории; что в массиве документов parent, то в массиве категорий id
$this->_docs = $docs;
//контроллер хранит массив документов в $this->_docs
return $docs;
//готово!
}
Вместо DocInfo пишем в чанке [+category+] и проверяем — Запросов: 9. Время: 0,2432 s.
В таких вот вещах и раскрывается вся cила DocLister (:
А можно уже и не рассматривать: modx.im/blog/docs/2759.html#comment24208 (:
10 комментариев
Для получения всех parent'ов через запятую можно воспользоваться методом implode(",", $this->getOneField('parent', true)); либо $_DL->sanitarIn($_DL->getOneField('parent', true));
Т.е. с учетом вышесказанного, итоговый код приобретает вид
Ну и наконец для того, чтобы решить поставленную задачу не обязательно создавать новый контроллер. Можно воспользоваться prepare параметром. Вот пример prepare-сниппета который делает тоже самое. Боллее подробный разбор этого кода я приводил тут
P.S. Спасибо за статью. Довольно доступно и от простого к сложному.
Методы да, упустил. Если dbQuery и sanitarIn еще вспоминаются, то getOneField первый раз вижу. Сейчас пытаюсь перейти на PHPStorm, может тогда будет проще разбираться.
Суть вопроса.
На сайте имеется личный кабинет.
В личном кабинете есть форма, к форме подключен текстовый редактор tinymce.
Из формы идет запись в свою таблицу через htmlspecialchars
В результате теги преобразовываются и записывается, например вместо
имеем (подчеркивание поставил для наглядности, чтобы не терялся смысл)
И сам вопрос — где и как правильно и корректно прописать в DocLister (контроллере onetable)обратную функцию htmlspecialchars_decode, чтобы то что записано в базе выводилось и воспринималось не как стрелки, а как теги
Спасибо
создайте сниппет, например, dl.prepare
и в доклистере вызывается &prepare=`dl.prepare`
работает