Разработка сайтов на MODX Revolution + phpTemplates + modxSmarty

Так как все больше программистов пытаются взять на вооружение объявленную технологию, и методика боле менее обкаталась и приняла четкие очертания, пишу пошаговую инструкцию как и с чего начать.

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

  1. Устанавливаем чистую MODX Revolution.
  2. Добавляем новый источник пакетов. Как минимум пакет modxSite не лежит в официальном репозитории modx.com, по этому его предстоит скачать с https://rest.modxstore.ru/extras/.

  1. Устанавливаем пакеты:
  • Ace
  • modxSDK
  • modxSite (из https://rest.modxstore.ru/extras/)
  • modxSmarty
  • shopModx (из него нам понадобятся только процессоры для выборки документов)
  • phpTemplates
  • Console
  • getPage
  1. Создаем базовый контроллер. Для этого создаем статический MODX-шаблон. Файл шаблона должен иметь расширение .php (к примеру, base.php) и запишем в шаблон следующий код:
<?php
// Получаем свойства текущего MODX-шаблона
$properties = $modx->resource->getOne('Template')->getProperties();
// Если в настройках шаблона не указано название файла-шаблона,
// то используем по умолчанию index.tpl
if(!empty($properties['tpl'])){
$tpl = $properties['tpl'];
}
else{
$tpl = 'index.tpl';
}
// Если документ не кешируемый, то отключаем кеширование Smarty
// (кеширование Smarty включается/выключается в настройках modxSmarty. По умолчанию отключено).
if ($modx->resource->cacheable != '1') {
$modx->smarty->caching = false;
}
// Отрабатываем Smarty-шаблон и возвращаем результат
return $modx->smarty->fetch("tpl/{$tpl}");

Таким образом мы полученный универсальный контроллер, и в дальнейшем на большинство шаблонов его будет вполне достаточно. И если вам надо создать новый MODX-шаблон просто с целью привязать новые TV-параметры, вы также указываете этот статический файл base.php в качестве источника кода для шаблона, и ничего дополнительно делать не придется. А если надо будет и отображение изменить (конечный шаблон), то просто в свойствах MODX-шаблона указываете новую настройку tpl с названием файла-шаблона. (Если здесь не понятно, лучше посмотреть видео).

По поводу кеширования MODX-шаблонов: если у шаблона нет настройки phptemplates.non-cached=true, то при повторном заходе на страницу шаблон не будет опять обрабатываться, а просто будет отдаваться код из кеша страницы. Если надо отключить кеширование шаблона, то просто в шаблоне создаем такую настройку, и устанавливаем в true. А иначе придется после каждого изменения в коде шаблона сбрасывать кеш сайта (то есть это как правило надо на время программинга или отладки).

Важно создать плагин на событие OnHandlerRequest, но с рангом очередности выше плагина modxSmarty. Это чтобы в Smarty-шаблонах переменная $template_url формировала путь к папке публичной части шаблона.

<?php
switch($modx->event->name){
    case 'OnHandleRequest':
        if($modx->context->key == 'mgr'){
            return;    
        }
        //URL к файлам шаблона (css, js, images etc.)
        $modx->smarty->assign('template_url',
            $modx->getOption('modxSite.template_url').$modx->getOption('modxSmarty.template').'/');
        break; 
}

Код вызова процессора через консоль (может использоваться в сниппетах и плагинах):

<?php
print '<pre>';
$modx->setLogLevel(3);
$namespace = 'shopmodx';
if(!$response = $modx->runProcessor('web/getdata', 
array(
    // Параметры
), array(
    'processors_path'   => $modx->getObject('modNamespace', $namespace)->getCorePath().'processors/',        
))){
    print "Не удалось выполнить процессор";    
    return;
}
print_r($response->getResponse());

Код общего процессора для выборки документов с условиями, расширяющего базовый процессор из shopModx-а.

<?php

require_once MODX_CORE_PATH .'components/shopmodx/processors/web/getdata.class.php';

class modWebGetdataProcessor extends ShopmodxWebGetDataProcessor{
    
    public function initialize(){
        
        $this->setDefaultProperties(array(
            'sort'              => "{$this->classKey}.menuindex",
            'dir'               => 'ASC',
            'showhidden'        => false,
            'showunpublished'   => false,
            'getPage'           => false,
            'limit'             => 10,
            'page'              => !empty($_REQUEST['page']) ? (int)$_REQUEST['page'] : 0
        ));
        
        
        if($page = $this->getProperty('page') AND $page > 1 AND $limit = $this->getProperty('limit', 0)){
            $this->setProperty('start', ($page-1) * $limit);
        }
        
        return parent::initialize();
    }
    
    public function prepareQueryBeforeCount(xPDOQuery $c) {
        $c = parent::prepareQueryBeforeCount($c);
        
        $where = array(
            'deleted'   => false,
        );
        
        if(!$this->getProperty('showhidden', false)){
            $where['hidemenu'] = 0;
        }
        
        if(!$this->getProperty('showunpublished', false)){
            $where['published'] = 1;
        }
        
        $c->where($where);
        
        return $c;
    }
    
    
    public function outputArray(array $array, $count = false) {
        if($this->getProperty('getPage') AND $limit = $this->getProperty('limit')){
            $this->modx->setPlaceholder('total', $count);
            $this->modx->runSnippet('getPage@getPage', array(
                'limit' => $limit,
            ));
        }
        return parent::outputArray($array, $count);
    }
}

return  'modWebGetdataProcessor';

P.S. Здесь очень сложно расписать все, так что кому интересно, все-таки сделайте усилие и посмотрите видеоролик. Там основное рассказано и показано в первые 30 минут. Вторая часть ролика может и очень нудная, но там есть неплохие примеры по использованию всего этого дела.

И не стесняйтесь, задавайте вопросы.