Эксперт по MODX
2,4
рейтинг
8 октября 2012 в 23:27

Разработка → Парадигма программирования процессорами в MODx Revolution

MODX*
Сразу оговорюсь, что данная статья — рассуждения и пища для размышлений. Совершенно не хочу устраивать холивары и навязывать другим свои методы программирования. Просто рассказываю, так как по себе ощущаю, что используемые мною методы программирования за последний год очень сильно изменились и в корне отличаются от методов предшествующих нескольких лет.

В данной статье я хотел бы рассказать насколько MODx Revolution в целом изменил мой подход к программированию.
Не знаю как кто программирует, но я считаю, что давным давно уже программирую методами ООП. К чему же в общих чертах сводилось программирование? Я писал (или брал готовые) классы под свои задачи (класс по работе с базой данных, класс по работе с шаблонами, класс еще под что-нибудь). Большинство классов были довольно большие и выполняли множество необходимых задач по своему профилю. Чаще всего по мере роста проекта многие классы разрастались, или обзаводились расширениями, или и то и другое. Так или иначе, уверен многие сталкивались с ситуацией часового разбора объекта в пару тысяч строк и несколькими десятками методов, чтобы разобраться куда можно внести очередные изменения, но так, чтобы при этом что-то другое не сломалось. Но на мой взгляд сложнее всего обеспечить гармоничное взаимодействие различных объектов между собой, особенно в плане перехвата ошибок при выполнении тех или иных действий, а главное в момент ошибки решить насколько она критичная и стоит ли прервать процесс выполнения, или можно идти дальше. А еще сюда же приписать клиент-серверные решения, чтобы стандартизировать ответы выполнения и для серверной части (с дальнейшей ее отрисовкой в шаблоны), и для Ajax-запросов.

Какой же именно инструментарий предлагает MODx Revolution для программирования логики проекта? Всего два класса: Процессор (выполняется классом modProcessor) и Коннектор (выполняется классом modConnector).
Что это такое? Процессор — отдельный файл с чаще всего одной или несколькими мелкими задачами, в результате выполнения которого ответ должен быть только положительным (что будет свидетельствовать о положительном результате выполнения), или отрицательный (желательно с конкретным сообщением об ошибке), что будет само собой говорить, что произошло что-то критически неправильно.

Приведу маленький пример. Мы хотим создать новый документ. Если документ будет создан, то замечательно, если нет, нам необходимо получить сообщение об ошибке.

1. Создадим папку наших процессоров в core/model/modx/processors/, назовем ее, к примеру, assets/
2. Создадим файл-процессор doc/create.php (полный путь получится core/model/modx/processors/assets/doc/create.php)
3. Пропишем в нем нужный нам код
<?
if(!$scriptProperties['pagetitle']){
	return $modx->error->failure('Не был указан заголовок страницы');
}
if(!isset($scriptProperties['parent'])){
	return $modx->error->failure('Не был указан родитель для документа');
}

$doc = $modx->newObject('modResource', scriptProperties);

if(!$doc->save()){
	return $modx->error->failure('Не удалось сохранить документ');
}

return $modx->error->success('Документ успешно создан', $doc->toArray());


4. А теперь в нужном нам месте вызовем данный процессор
$response = $modx->runProcessor('assets.doc.create', array(
    'parent' => 0,
    'pagetitle' => 'Наш документ'
));

if($response->isError()){
	print "Произошла ошибка". $response->getMessage();
}
else{
	$object = $response->getObject();
	print "Был создан документ с ID {$object['id']}";
}

/*$modx->runProcessor - стандартный метод MODx. Расширение файла .php для исполняемого файла добавляется автоматически, то есть нам не надо писать create.php
Точка - разделитель папок. То есть в данном случае assets.doc.create говорит о том, что исполняемый файл лежит
в папке assets/doc/
По умолчанию поиск  исполняемого  файла выполняется в папке core/model/modx/processors/
Вы  можете передать третьим параметром массив опций, в частности указав свою нанку процессоров
$modx->runProcessor($path, $properties, array( processors_path => self::$processorsPath));
Переданный второй аргумент $properties - массив параметров, который будет виден в рамках процессора в виде переменной scriptProperties. Этот положительный момент мы оценим чуть позже.
Так же внутри процессора виден объект $modx, что освобождает нас  от необходимости глобалить его в рамках наших пользовательских методов.
*/


Это частный пример и не раскрывает даже примерно того, о чем я хотел сказать.
Красота использования данного метода больше раскрывается при использовании сразу нескольких вложенных процессоров.
Вот мы вызовем процессор, в котором вызываются другие процессоры (приведен код именно вызываемого процессора).
<?

$user = false;
$doc  = false;

// Пытаемся создать пользователя
$response = $modx->runProcessor('security/user/create', $scriptProcessor);

// Если ошибка, возвращаем сообщение из  выполняемого процессора
if($response->isError()){
	return $modx->error->failure($response->getMessage());
}
else{
	// Иначе получаем пользователя
	$user = $response->getObject();
}

// Если все ОК, выполняем процессор на создание документа
$response = $modx->runProcessor('assets.doc.create', array(
    'parent' => 0,
    'pagetitle' => 'Наш документ'
));

// Опять-таки, если ошибка, возвращаем сообщение из выполняемого процессора
if($response->isError()){
	return $modx->error->failure($response->getMessage());
}
else{
	// Иначе получаем документ
	$doc = $response->getObject();
}

// Если все ОК, возвращяем успех
return array(
	'success' => true,
	'message' => '',
	'object' => array(
		'user' 	=> $user,
		'doc'	=> $doc,
	)
);

/*
	Обратите внимание, что в конце мы использовали не метод $modx->error->success(failure),
	а вернули именно массив.
	Принципиально на выходе вы можете ничего и не заметить, то есть через $response->getObject() вы получите тот же самый объект,
	но  все же разница есть.
	Возвращая $modx->error->success('', $object); нельзя вернуть именно объект. 
	Возвращаемый объект методом $modx->error->success будет преобразован в массив,
	при чем преобразование будет выполнено рекурсивно по всем уровням и ключи разных уровней будут переписаны,
	в результате чего можно получить одноуровневый массив с потерей данных.
	Именно по этому не смотря на то, что системный процессор security/user/create возвращает не $user->toArray(), а именно $user,
	в результате мы получаем не объект $user, а именно массив. То есть мы не сможем с ним выполнить методы класса modUser типа ->toArray()
	Возвращая сразу массив с элементом 'object', можно вернуть именно объект, 
	то есть на выходе выполнить типа $response->getObject()->get('id');
*/

Таким образом на выходе мы получим четкий результат, или $response->isError(), или нет.
При этом мы не задумываемся о степени вложенности этих процессоров, сколько бы уровней вложенности процессоров не было, при правильной организации выполнения независимо от того, на каком уровне вложенности произойдет ошибка, мы будем об этом знать и выведем текстовое сообщение, так как return $modx->error->failure($response->getMessage()); каждый раз будет возвращать сообщение следующего вложенного процессора.

Надо отметить, что вся админская часть MODx написана на процессорах (посмотрите папку /core/model/modx/processors/) и если правильно использовать процессоры, то многие сторонние пакеты могут вообще не понадобиться.
Что может быть проще, чем $modx->runSnippet('security/login', $scriptProcessor);?
MODx выполнит все необходимое и в случае чего вернет одно из прописанных на все случаи сообщений об ошибке. В этих процессорах и проверки на права доступов, и всякие системные переменные, и вызовы других связанных процессоров. В общем все что нужно…

Таким образом мы как бы под каждое небольшое действие создаем нужный нам процессор и для выполнения более обширных задач собираем отдельные процессоры в кучу.
Во-первых, такие отдельные процессоры легче понять. В них четко понятно что для чего делается и где логическое завершение задачи. И наверно самое главное — меньше всяких лишних ветвлений if, else и т.п., так как у некоторых эти if-else порой достигают нескольких сотен (а то и тысяч) строк кода.
Во-вторых, выход из строя одного процессора оказывает гораздо меньшее негативное влияние на работу всего сайта, по сравнению с выходом одного большого класса из-за ошибки разбора кода или типа того (случай конечно непонятный нормальному программисту, но тем не менее бывает всякое).
Ну а в-третьих, в целом движок с подобной реализацией обслуживается гораздо легче и интуитивней.

Но еще один очень мощный плюс в данном подходе — это стандартизированные ответы как на стороне сервера, так и браузера (при использовании Ajax).
Если вы отправите запрос на коннектор, в котором будет вызван нужный вам процессор, ответ будет возвращен в виде JSON-объекта, где будут и success, и message и object.
Это позволяет использовать одни и те же процессоры как для серверной логики, так и псевдо-клиентской.
Если будет интерес, более подробно о коннекторах напишу позже.
Ланец Николай @Fi1osof
карма
13,0
рейтинг 2,4
Эксперт по MODX
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

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

  • +1
    Позвольте, Вас немного исправить. Есть неофициальный ответ от представителя MODX LLC о том, что MODx является устаревшим названием платформы. Сейчас правильно писать MODX.
    • +2
      ОК, обязательно возьму это на заметку.
  • 0
    Увидел заголовок статьи, завис, мозг начал выдавать варианты про программирование видеокартами, HDD, RAM…
    • 0
      Предполагал такой вариант. Но все же статья в блоге MODX Revolution, и многим любителям этого движка так или иначе понятно, что такое процессор.
      Но рад был бы, если бы кто-то предложил какое-то свое название всему этому безобразию…
      • 0
        Просто я, честно говоря, впервые с этим сталкиваюсь. Поэтому программирование процессорами для меня — это что-то из области забивания гвоздей микроскопами.
        • 0
          Но ведь и процессор — это не всегда железка, так же как и шина, винчейстер и т.п.
          В общем как я и предупреждал с самого начала — это статья-размышления. Некоторые вещи сложно бывает назвать своими именами.
  • НЛО прилетело и опубликовало эту надпись здесь
    • +1
      Для меня более важен момент, что проверки будут проходить как задумали авторы MODX, а не как я криво написал.

      И это гарантирует, что расширение, работающее через процессоры, будет работать во всех новых версиях MODX и все системные действия будут отрабатывать штатно.
    • 0
      Да нет, вы меня видимо неправильно поняли.
      Первое: я не говорю, что это мое творение. Об этом вообще не было речи. Я говорил о своем стиле разработки, который перенял от MODX.
      Второе: я не говорил, что правильно создавать ресурс через объект. Безусловно правильно объект создавать через процессор resource/create
      Но предполагалось же, что кто-то вообще не знает что такое процессор, и я хотел вкратце показать маленький процессор, так как $doc->save() интуитивней более понятен для многих, в том числе тех, что с MODX может и не сталкивался, но сталкивался с PDO.
      А суть была показать работу вложенных процессоров, так как это в корне отличается от привычных методов написания профильных классов под все случаи жизни.
  • +1
    Я бы только вот уточнил такую деталь, как «классные» процессоры. Все же ребята из MODX сделали огромную работу, переведя кучу спагетти-кода процессоров в классы. И раз речь шла об ООП, то правильнее было бы использовать именно их. За счет наследования кода становится намного меньше в самих процессорах и код более понятный.
    • –1
      И скажем честно, обычные процессоры после «классных» — херня на постном масле.

      Заметка про них и заготовка компонента, с их использованием — там же есть и видео, как легко начать делать новый компонент.
    • 0
      Даже если это «классный» процессор, все же суть его остается прежней — выполнить какое-то свое, локальное действие и или вернуть успех, или ошибку. Как бы то ни было, это все равно куча файлов под свои задачи.
      • +1
        Универсальных, удобных, с кучей скрытого, но нужного кода. Подход себя вполне оправдывает, особенно когда имеем дело с аджаксовой админкой.
        • 0
          Да, это я как раз и пытался донести. Самое главное в этом подходе, что независимо от того, вызову я этот процессор на стороне сервера чисто для серверной логики, или запрошу выполнение через коннектор, я знаю точно, что я получу стандартизированный ответ и все будет ОК.
          Лично для меня сейчас такой подход кажется наилучшим.
  • 0
    правильно ли я понял, что процессоы (если очень упрощенно) представляют собой аналоги функций?
    • 0
      В более ранних ветках MODX-а именно так и было. Один процессор — как бы одна функция, котораямогла вызывать и другие процессоры-функции, но обязательно возвращала какой-то результат (собственный, или результат других процессоров). А не возврат результата расценивается как отрицательный результат.

      Но теперь в MODX процессоры — это уже полноценные классы. Есть базовый класс modProcessor, есть еще несколько классов, производных от него, для выполнения тривиальных задач (create, update, remove, list и т.п.). И теперь своими процессорами можно просто расширять эти базовые, в которых уже прописано много полезного функционала, и процессор на создание своего объекта-записи может уложиться в 10 строк, включая проверку доступов, вывод результата и т.п. Но суть осталась та же — четко вернуть какой-то результат, а задача четко локализована.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.