MODX - свой процессор

Надо признаться - поначалу вся структура файлов и вызовов в системе повергла меня в ужас. Казалось уж чрезмерно усложненной. Действительно многие вещи можно сделать компактнее и быстрее. Но со временем все стало понятно и прояснилась логика разработчиков. Да и сопровождать свои скрипты наверное намного удобнее именно при такой организации. Если, например, надо изменить кусок кода для считывания из базы, то уже будешь знать, что надо найти соответсвующий процессор(небольшой по размеру). Если изменить отображение - найти соответствующий чанк. Ну и так далее. Т.е. легче работать с определенным количеством хорошо структурированных компонентов, небольших по размеру, чем ползать по огромному файлу, держа в голове все связи. Ну или тратя время на тщательное документирование. Так первые коннекторы я довольно бодро писал на "голом" PHP и они, в принципе, отлично работали. И все было в одном файле. Это хорошо когда обрабатываешь пару запросов. Потом появляются ветвления и все уже не так бодро. Поэтому и пришла мысль использовать общий коннектор и 'родной' modx->runProccessor. В итоге по идее на каждый запрос нужно будет написать только небольшой свой процессор. Естественно это касается только нестандартных процессоров. Т.е. не GRUD-процессоров (GRUD - Greate, Read, Update, Delete).

Для функционирования backend всегда создается свой коннектор. Он создает объект нашего класса и подключает стандартный коннектор. Для полного контроля над процессом я решил для контекста WWW(frontend) использовать упрощенный коннектор, который создаст наш объект и сразу будет вызывать modx->runProcessor и возвращать json в виджет.

Тут есть один ньюанс. Если напрямую попробовать запустить свой процессор через стандартный коннектор из контекста WWW - работать не будет. И это правильно. Иначе кто угодно может заслать из JS какой угодно запрос на любые манипуляции с базой. Дело в $_POST['HTTP_MODAUTH'] в котором передается $site_id. Стандартный коннектор без соответствующего значения не будет вызывать процессоры. Можно, естествеено, подставить в запрос эту переменную, но это очень легкомысленно. Поэтому и нужно написать свой коннектор, который будет вызывать "безобидные" процессоры, которые написаны специально для WWW. Т.е. заблокирует доступ к стандартным процессорам.

Работает так:

  • Как параметры получает в частности $action и $options['processors_path']. Ну и прочие, но они пока нам не важны.
  • Потом куча разных вариантов при отсутсвии одного их них. Будем считать, что с этим все ОК.
  • Склеиваются эти два параметра, прибавляется расширение '.class.php'. Получаем имя файла класса процессора. Т.е. если $options['processors_path']= '/home/www/q12/pocessors/www/', $action="comon/getperiod" - $processorFile='/home/www/q12/pocessors/www/comon/getperiod.class.php'
  • Подключается этот вычисленный файл($processorFile)
  • Если скрипт файла возвращает '1', то имя класса вычисляется из $action путем удаления слэшей, точек и прочих символов.
  • Если возвращает имя класса, то и ладно).
  • Вызывается статический метод класса процессора getInstance(&$this,$className,$scriptProperties) который создает экземпляр процессора($processor)
  • $processor->setPath($processorFile);
  • $response = $processor->run();
  • возвращается $response

Теперь понятна общая структура пользовательскиx классов:

  • Класс должен содержать методы(как минимум):
    • getInstance
    • setPath
    • run
  • вернуть литеральный json
  • Скрипт файла класса должен возвращать имя класса нашего процессора, чтоб потом мучительно не искать причины "тишины"...

Это справедливо, если полностью создавать свой клас с нуля. Посмотрим на иерархию классов процессоров(кратко, укажу не все свойства и методы):

  • abstract class modProcessor
    • public $modx
    • public $path
    • public $properties
    • function __construct(modX & $modx,array $properties = array()) - сохраняет ссылку на modx и копирует $scriptProperties
      • $this->modx =& $modx;
      • $this->setProperties($properties);
    • public function setPath($path) - просто копирует путь.
    • boolean public function initialize() - должен вернуть true, что и делает по умолчанию.
    • public static function getInstance(modX &$modx,$className,$properties = array()) - создает экземпляр класса нашего процессора
      • $processor = new $className($modx,$properties);
      • return $processor;
    • abstract public function process();
    • responce public function run()
      • boolean $this->initialize(); - можно не трогать ничего, по умолчанию возвращает true
      • $o = $this->process(); далее - вариант реализации(в modProcessor она пустая):
        • $this->beforeOutput(); - обратка(callback). Не обязательно, можно своих обраток напихать. Естественно, создав свой базовый класс.
        • return this->outputArray(array $array,$count = false)
          • if ($count === false) { $count = count($array); }
          • return '{"success":true,"total":"'.$count.'","results":'.$this->modx->toJSON($array).'}';
      • $response = new modProcessorResponse($this->modx,$o); $o - это литеральный json. Здесь просто добавляется &$modx и проверки
      • return $response;
  • abstract class modObjectProcessor extends modProcessor - базовый класс для работы с объектами xpdo
  • abstract class modObjectGetProcessor extends modObjectProcessor - базовый класс для "классов-читателей(выборки записей из таблиц)"
  • ... и так далее

Теперь ясно, что если унаследовать от modProcessor(а лучше от modObjectProcessor), то можно определить только один свой метод: "process". Oн должен создать ассоциативный массив данных. Примерно так:

         Array(
                  [id]=>345,
                  [name]=>'Ленинградская',
                  [id_city]=>1
         )
     
Далее пропустить этот массив через this->outputArray(array $array,$count = false). И вернуть литеральный json. Предполагается что все делаем через json.

Создадим на клиенте функцию запроса некоей инфы(на ExtJS, т.к. этот фрейворк ипользуется в MODX и "заточен" на работу с коннекторами).

CFG_COUNTERS.win_get_info_com=function(win){
  var s=new Ext.data.JsonStore({
   url: '/.../components/my_component/connector.php'
   ,root: 'results'
   ,totalProperty: 'total'
   ,baseParams:{action: 'www/com/getStartInfo',WEB_PROC:'1'}  //WEB_PROC по этому флагу обычный коннектор подключает www-коннектор
   ,fields: ['id','name']
   ,listeners:{
          load:{ fn:function(s,r,o){
                 alert(r[0].data.name);
                 } 
                 ,scope:this
          }
          ,exception: {fn:function(m){
                         alert('ошибка');            
                       } 
                      ,scope:this
                      }
       }
  });        
  s.load(); 
}
         
     

вызов функции при загрузке формы
         .............................................
         ,listeners:{
                show:{fn:CFG_COUNTERS.win_get_info_com, scope:this}
      
             }  
         .............................................
         
     

Процессор тестовый.

         <?php
           class countersWWWgetStartInfoPr extends modProcessor {
           public function process() {
                     $list = array();
                     $list[0]['id']=3;
                     $list[0]['name']="Qu-qu-qu!!!";
                     return $this->outputArray($list);
                  }
            }
            return 'countersWWWgetStartInfoPr';
     

Как видите, все реально работает).

Получается, что коннектор выступает в роли диспетчера, подключающего различные процессоры, передаваемые в $action. Согласен, что все это выглядит несколько замороченно. Можно написать обычный хендлер за пол-часа. А на освоение процессоров у меня ушла неделя(помимо основной работы и грядок). Но, поверьте, во первых: зная внутреннее устройство как-то увереннее себя чувствуешь, во-вторых: не поворошив все это ручками - не поймешь всех тонкостей. Потом мы автоматом получаем очень гибкое назначение прав на наш процессор(достаточно свойству 'permission' назначить любую строку. Напрмер myProccessor->permission='myProcGetList' и все!!!). И все это воздастся легкостью сопровождения и совершенствования кода в дальнейшем. Ну и продвинет понимание глубин MODX. Хочется верить...

Кроме того, не надо забывать, что уже будет загружен сам modx. И класс нашего компонента. В итоге получаем предельно прозрачный код.


Комментарии 0






Разрешённые теги: <b><i><br>Добавить новый комментарий: