В этой статье мы рассуждали о способах передачи переменных в шаблонизатор. Возможные варианты — через сессию, плейсхолдеры, контейнер. Но всё это вызывает ощущение костыльности. Т.е. чтобы передать какую-то вещь кому-то, вы не курьера посылаете по конкретному адресу, а ловите попутку и просите водителя передать. Тем более, что у других шаблонизаторов возможность передачи переменных есть из коробки — у Smarty через метод assign(), у Blade через share(). Покопавшись в документации я обнаружил, что и у Fenom есть такая возможность. Для этого нужно установить специальный add-on. В pdoTools стандартным способом это сделать нельзя. Поэтому я покажу, как это можно сделать самостоятельно.

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

Расширение класса pdoTools

В папке core/components/pdotools/model/pdotools/ создаём файл pdotoolsext.class.php с таким содержимым:

<?php
require_once 'pdotools.class.php';

class pdoToolsExt extends pdoTools
{
    /**
     * Loads template engine
     *
     * @return bool|Fenom
     */
    public function getFenom()
    {
        if (!$this->fenom) {
            try {
                if (!class_exists('StorageFenom')) {
                    require '_fenom_storage.php';
                }
                $this->fenom = new StorageFenom($this);
            } catch (Exception $e) {
                $this->modx->log(xPDO::LOG_LEVEL_ERROR, $e->getMessage());

                return false;
            }
        }

        return $this->fenom;
    }
    /**
     * @param string|array $chunk
     * @param array $properties
     *
     * @return mixed|string
     */
    public function fenom($chunk, array $properties = array())
    {
        $content = is_array($chunk)
            ? trim($chunk['content'])
            : trim($chunk);
        if (empty($this->config['useFenom']) || !preg_match($this->config['fenomSyntax'], $content)) {
            return $content;
        }

        if ($fenom = $this->getFenom()) {
            $name = '';
            if (is_array($chunk)) {
                if (!empty($chunk['binding'])) {
                    $name = $chunk['binding'] . '/';
                }
                if (!empty($chunk['id'])) {
                    $name .= $chunk['id'];
                } elseif (!empty($chunk['name'])) {
                    $name .= $chunk['name'];
                } else {
                    $name .= md5($content);
                }
            } else {
                $name = md5($content);
            }
            /** @var Fenom\Template $tpl */
            if (!$tpl = $this->getStore($name, 'fenom')) {
                if (!empty($this->config['useFenomCache'])) {
                    $cache_options = array(
                        'cache_key' => 'pdotools/' . $name,
                    );
                    if (!$cache = $this->getCache($cache_options)) {
                        if ($tpl = $this->_compileChunk($content, $name)) {
                            $this->setCache($tpl->getTemplateCode(), $cache_options);
                        }
                    } else {
                        $cache = preg_replace('#^<\?php#', '', $cache);
                        $tpl = eval($cache);
                    }
                } else {
                    $tpl = $this->_compileChunk($content, $name);
                }
                if ($tpl) {
                    $this->setStore($name, $tpl, 'fenom');
                }
            }

            if ($tpl instanceof Fenom\Render) {
                // Add system variables
                if (!$microMODX = $this->getStore('microMODX')) {
                    if (!class_exists('microMODX')) {
                        require '_micromodx.php';
                    }
                    $microMODX = new microMODX($this);
                    $this->setStore('microMODX', $microMODX);
                }
                $properties['_modx'] = $microMODX;
                $properties['_pls'] = $properties;

                // Add system objects
                if (!empty($this->config['useFenomMODX'])) {
                    $properties['modx'] = $this->modx;
                    $properties['pdoTools'] = $this;
                }
                try {
                    $content = $fenom->fetch($tpl, $properties);
                } catch (Exception $e) {
                    $this->modx->log(modX::LOG_LEVEL_ERROR, $e->getMessage());
                    $this->modx->log(modX::LOG_LEVEL_INFO, $tpl->getTemplateCode());
                }
            }
        }

        return $content;
    }
}

В нём мы изменили 2 метода.

Расширяем класс FenomX

В этой же папке создаём файл _fenom_storage.php:

<?php
require_once '_fenom.php';
require_once dirname(dirname(__FILE__)) . '/fenom/StorageTrait.php';

class StorageFenom extends FenomX
{
    use Fenom\StorageTrait;
}

Как мы видим, этот новый класс StorageFenom нужен только для подключения трейта Fenom\StorageTrait, в котором заложен функционал работы с переменными. Его тоже нужно добавить.

Добавляем трейт

Я взял его из официального пакета . Но пришлось сделать маленькую правку для адаптации к MODX. Доработанную версию можно взять тут. Копируем и вставляем в файл core/components/pdotools/model/fenom/StorageTrait.php, который вам нужно предварительно создать.

Подключаем класс pdoToolsExt

Ну и последнее, что нужно сделать — указать в системной настройке pdoTools.class наш новый класс «pdotools.pdotoolsext».

Использование

Теперь можно легко в чанки передавать переменные без каких-либо костылей. Для примера. У вас есть сниппет, который вычисляет какие-то значения, а затем передаёт их в чанк. Раньше можно было использовать в чанке только плейсхолдеры MODX. Теперь вы можете использовать переменные фенома.

# Пример сниппета

// Инициализируем pdoTools
$pdoTools = $modx->getService('pdoTools', 'pdoToolsExt');
// или получаем уже инициализированный в парсере 
// $pdoTools = $modx->getParser()->pdoTools;

// Определяем переменные
$str = 'string';
$names = ['Jonh', 'Mary','Mickael', 'Lisa'];

// Передаем их в Fenom
$fenom = $pdoTools->getFenom();
$fenom->assign('str', $str);
$fenom->assign('names', $names);
// Или через массив
$fenom->assignAll(['str' => $str, 'names' => $names]);

return $pdoTools->getChunk('chunk1');

В чанке будут доступны переменные $str и $names.

// Чанк chunk1
<p>Переменная Fenom: {$str}</p>
<p>Цикл:</p>
<ul>
    {foreach $names as $name}
    <li>{$name}</li>
    {/foreach}
</ul>

На странице мы получим такой результат:

Точно также переменные можно передавать и в шаблоны. Правда только из плагинов. В трейте вы найдёте ещё методы для работы с переменными. Попробуйте разобраться.

П.С. Думаю, было бы неплохо иметь такой функционал в pdoTools из коробки. А вы как думаете?

П.П.С. Василий пояснил, что Fenom видит переменные, переданные в чанк. Поэтому достаточно сделать так:

...
// Определяем переменные
$str = 'string';
$names = ['Jonh', 'Mary','Mickael', 'Lisa'];

return $pdoTools->getChunk('chunk1', ['str' => $str, 'names' => $names]);

Значит описанный мной функционал пригодится для передачи переменных из плагинов или для сохранения глобальных переменных, которые будут доступны в любом месте.

19 сентября 2017, 09:46   1298     2

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

  1. Андрей 03 ноября 2017, 15:55 # 0
    Сергей, спасибо за материал. )
    Не подскажете, как организовать следующую логику с помощью переменных на феном?
    Есть несколько чанков в файлах (1.tpl, 2.tpl, 3.tpl) Есть необходимость один из них подключать их в шаблоне, в зависимости от настройки ClientConfig в поле 'chunk' (base==1||business==2||church==3)
    {include 'file:result.tpl'}
    1. Сергей Шлоков 03 ноября 2017, 17:06 # 0
      Никогда не работал с ClientConfig. Можно попробовать так:
      {set $chunk = 'file:chunks/' ~ $_modx->config.chunk ~ '.tpl'} // 1.tpl или 2.tpl или 3.tpl
      {include $chunk}
      
      Путь к чанкам core/elements/chunks. Если не совпадает, то нужно указать свой.

    Вы должны авторизоваться, чтобы оставлять комментарии.

    Выделите опечатку и нажмите Ctrl + Enter, чтобы отправить сообщение об ошибке.