Процессоры в MODX


В MODX есть файлы-процессоры, это php скрипты, которые выполняют определенные функции. Загляните в /core/model/modx/processors, и вы увидите, как их много.

Работать с процессорами можно из любого сниппета или плагина при помощи метода runProcessor:
$response = $modx->runProcessor('action/path/to/processor',$arrayOfProperties,$otherOptions);
В ответ мы получаем объект modProcessorResponse, со всеми его методами.

Стандартные процессоры

К примеру, в директории security есть процессоры login и logout, которые управляют авторизацией юзеров. Вот как мы можем авторизовать юзера:
$username = 'bezumkin';
$password = '*********';
$data = array(
    'username' => $username,
    'password' => $password,
    'rememberme' => 1,
    'login_context' => 'web',
);    
$response = $modx->runProcessor('/security/login', $data);
if ($response->isError()) {
    $modx->log(modX::LOG_LEVEL_ERROR, 'login error. Username: '.$username.', Message: '.$response->getMessage());
}
Выход с сайта и того проще:
$response = $modx->runProcessor('/security/logout');
if ($response->isError()) {
    $modx->log(modX::LOG_LEVEL_ERROR, 'Logout error. Username: '.$modx->user->get('username').', uid: '.$modx->user->get('id').'. Message: '.$response->getMessage());
}
Именно так и сделана авторизация в моем компоненте Loginza.
Это очень удобно и гарантирует, что компонент будет работать во всех версиях MODX. Поэтому, если возможно, всегда нужно использовать стандартные процессоры.

Конечно, стандартные процессоры не умеют работать с вашими расширениями.

Собственные процессоры

Использование своих процессоров отличается от стандартных только тем, что нужно указать директорию, откуда их брать. Смотрим пример из miniShop:
// Массив, который мы передадим в процессор, там его ловить в $scriptProperties
$processorProps = array(
    'id' => 55
);
// Массив опций для метода runProcessor
$otherProps = array(
    // Здесь указываем где лежат наши процессоры
    'processors_path' => $modx->getOption('core_path') . 'components/minishop/processors/'
);
// Запускаем
$response = $modx->runProcessor('web/orders/getlist', $processorProps, $otherProps);
// И возвращаем ответ от процессора
return $response->response;
Это чуть измененный пример из сниппета miniShop, где он обрабатывает запросы личного кабинета.

Как вы видите, процессор для запуска указывается без расширения, путем от директории процессоров. Если мы не указываем свою директорию, то это будет /core/model/modx/processors/, по умолчанию. В моем примере — мы ее меняем на директории внутри компонента miniShop.

Стандартный процессор внутри собственного

Вот и сладкое! Смотрим пример:
$response = $modx->runProcessor('resource/create', $_POST);
if ($response->isError()) {
    return $modx->error->failure($response->getMessage());
}

$id = $response->response['object']['id'];
В этом примере мы создаем стандартным процессором новый ресурс из присланных данных, и получаем из ответа id этого ресурса, для дальнейшей работы. Потом уже мой процессор добавляет свойства товара к этому ресурсу.
Вот исходный код всего процессора miniShop для создания нового товара.

При таком подходе, опять же, мы гарантируем, что независимо от будущих изменений в MODX наш процессор будет работать во всех версиях. И, что очень важно, будет работать плагины, которые должны работать при создании новых ресурсов. Также будет сгенерирован alias (если вы их используете), причем как это указано в настройках, через транслитерацию, или нет.
Таким же образом работает и обновление товаров, а другой мой компонент mSearch, ловит событие OnDocFormSave и индексирует этот ресурс.

Конечно, можно не использовать в таких случаях runProcessor, а работать через newObject — но тогда нужно самостоятельно генерировать события для плагинов, определять незаданные поля нового ресурса, генерировать alias и еще много чего.
Зачем, если авторы MODX все для нас уже предусмотрели?

Заключение

Процессоры — отличная вещь и нужно использовать их по-максимуму, везде, во всех своих сниппетах. Если вы хотите сделать что-то с ресурсом или другим элементом — посмотрите, нет ли для этого готового процессора в системе?
Если есть — используйте его. Это оградит вас от лишней головной боли, и позволит другим расширениям и плагинам взаимодействовать с вашим кодом. Кстати, так и писать выходит меньше — запустил runProcessor, получил ответ и делай, что хочешь.
20 june 2012, 07:06    Василий Наумкин   G+  
14    4785 0

Comments (23)

  1. Ivan Klimchuk 20 june 2012, 09:37 # 0
    Перевод нужен. Сам все никак не сяду перевести. Новые процессоры на классах основаны и работают на самом деле очень круто, так как унаследовал класс и только самое необходимое переопределяешь.
    1. Василий Наумкин 20 june 2012, 09:59 # 0
      Да, мне тоже очень понравилось =)
      Сегодня вечером или завтра утром постараюсь сделать перевод.
    2. Valentin Rasulov 20 june 2012, 14:50 # 0
      Молодец Василий! Так как у Вас опыт есть по ресурсам, написали бы топик по созданию ресурса через runProcessor — а то на форумах смотрю, такое городят (хотя и сам такое городил) :)… Думаю новичкам будет полезен топик!
      Удачи.
      1. Василий Наумкин 20 june 2012, 14:59 # 0
        Да на целый топик тут никак не тянет. В тексте есть ссылка на процессор miniShop, который создает ресурс и дальше с ним работает.

        Если нужно просто создать ресурс — вот простейший код:

        $data = array('pagetitle' => 'Название', 'content' => 'Текст страницы');
        $modx->runProcessor('resource/create', $data);

        Все.
        В массив $data можно пихать любые свойства ресурса MODX, если их нет — будут дефолтные. Можно даже пустой массив отправить, тогда появится «Ресурс без названия».

        Считаете, нужно это добавить в заметку?
        1. Valentin Rasulov 20 june 2012, 15:33 # 0
          Хи. Это всё понятно, для некоторых хватит выражения «есть runProcessor», а для многих нужно по полочкам разлаживать.
          Я имел в виду простоу топик-урок, как с фронта создать ресурс по правильному (модексовски). Думаю это как отдельная тема уже.
          Хотя гнать коня не нужно наверное, если будет нужно кому, то попросят!
          1. Василий Наумкин 20 june 2012, 15:37 # 0
            Чтобы что-то с фронта делать, надо сначала права юзера освоить.

            Да и вообще, непростая это тема, много заморочек.

            Но на будущее оставим этот вопрос. Может и напишу, если буду что-то такое делать.
            1. Andrew Vakulenko 20 june 2012, 23:26 # 0
              >>Я имел ­в виду простоу топик-урок, как с фронта создать ресурс по правильному ­(модексовски)
              да, это было бы здорово!
              1. Василий Наумкин 20 june 2012, 23:54 # 0
                Права доступа уже освоили?

                community.modx-cms.ru/blog/documentation/869.html
                1. Andrew Vakulenko 21 june 2012, 11:00 # 0
                  да, не могу сказать, что полностью освоил всю гибкость настройки прав доступа, но создать группу пользователей, предоставить политику, доступ к контекстам, связать с пользователями и др. настойки — это можно.
                  1. Василий Наумкин 21 june 2012, 11:05 # 0
                    Окей.

                    Будет время — напишу про создание ресурсов с фронтенда — добавляйте rss блога себе в ридер.
                    1. Bluetenstadt 07 july 2016, 20:26 # 0
                      Статья была уже?
        2. Andrew Vakulenko 20 june 2012, 23:24 # 0
          Спасибо, что еще шире раскрыли тему про runProcessor. Конечно, пока не попробуешь в боевых условиях, не сможешь оценить и понять весь «механизм» работы, будем пробовать :)
          1. Евгений Тимочкин 29 october 2012, 16:26 # 0
            Здравствуйте.
            Тема очень интересна. Сейчас рассматриваю возможность использования процессоров для создания контента из front-end'a сайта (ну не хочу я пускать всех в mgr, да и конечному пользователю будет удобно из front-end'a работать). Конкретно пытаюсь сделать добавление статей в блог. Может я конечно изобретаю велосипед, и все уже написано за меня? Не поможете советом по этому вопросу?
            1. Андрей Ятин 10 may 2013, 17:23 # 0
              В общем процессоры это действительно круто. Если будет время напишите пожалуйста краткую заметку, или внесите пару строк в текущую, я не сразу, думаю как и многие, понял что они могут работать с любыми типами (надо указывать class_key).
              1. Alexey Barsukov 10 october 2013, 12:34 # 0
                Наткнулся на одну особенность функции runProcessor, после нее лог переводится в обычный error-лог modx. Если пишете свой компонент, который работает с консолью через регистры, может сложиться впечатление, что функция runProcessor просто не работает, но это не так. Просто лог из регистров после ее вызова переводится в error-лог. Чтобы вернуть его обратно в регистры, нашел такой способ: нужно после runProcessor писать:
                $this->modx->request->registerLogging($_POST);
                . Как я понял, эта функция смотрит наличие ключей регистров в массиве (в данном случае $_POST) и если информации хватает (присутствуют ключи topic и т.д.), снова переводит лог в регистры.
                1. Константин Ильин 29 september 2016, 23:09 # 0
                  Никак не могу понять как получить id ресурса в функции prepareQueryBeforeCount для процессора modObjectGetListProcessor

                  public function prepareQueryBeforeCount(xPDOQuery $c)
                      {
                          $resource_id = intval($this->getProperty('resource_id'));
                          $c->where(array('id' => $resource_id));
                          return $c;
                      }
                  
                  причем если указать явно $resource_id = 1 работает.

                  И сразу еще вопрос только для процессора modObjectGetProcessor
                  Подскажите как там получать и возвращать данные, немного не понял его.
                  1. but1head 30 september 2016, 02:52 # 0
                    Обычно параметр (поиск) внутри query, у вас точно resource_id передается?
                    public function prepareQueryBeforeCount(xPDOQuery $c)
                        {
                            $query = trim($this->getProperty('query'));
                            if ($query) {
                                $c->where(array(
                                    'name:LIKE' => "%{$query}%",
                                    'OR:description:LIKE' => "%{$query}%",
                                ));
                            }
                            return $c;
                        }
                    
                    1. Константин Ильин 30 september 2016, 10:58 # 0
                      модель такая:
                           <object class="trItem" extends="modResource">
                              <composite alias="Trans" class="trItemData" local="id" foreign="id_res" cardinality="many" owner="local" />
                          </object>
                      
                          <object class="trItemData" table="translator_data" extends="xPDOSimpleObject">
                              <field key="id_res" dbtype="int" precision="10" phptype="integer" null="false" default=""/>
                      	/***еще поля***/
                      	<aggregate alias="TranMain" class="trItem" local="id_res" foreign="id" cardinality="one" owner="foreign" />
                          </object>
                      
                      теперь запускается процессор на панели ресурса(modx-resource-tabs) getlist — trItemData, в котором получаю объекты trItemData, но проблема в том, что получаю все объекты всех ресурсов. Отсюда становится понятно, что надо указывать id текущего ресурса для выборки объектов trItemData(собственно поле id_res для этого и делал), но вот как получить для where id текущего ресурса никак не пойму.
                      Проще говоря как получить объекты trItemData только текущего ресурса(trItem)?
                      1. Сергей Шлоков 30 september 2016, 11:34 # +1
                        В вызове getlist процессора нужно добавить параметр с id текущего ресурса, а уже в процессоре его ловить. В вашем примере вы в процессоре ловите параметр resource_id
                        $this->getProperty('resource_id')
                        
                        А вы его передаёте в процессор?
                        1. Константин Ильин 30 september 2016, 12:07 # 0
                          Это мой первый компонент и я думал под танцы с бубном он передается автоматически))

                          Спасибо большое Сергей! все получилось, подсмотрел в других компонентах

                          Сделал так:
                          В плагине создал запись
                          $modx->controller->addHtml('<script type="text/javascript">
                                  	Trans.config = ' . $modx->toJSON($Trans->config) . ';
                                  	Trans.config.res_id = "'.$resource->get('id').'";
                                  	
                          		Trans.config.connector_url = "' . $Trans>config['connectorUrl'] . '";
                          			'.$trConfig.'
                          			</script>
                                  ');
                          в js
                          Trans.grid.Items = function (config) {
                              Ext.applyIf(config, {
                                  url: Trans.config.connector_url,
                                	*****
                                  baseParams: {
                                      action: 'mgr/transitems/getlist' ,
                                      res_id: Trans.config.res_id
                                  }, 
                          	*****
                          1. Сергей Шлоков 30 september 2016, 12:53 # +1
                            Пожалуйста!
                            В плагине 2 последние строчки конфига можно убрать. Так как res_id можно получить в js. А Trans.config.connector_url дублирует Trans.config.connectorUrl.
                            Js будет выглядеть так
                            Trans.grid.Items = function (config) {
                                Ext.applyIf(config, {
                                    url: Trans.config.connectorUrl,
                                  	*****
                                    baseParams: {
                                        action: 'mgr/transitems/getlist',
                                        res_id: MODx.request.id //id текущего документа
                                    }, 
                            	*****
                            
                            1. Константин Ильин 30 september 2016, 13:52 # 0
                              О как) Посмотрел MODx.request, получается из get берется. Вот по extJs modx'а мало что-то доков или я плохо ищу, посоветуешь что нибудь?
                              Насчет connectorUrl проглядел) вот что значит не подумав скопировать)
                              Еще раз спасибо Сергей!
                              1. Сергей Шлоков 01 october 2016, 09:00 # 0
                                Вот по extJs modx'а мало что-то доков или я плохо ищу, посоветуешь что нибудь?
                                Копать исходники. Вот тут есть немного.
                  You need to login to create comments.