Спрашивали? Отвечаем Ответы на популярные вопросы по MODX Evolution
У всех, кто впервые сталкивается с MODx Evolution, очень часто возникают подобные вопросы: как вывести дату создания документа, как вывести автора документа, как вывести заголовок родителя и т.д. и т.п. Согласитесь, не для всех это может оказаться элементарным на первых шагах изучения MODx. А сколько было потрачено нервов и времени в бесплодных попытках найти нужный ответ! И не всегда даже найденный ответ бывает очевиден и понятен. Хватит! Настало время дать сразу все ответы на все вопросы. Ну в меру собственных сил и знаний, конечно. В этой статье я буду собирать все подобные вопросы и стараться дать максимально подробный ответ, конечно же все это будет сопровождаться готовыми рабочими примерами, которые каждый сможет применить на практике.
Автор не считает себя великим гуру, поэтому, в подготовке этой статьи рассчитывает на помощь читателей. Присылайте свои вопросы и найденные решения, комментируйте и предлагайте альтернативные подходы, критикуйте и становитесь соавторами этой подборки.
Спрашивали? Отвечаем!
Сниппеты можно вызывать двумя способами:
[[НазваниеСниппета]] - кэшируемый вызов сниппета
[!НазваниеСниппета!] - некэшируемый вызов сниппета
Если вызов основного сниппета некэшируемый, то в шаблоне сниппет обязательно должен быть кэшируемым. Более того, чтобы избежать проблем с выводом сниппетов (например с постраничным разбиением), основной сниппет желательно всегда делать некэшируемым.
К примеру, имеем некэшируемый вызов Ditto:
[!Ditto? &startID=`10` &tpl=`ditto_tpl`!]
у которого в шаблоне ditto_tpl надо вызвать сниппет Wayfinder:
<div class="wrapper">
[[Wayfinder? &startId=`[+id+]`]]
</div>
Расширим задачу, сформулировав вопрос так: Как вообще выводить даты? Т.е. это может быть любая дата, как в шаблоне для Ditto так и на странице самого документа.
а) Задача: вывести дату создания документа в ленте новостей
Для вывода даты нам потребуется вставить в шаблон news_tpl в месте вывода этой даты специальный плэйсхолдер Ditto - [+date+] – он выводит дату в установленном формате (которую мы зададим позже, при вызове Ditto). По умолчанию используется значение createdon (дата создания документа). Может принимать значения: editedon (дата последнего редактирования) и pub_date (дата публикации документа).
Создаем чанк news_tpl:
<div class="news">
[+date+] | [+longtitle+]
<p>[+introtext+]</p>
</div>
Для того, чтобы задать параметр из которого Ditto будет брать значение даты используем &dateSource=`editedon`. Для того чтобы задать формат даты, используем &dateFormat=`%d.%m.%Y`, где значением выступает любой валидный формат времени, который соответствует правилам функции PHP - strftime:
%a | сокращенное название дня недели в текущей локали | Пт |
%A | полное название дня недели в текущей локали | 22.11.2024 |
%b | сокращенное название месяца недели в текущей локали | 22.11.2024 |
%B | полное название месяца недели в текущей локали | Ноябрь |
%c | предпочтительный формат даты и времени в текущей локали | 22.11.2024 |
%C | столетие (год, деленный на 100 и огругленный до целого, от 00 до 99) | 22.11.2024 |
%d | день месяца в виде десятичного числа (от 01 до 31) | 22.11.2024 |
%D | аналогично %m/%d/%y | 22.11.2024 |
%e | день месяца в виде десятичного числа, если это одна цифра, то перед ней добавляется пробел (от ' 1' до '31') | 22.11.2024 |
%g | подобно %G, но без столетия. | 22.11.2024 |
%G | Год, 4-значное число, соответствующее номеру недели по ISO (см. %V). Аналогично %Y, за исключением того, что если номер недели по ISO соответствует предыдущему или следующему году, используется соответствующий год. | 2024 |
%h | аналогично %b | ноя |
%H | номер часа от 00 до 23 | 22.11.2024 |
%I | номер часа от 01 до 12 | 22.11.2024 |
%j | номер дня в году (от 001 до 366) | 327 |
%m | номер месяца (от 01 до 12) | 22.11.2024 |
%M | минуты | 22.11.2024 |
%n | символ "\n" | 22.11.2024 |
%p | `am' или `pm', или соответствующие строки в текущей локали | 22.11.2024 |
%r | время в формате a.m. или p.m. | 22.11.2024 |
%R | время в 24-часовом формате | 22.11.2024 |
%S | секунды | 22.11.2024 |
%t | символ табуляции ("\t") | 22.11.2024 |
%T | текущее время, аналогично %H:%M:%S | 22.11.2024 |
%u | номер дня недели от 1 до 7, где 1 соответствует понедельнику | 22.11.2024 |
%U | порядковый номер недели в текущем году. Первым днем первой недели в году считается первое воскресенье года. | 22.11.2024 |
%V | Порядковый номер недели в году по стандарту ISO 8601:1988 от 01 до 53, где 1 соответствует первой неделе в году, в которой как минимум 4 дня принадлежат этому году. Первым днем недели считается понедельник. (Используйте %G or %g для определения соответствующего года) | 47 |
%W | порядковый номер недели в текущем году. Первым днем первой недели в году считается первый понедельник года. | 22.11.2024 |
%w | номер дня недели, 0 соответствует воскресенью | 22.11.2024 |
%x | предпочтительный формат даты без времени в текущей локали | 22.11.2024 |
%X | предпочтительный формат времени без даты в текущей локали | 22.11.2024 |
%y | год без столетия (от 00 до 99) | 22.11.2024 |
%Y | год, включая столетие | 22.11.2024 |
%Z | временная зона в виде смещения, аббривеатуры или полного наименования | 22.11.2024 |
%% | символ `%' | 22.11.2024 |
Примеры вызова Ditto с разными значениями даты:
[!Ditto? &startID=`10` &tpl=`news_tpl` &dateSource=`createdon` &dateFormat=`%d.%m.%Y` &display=`5`!] // дата создания документа
[!Ditto? &startID=`10` &tpl=`news_tpl` &dateSource=`editedon` &dateFormat=`%d.%m.%Y` &display=`5`!] // дата последного редактирования документа
[!Ditto? &startID=`10` &tpl=`news_tpl` &dateSource=`pub_date` &dateFormat=`%d.%m.%Y` &display=`5`!] // дата публикации документа
б) Задача: вывести дату на странице самой новости
Казалось бы, чего проще? Меняем в чанке news_tpl все + на * и нужный чанк готов. Но в MODx нет специального тега [*date*], поэтому, вместо него придется использовать [*createdon*], [*editedon*] или [*pub_date*].
Для начала пробуем [*createdon*] или [*editedon*] и получаем вместо даты что-то типа этого:
1318407717
Почему же так получилось? Потому что, любое время в БД записывается в виде Unix Timestamp — количество секунд от 1 января 1970 года до текущего момента. Зачем же такое придумали? Ну, например, для того, чтобы была возможность оперировать датой в независимости от ее формата. На самом деле, это будет легко перевести в нужный нам формат, но об этом чуть позже.
Берем следующий параметр [*pub_date*] и получаем:
0
Ну а тут то что не так, спросите вы? Оказывается, по-умолчанию параметр pub_date не устанавливается, поэтому его значение равно 0 и попытка его вывести выдаст 0 или 01.01.1970. Чтобы избежать этого, придется параметр pub_date делать обязательным к заполнению или установить ему значение по умолчанию. В этом нам поможет ManagerManager. Но даже если параметр будет заполнен, на выходе мы вновь увидим количество секунд, прошедших с 01.01.1970.
Решение 1. На помощь придет PHx, только прежде чем его устанавливать, ознакомьтесь с возможными проблемами. При установленном PHx вывод даты можно сделать таким образом:
[*createdon:date=`%d.%m.%Y`*] // дата создания документа
[*editedon:date=`%d.%m.%Y`*] // дата последнего редактирования документа
[*pub_date:date=`%d.%m.%Y`*] // дата публикации документа
Кстати, PHx можно использовать и в шаблоне Ditto, вставив вместо плэйсхолдера [+date+] один из этих плэйсхолдеров:
[+createdon:date=`%d.%m.%Y`+] // дата создания документа
[+editedon:date=`%d.%m.%Y`+] // дата последнего редактирования документа
[+pub_date:date=`%d.%m.%Y`+] // дата публикации документа
В этом слачае, при вызове Ditto параметры &dateSource и &dateFormat не нужны.
Решение 2. Для вывода даты в нужном формате можно воспользоваться сниппетом. Создаем новый сниппет, назовем его, к примеру, DateFormat, и помещаем в него следующий код:
<?php
setlocale(LC_ALL, 'ru_RU.UTF-8');
if ( $val == '' ) $val=time();
if ($format == '' ) $format = "%d.%m.%Y";
return strftime($format, $val);
?>
В том месте, где нам необходимо вывести дату, помещаем такой вызов этого сниппета:
[!DateFormat? &val=`[*createdon*]` &format=`%d.%m.%Y`!]
В параметре &val мы задаем значение для даты, а в параметре &format нужный формат. Сниппет может применяться как на странице новости, так и в шаблоне для Dito, не забудьте, если Ditto вызывается некэшируемым, то в его шаблоне сниппет должен вызываться как кэшируемый.
Если вызвать сниппет вообще без параметров:
[!DateFormat!]
то он выведет текущую дату в формате заданном по умолчанию: "%d.%m.%Y", этот формат можно поменять в коде сниппета.
Вот пример, где это может пригодиться: Необходимо отсортировать горящие туры по дате вылета. Дата вылета задается в TV-параметре data_toura с типом ввода Date. При этом, эта дата должна быть выведена в ленте горящих туров и на странице самого тура в формате %d-%m-%Y.
Для того, чтобы можно было отсортировать туры в ленте горящих туров, которая выводится с помощью снипета Ditto, мы будем использовать параметр &orderBy=`data_toura ASC`. Для того, чтобы сортировка у нас получилась правильная, необходимо задать параметру data_toura значение в формате количества секунд, прошедших с 1 января 1970 года. Для этого нам необходимо установить у этого параметра значение Визуальный компонент как Unixtime:
Для отображения этой даты в шаблоне Ditto и на странице тура будем использовать PHx или сниппет DateFormat из предыдущей статьи:
[!DateFormat? &val=`[*data_toura*]` &format=`%d-%m-%Y`!] // на странице тура
[[DateFormat? &val=`[+data_toura+]` &format=`%d-%m-%Y`]] // в шаблоне Ditto
Т.е. мы хотели бы увидеть дату в формате: 12 декабря 2012 года.
Создаём сниппет convertDate и вставляем в него такой код:
<?php
$MyDate= (isset($MyDate)) ? $MyDate: $modx -> documentObject['MyDate'];
$type= (isset($type)) ? $type: $modx -> documentObject['type'];
$monthes = array('','января','февраля','марта','апреля','мая','июня','июля','августа','сентября','октября','ноября','декабря');
$day = date("j" ,$MyDate);
$month = $monthes[date("n",$MyDate)];
$year = date("Y",$MyDate);
echo $day.' '.$month.' '.$year.' года';
?>
Вызываем сниппет:
[!convertDate? &MyDate=`[*createdon*]`!] // в документе MODx
[[convertDate? &MyDate=`[+createdon+]`]] // в шаблоне Ditto
В качестве значения параметра &MyDate могут выступать createdon, editedon, pub_date, unpub_date и TV-параметр с типом ввода Date и визуальным компонентом Unixtime.
Решение 1. Для этой цели можно воспользоваться возможностями PHx, если он у вас установлен.
Логин того, кто создал документ, будет выводиться таким образом:
[*createdby:userinfo=`username`*] // в докуенте MODx
[+createdby:userinfo=`username`+] // в шаблоне Ditto
К примеру, если документ создал администратор, то выведется его логин admin.
Но для того, чтобы вывести не логин, а полное имя пользователя, которое указано в соответствующем поле в настройках пользователей, делаем так:
[*createdby:userinfo=`fullname`*]
В этом случае выведется Default admin account - это значение полного имени администратора по умолчанию. Чтобы вывелось Администратор или как-то иначе, необходимо заменить его полное имя в Пользователи >> Управление менеджерами.
Для вывода E-mail пользователя:
[*createdby:userinfo=`email`*]
Решение 2. Плагин PHx можно заменить простым сниппетом. Создадим новый сниппет UserInfo с таким кодом:
<?php
/*
* UserInfo - сниппет для получения данных пользователя
*/
$output = "";
$userinfo = $modx->getUserInfo($id); //Используем Id пользователя для получения массива с данными пользователя
$out1 = $userinfo["fullname"]; // Получаем полное имя пользователя
$out2 = $userinfo["phone"]; // Получаем телефон пользователя
$out3 = $userinfo["email"]; // Получаем электронный адрес пользователя
$output = 'Автор: ' . $out1 . ' Телефон: ' . $out2 . ' E-mail: ' . $out3;
return $output;
?>
В шаблоне помещаем такой вызов сниппета:
[!UserInfo? &id=`[*createdby*]`!]
где
&id=`[*createdby*]` - передаем в наш сниппет ID пользователя, создавшего документ. С помощью параметра editedby можно передать ID пользователя, редактировавшего документ.
С помощью этого сниппета можно выводить любые данные пользователя из таблицы user_attributes. Описание getUserInfo.
Решение 1. Вновь расширим задачу: Как вообще выводить параметры родительского документа?
На помощь опять придет PHx.
Вывести ID родительского документа мы можем с помощью специального тега MODx:
[*parent*] // в документе MODx
[+parent+] // а шаблоне Ditto
Чтобы вывести заголовок родительского документа, применяем PHx:
[*id:parent=`pagetitle`*] // в документе MODx
[+id:parent=`pagetitle`+] // а шаблоне Ditto
Чтобы вывести ID родителя родителя, т.е. дедушки:
[*parent:parent=`id`*] // в документе MODx
[+parent:parent=`id`+] // а шаблоне Ditto
Чтобы вывести заголовок прадедушки:
[*parent:parent=`id`:parent=`pagetitle`*] // в документе MODx
[+parent:parent=`id`:parent=`pagetitle`+] // а шаблоне Ditto
и т.д.:
[*parent:parent=`id`:parent=`id`:parent=`pagetitle`*] // в документе MODx
[+parent:parent=`id`:parent=`id`:parent=`pagetitle`+] // а шаблоне Ditto
Таким образом можно получать не только id или заголовок, а и любой другой параметр, например расширенный заголовок родителя:
[*id:parent=`longtitle`*] // в документе MODx
[+id:parent=`longtitle`+] // а шаблоне Ditto
Решение 2. Если по каким-то причинам вы не можете использовать PHx, на помощь придет сниппет getField.
Скачайте и установите сниппет, как это описано в документации. Чтобы получить заголовок родительского документа, сниппет надо вызвать с такими параметрами:
[!GetField? &parent=`0` &field=`pagetitle`!]
При parent=0 будет использоваться родительский документ. В качестве значения параметра field можно указывать любое поле документа или TV-параметр.
Чтобы получить заголовок или другое поле "дедушки", используются следующие параметры:
[!GetField? &parent=`1` &parentLevel=`2` &field=`pagetitle`!]
При parent=1 используется корневой каталог. Параметр parentLevel определяет глубину вложенности от текущего документа до корневого каталога: 0 - корневой документ, 1 - родитель, 2 - дедушка, 3 - прадедушка и т.д.
Этот сниппет позволяет выводить любой параметр любого документа. С помощью параметра docid можно задать ID интересующего документа, а с помощью параметра field необходимое поле или TV-параметр:
[!GetField? &docid=`25` &field=`pagetitle`!]
К примеру, выводим ленту новостей и необходимо, чтобы длина заголовка не превышала 200 символов, при этом, надо чтобы слова не обрезались и в конце строки появлялись три точки.
Для этих целей используем сниппет truncate. Код сниппета:
<?php
$lenf = $len;
//Заменяет символы перевода строки на HTML тег
$order = array("\r\n", "\n", "\r");
$replace = '<br />';
$what = str_replace($order, $replace, $text);
if (strlen($what) > $lenf) {
$what = preg_replace('/^(.{' . $lenf . ',}? ).*$/is', '$1', $what) . '...';
}
return $what;
?>
Вызов сниппета будет таким:
[!truncate? &text=[*pagetitle*] &len=200!] // в документе MODx
[[truncate? &text=[+pagetitle+] &len=200]] // а шаблоне Ditto
Сниппет обрежет текст до определенного количества символов не обрезая слова и добавит в конце три точки.
Придется отредактировать файл assets/snippets/ditto/formats/rss.format.inc.php
На 80 строчке есть такая запись:
$GLOBALS["dateSource"] = isset($dateSource) ? $dateSource : "createdon";
// date type to display (values can be createdon, pub_date, editedon)
// set tpl rss placeholders
$placeholders['rss_date'] = array($GLOBALS["dateSource"],"rss_date");
Т.е. по умолчанию используется createdon. Надо заменить на pub_date, т.е.:
$GLOBALS["dateSource"] = isset($dateSource) ? $dateSource : "pub_date";
Если у вас псевдонимы генерируются автоматически с помощью плагина TransAlias, то такие знаки препинания, как "!", ":", "," и т.д. автоматически появятся и в вашем псевдониме, если они присутствуют в заголовке. Но если запятая или восклицательный знак не влияют на работоспособность псевдонима, тем не менее, их присутствие вряд ли его украсит. К счастью, это легко исправляется.
В настройках TransAlias достаточно выбрать в параметре Restrict alias to значение lowercase alphanumeric и псевдонимы будут генерироваться правильно, т.е. без лишних знаков препинания.
Для этого воспользуемся сниппетом ChildCounter (сниппет найден здесь):
<?php
$docid = isset($docid) ? intval($docid) : $modx->documentIdentifier;
$depth = isset($depth) ? intval($depth) : 0;
$isfolder = isset($isfolder) ? intval($isfolder): 0;
$tpl = isset($tpl) ? $tpl: -1;
$published = isset($published) ? intval($published): 1;
$davailable = $modx->getChildIds($docid, $depth);
if ($davailable){
$where = ($tpl > 0) ? 'template='.$tpl : 'isfolder='.$isfolder;
$dcount = $modx->getDocuments($davailable, $published, 0, 'id', $where);
return count($dcount);
}
return 0;
?>
Параметры сниппета ChildCounter:
&isfolder - Если 1 - вернёт количество папок, если 0 - количество документов НЕ папок. Значения 0 или 1. По умолчанию 0.
&published - Если 0 - вернёт количество неопубликованных документов, если 1 - количество опубликованных документов. Значения 0 или 1. По умолчанию 1.
Пример использования:
[[ChildCounter? &docid=`406` &depth=`2` &tpl=`15` &published=`0` &isfolder=`0`]]
вернет количество неопубликованных документов с шаблоном 15 в контейнере с id равным 406 при глубине сканирования 2.
Очень часто мы сталкиваемся с необходимостью вывести на странице какой-то параметр, например, расширенный заголовок longtitle. Но что делать, если этот параметр не заполнен, а выводим мы его в качестве заголовка? Конечно, можно с помощью плагина ManagerManager задать обязательное заполнение этого параметра и наша головная боль будет решена. Но что, если мы в силу некоторых обстоятельств не можем этого сделать?
Пример 1.
С помощью плагина PHx.
Вместо тега [*longtitle*] вставляем такую конструкцию:
<h1>[+phx:if=`[*longtitle*]`:is=``:then=`[*pagetitle*]`:else=`[*longtitle*]`+]</h1>
Читается это так: Если параметр longtitle пустой, то выводим pagetitle, иначе выводим longtitle.
С помощью сниппета alterTitle
Сниппет alterTitle выводит pagetitle если не заполнен longtitle.
Пример использования:
<h1>[[alterTitle? &id=`[*id*]`]]</h1>
где
&id=`[*id*]` - передаем в сниппет ID ресурса, в котором вызываем сниппет. Фактически, этот сниппет можно использовать и для вывода заголовка родителя, передав [*parent*].
Пример 2.
С помощью плагина PHx.
Аналогичную конструкцию можем применять и для других параметров. Например, у нас есть параметр foto у шаблона Новости, в котором содержится картинка для новостей, но не у всех новостей будут картинки. И если у новости нет картинки, нам необходимо вывести картинку "заглушку". Я поступил следующим образом, создал еще один параметр nofoto тип ввода Image и назначил его шаблону Новости. В значении по умолчанию указал путь к картинке "заглушке". С помощью плагина ManagerManager спрятал этот параметр от всех пользователей:
mm_hideFields('nofoto');
А в том месте, где нужно вывести параметр foto поместил такую конструкцию:
[+phx:if=`[*foto*]`:is=``:then=`<img alt="Нет фотографии" src="[*nofoto*]">`:else=`<img alt="[*pagetitle*]" src="[*foto*]">`+]
С помощью сниппета if
Документация к сниппету.
С помощью виджета Custom Widget.
Используется только для TV-параметров. Детали тут.
Очень часто в проектах бывает необходимо организовать переход на предыдущий и следующий документ в папке. Это можно организовать с помощью сниппета prevnextPage, который я нашел здесь. Приводить описание параметров не имеет смысла, так как они хорошо описаны в самом коде сниппета.
Пример вызова сниппета:
[[prevnextPage? &directOutput=`1` &letsCycle=`1`]]
где
После встречи очередного нового года и длинных и запойных выходных, вы возвращаетесь к работе и обнаруживаете, что у всех ваших сайтов надо обновлять год в копирайте. А почему бы не автоматизировать сей процесс? Подобная идея уже приходила в голову не только мне, поэтому я довольно быстро нашел готовое решение в пару строчек и немного его модифицировал.
Создайте новый сниппет (название не так важно, ну пусть будет copyright) с таким кодом:
<?php
$copyfrom = (isset($copyfrom )) ? $copyfrom : '2012';
$copyholder = (isset($copyholder)) ? $copyholder : ' [(site_name)]';
if (date('Y') != $copyfrom) { $copytill = ' - ' . date('Y'); }
$copyright = '© ' . $copyfrom . $copytill . $copyholder;
return $copyright;
?>
Пример вызова:
[[copyright]]
или
[[copyright? ©from=`2008` ©holder=`Иван Иванович Иванов.`]]
где