Передача переменных в Fenom

В этой статье мы рассуждали о способах передачи переменных в шаблонизатор. Возможные варианты — через сессию, плейсхолдеры, контейнер. Но всё это вызывает ощущение костыльности. Т.е. чтобы передать какую-то вещь кому-то, вы не курьера посылаете по конкретному адресу, а ловите попутку и просите водителя передать. Тем более, что у других шаблонизаторов возможность передачи переменных есть из коробки — у 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
     */publicfunction getFenom(){if(!$this->fenom){try{if(!class_exists('StorageFenom')){require'_fenom_storage.php';}
                $this->fenom =newStorageFenom($this);}catch(Exception $e){
                $this->modx->log(xPDO::LOG_LEVEL_ERROR, $e->getMessage());returnfalse;}}return $this->fenom;}/**
     * @param string|array $chunk
     * @param array $properties
     *
     * @return mixed|string
     */publicfunction 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 instanceofFenom\Render){// Add system variablesif(!$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 objectsif(!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';classStorageFenomextendsFenomX{useFenom\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]);

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