Разработка кастомного плагина MODX
Плагин MODX — PHP-код, привязанный к системным событиям. Перехватывает моменты жизненного цикла CMS: загрузка страницы, сохранение ресурса, аутентификация, ошибки, кэш. В отличие от сниппета, плагин не возвращает контент — он изменяет поведение системы.
Привязка к событиям
// Плагин AutoSEO — автоматически заполняет SEO-поля
// События: OnBeforeDocFormSave (до сохранения ресурса в менеджере)
$eventName = $modx->event->name;
switch ($eventName) {
case 'OnBeforeDocFormSave':
handleBeforeSave($modx, $resource, $mode);
break;
case 'OnDocFormSave':
handleAfterSave($modx, $resource, $mode);
break;
}
function handleBeforeSave($modx, $resource, $mode): void {
// Автозаполнение description из introtext
if (empty($resource->get('description')) && !empty($resource->get('introtext'))) {
$description = mb_substr(strip_tags($resource->get('introtext')), 0, 160);
$resource->set('description', $description);
}
// Автозаполнение longtitle из pagetitle
if (empty($resource->get('longtitle'))) {
$resource->set('longtitle', $resource->get('pagetitle'));
}
}
Популярные события MODX
| Событие | Когда | Применение |
|---|---|---|
| OnPageNotFound | 404 ошибка | Кастомный 404, редиректы |
| OnHandleRequest | Каждый запрос | Редиректы, геолокация |
| OnLoadWebDocument | Загрузка ресурса | Модификация контента |
| OnBeforeDocFormSave | До сохранения в менеджере | Валидация, автозаполнение |
| OnDocFormSave | После сохранения | Синхронизация с внешней системой |
| OnUserLogin | Вход пользователя | Логирование, 2FA |
| OnCacheUpdate | Очистка кэша | Синхронизация CDN |
| OnSiteRefresh | Сброс настроек | Хуки деплоя |
Плагин 301-редиректов
<?php
// Плагин Redirects
// Событие: OnPageNotFound
$uri = $_SERVER['REQUEST_URI'];
$redirects = $modx->getOption('custom_redirects', null, '');
// Редиректы хранятся как JSON в системной настройке
$redirectMap = json_decode($redirects, true) ?? [];
foreach ($redirectMap as $from => $to) {
if (strpos($uri, $from) === 0) {
$target = str_replace($from, $to, $uri);
$modx->sendRedirect($target, ['responseCode' => 'HTTP/1.1 301 Moved Permanently']);
exit;
}
}
// Показать кастомную 404 страницу
$errorPage = $modx->getOption('error_page', null, 404);
$modx->sendForward($errorPage, ['response_code' => 'HTTP/1.1 404 Not Found']);
Плагин геолокации
<?php
// Плагин GeoRedirect
// Событие: OnHandleRequest
if ($modx->context->key === 'web') {
$ip = $_SERVER['HTTP_CF_CONNECTING_IP'] ?? $_SERVER['REMOTE_ADDR'] ?? '';
// Только для первого посещения (без куки)
if (!isset($_COOKIE['geo_redirect_done'])) {
$geoData = getGeoByIp($ip);
$currentUri = $_SERVER['REQUEST_URI'];
$countryContextMap = ['BY' => 'by', 'UA' => 'ua', 'KZ' => 'kz'];
$targetContext = $countryContextMap[$geoData['country']] ?? null;
if ($targetContext && $modx->context->key !== $targetContext) {
setcookie('geo_redirect_done', '1', time() + 86400, '/');
$modx->sendRedirect('/' . $targetContext . $currentUri);
exit;
}
}
}
function getGeoByIp(string $ip): array {
// MaxMind GeoIP2 или ipapi.co
$response = file_get_contents("https://ipapi.co/{$ip}/json/");
return json_decode($response, true) ?? ['country' => 'RU'];
}
Плагин синхронизации с CRM
<?php
// Плагин CRMSync
// Событие: OnDocFormSave
if ($modx->event->params['mode'] === modSystemEvent::MODE_UPD
&& in_array($resource->get('template'), [5, 6])) { // только определённые шаблоны
$crmApiKey = $modx->getOption('crm_api_key');
$crmApiUrl = $modx->getOption('crm_api_url');
$data = [
'id' => $resource->id,
'title' => $resource->get('pagetitle'),
'url' => $modx->makeUrl($resource->id, '', '', 'full'),
'price' => $resource->getTVValue('price'),
'category' => $resource->get('parent'),
'updated' => time(),
];
$ch = curl_init($crmApiUrl . '/products/' . $resource->id);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_POSTFIELDS => json_encode($data),
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Authorization: Bearer ' . $crmApiKey,
],
CURLOPT_TIMEOUT => 5,
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode >= 400) {
$modx->log(modX::LOG_LEVEL_ERROR, "CRM sync failed for resource {$resource->id}: {$response}");
}
}
Хранение плагина в файле
// В редакторе плагина: File Source = файловая система
// Файл: core/components/myplugin/myplugin.plugin.php
// Это позволяет версионировать плагин в git
Плагин StaticElements синхронизирует плагины между БД и файловой системой.
Сроки
Разработка плагина для одного-двух событий — 0.5–1 день. Сложный плагин с множеством событий и внешними интеграциями — 2–4 дня.







