Прокачиваем навык программирования на Fenom


Информация для разработчиков. Сложность средняя.
Практически все разработчики MODX используют тот или иной шаблонизатор. Большинство в рунете сделали свой выбор в пользу Fenom. Благодаря Василию конечно. Этот шаблонизатор достаточно простой и лёгкий. И его функционала хватает для решения практически всех своих задач. В этой статье мы посмотрим на него немного под другим углом и попробуем его возможности расширения.
Не так давно в pdoTools было добавлено событие «pdoToolsOnFenomInit». Думаю, не все осознали, что это событие даёт. Про собственные модификаторы уже писали. А если заглянуть в документацию Fenom, то можно увидеть, что возможности расширения очень большие. Давайте глянем на примеры.

Добавление пользовательской глобальной переменной

Это может пригодится, например, для много контекстных сайтов. При инициализации Fenom сразу определить нужные значения и использовать их потом в разметке.
// Плагин
switch ($modx->event->name) {
    case 'pdoToolsOnFenomInit':
            $fenom->addAccessorSmart("site", "data", Fenom::ACCESSOR_PROPERTY);
            // Переменные можно определить статически или динамически по условию
            $fenom->data = [
                "domain" => 'example.ru',
                "support" => 'support@example.ru'
            ];
    break;
}
А затем в шаблоне добавить
<div class="copyright">© <a href="//{$.site.domain}">{$.site.domain}</a></div>
<div class="support">Support <a href="mailto:{$.site.support}">{$.site.support}</a></div>

Пользовательские теги

Добавлять свои собственные теги — офигенная вещь. Причем теги могут быть линейными (как например, include) и блоковыми ({if}...{/if}). Покажу на примере. Определим блок только для администраторов и добавим для этого блоковый тег can.
<div>
    {can}
        {include 'chunkForAdministrators'}
    {/can}
</div>
Осталось подключить тег в плагине
// Плагин
switch ($modx->event->name) {
    case 'pdoToolsOnFenomInit':
        $fenom->addBlockFunction('can', function (array $params, $content) {
            if (user(user_id())->isMember('Administrator')) {
                return $content;
            } else {
                return 'Данная информация только для администраторов!';
            }
        });
        break;
}
Может пример несколько надуманный, но он показывает возможности пользовательских тегов. Как вариант, можно добавить теги mobile, desktop. Ну в общем, надеюсь, понятно.
В теги можно передавать параметры
// Плагин
switch ($modx->event->name) {
    case 'pdoToolsOnFenomInit':
        $fenom->addBlockFunction('can', function (array $params, $content) {
	    list($group, $param2) = $params;
	    if (user(user_id())->isMember($group) {return $content;}
        });
        break;
}
А передают их так
<div>
    {can 'Administrator' 'param2'}
        {include 'chunkForAdministrators'}
    {/can}
</div>


Ещё пример. Можно легко добавить тег, который будет включать файловый чанк. Назовём его includeFile.
// Плагин
$fenom->addFunction("includeFile", function (array $params) {
    $file = MODX_CORE_PATH . 'elements/chunks/' . $params[0]; 
    $output = '';
    if (file_exists($file)) $output = chunk($file, $params[1]); 
    return $output;
});
А в шаблоне пишем так:
{includeFile 'chunk.tpl' ['var' => 'value', 'var2'=>'value2']}  // указываем имя файла и массив плейсхолдеров
Fenom подключит файл core/elements/chunks/chunk.tpl и распарсит его.

Разрешённые функции

В Fenom количество разрешённых php функций ограничено. Но можно самостоятельно добавить нужную функцию к списку разрешённых.
switch ($modx->event->name) {
    case 'pdoToolsOnFenomInit':
        $fenom->addAllowedFunctions(['app']);
        break;
}

Заключение

Ну и напоследок расскажу ещё об одном приёме. Бывает иногда нужно прокинуть переменную в шаблонизатор. Т.е. в сниппете чего-то рассчитали или в классе и нужно передать в шаблон. Прямого способа я не знаю. Если есть, поправьте меня. Один из вариантов — сохранять в сессии, а в шаблоне доставать через $.session(). Способ спорный. Раздувать временными данными сессию, увеличивая тем самым и без того растущую как на дрожжах таблицу сессий. Желательно удалять их после использования. В общем, как-то не очень.
Ещё есть способ, как подсказывает Василий в комментариях, через плейсхолдеры. Главное, чтобы название не пересекалось с другими плейсхолдерами, ТВшками и т.п.
После выхода последней версии библиотеки modHelpers сделать этого достаточно легко и не надо заботиться о чистке сессии. Всё что нужно, добавить нужную переменную в контейнер, а затем феномом её вытащить.
// Где-то в коде
$array = array('ids' => [1,2,3,4,5]);
$object = $modx->getObject('modResource', 100);
app()->instance('var.array', $array); // Добавляем префикс "var." для семантики, чтобы было понятно,
app()->instance('var.object', $object); // что это переменные.

// В шаблоне/чанке/ресурсе
{$array = app('var.array')}
{$object = app('var.object')}

Всё возможности в одной статье не опишешь. Но надеюсь, описанные приёмы пригодятся как опытным, так и начинающим разработчикам.
25 августа 2017, 10:32    Сергей Шлоков   G+  
29    770 +24

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

  1. Василий Наумкин 25 августа 2017, 10:43 # +2
    Один из вариантов — сохранять в сессии, а в шаблоне доставать через $.session().
    В MODX для этого обычно используют массив modX::placeholders, доступ к которому обеспечивается setPlaceholder() и getPlaceholder().
    1. Сергей Шлоков 25 августа 2017, 10:48 # +1
      Согласен. Главное исключить вероятность пересечения названия, чтобы ничего не поломалось.
      1. Сергей Шлоков 19 сентября 2017, 10:13 # +2
        Вышеописанные варианты (и сессии, и плейсхолдеры) все-таки являются некими костылями — и сессии и плейсхолдеры предназначены для другого. На самом деле в Fenom есть возможность передавать переменные в шаблоны. Кому интересно как, читайте.
    2. Николай Савин 25 августа 2017, 12:19 # 0
      Спасибо полезная статья. Я и не знал о таких возможностях, за исключением собственных модификаторов.
      А насчет сессий и плейсхолдеров. Поправьте меня если я ошибаюсь, но у сессий больше время хранения. Один раз добавив что то в сессию, я могу вызвать эти данные когда угодно и где угодно (в пределах жизни сессии конечно). А можно ли использовать значение плейсхолдера после его получения на других страницах? По моему только на той странице, где мы этот плейсхолдер получили.
      Я не говорю, что стоит использовать сесссии, лишь отметил разницу и возможное преимущество (сомнительное).
      1. Воеводский Михаил 25 августа 2017, 13:01 # +1
        С плейсхолдерами правильнее вести речь не о страницах, а о «сеансе» связи. Пока запущен и отрабатывается текущий запрос пользователя, плейсхолдер живет. Как только MODX завершил работу и отдал результат, массив с плейсхолдерами уничтожается наравно со всеми остальными свойствами MODX.
        1. Сергей Шлоков 25 августа 2017, 17:18 # +1
          Сессия может пригодится, когда нужно передать данные между запросами. Но, как я уже говорил, нужно помнить, что MODX записывает данные сессии в таблицу сессий, что увеличивает её размер. При высокой посещаемости это может вырасти в проблему.

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