Skip to content
  1. Компоненты
  2. MiniShop3
  3. Разработка
  4. События
  5. Заказ

События заказа ​

Добавление, валидация и удаление полей заказа, а также его оформление.

Какие события могут прервать операцию ​

В отличие от событий корзины, здесь ядро читает ответ плагина почти всюду — в том числе у after-событий. Вернуть ошибку через $modx->event->output(...) можно у всех событий страницы, кроме msOnMgrCreateOrder: его вызывают, не читая ответ.

Поэтому msOnRemoveFromOrder, msOnEmptyOrder и msOnCreateOrder способны провалить уже выполненную операцию: поле удалено или заказ создан, но клиент получит ошибку.

У событий валидации ответ работает иначе — не прерывает операцию, а подменяет значение: msOnBeforeValidateOrderValue и msOnValidateOrderValue возвращают value, msOnErrorValidateOrderValue — error.

msOnBeforeAddToOrder ​

Вызывается перед добавлением или изменением поля заказа.

Для служебных полей событие не придёт

С 1.14.2-beta1 OrderFieldManager отклоняет служебные колонки черновика до вызова события. Плагин, который перехватывал запись в customer_id, status_id, cost или properties, больше не сработает. Проверка одна для всех четырёх событий поля: msOnBeforeAddToOrder, msOnAddToOrder, msOnBeforeRemoveFromOrder, msOnRemoveFromOrder. Перечень закрытых колонок — в разделе какие поля фронтенд менять не вправе.

Параметры ​

ПараметрТипОписание
keystringКлюч поля
valuemixedЗначение поля
draftmsOrder | nullТекущий черновик заказа (может отсутствовать на самом первом шаге)

Прерывание операции ​

php
<?php
switch ($modx->event->name) {
    case 'msOnBeforeAddToOrder':
        $key = $scriptProperties['key'];
        $value = $scriptProperties['value'];

        // Запретить выбор определённого способа доставки
        if ($key === 'delivery_id' && $value == 5) {
            $modx->event->output('Доставка временно недоступна');
            return;
        }
        break;
}

Модификация данных ​

php
<?php
switch ($modx->event->name) {
    case 'msOnBeforeAddToOrder':
        $key = $scriptProperties['key'];
        $value = $scriptProperties['value'];

        $values = &$modx->event->returnedValues;

        // Форматирование телефона
        if ($key === 'phone') {
            $values['value'] = preg_replace('/\D/', '', $value);
        }

        // Приведение email к нижнему регистру
        if ($key === 'email') {
            $values['value'] = strtolower(trim($value));
        }
        break;
}

msOnAddToOrder ​

Вызывается после добавления поля в заказ (после успешной валидации).

Параметры ​

ПараметрТипОписание
keystringКлюч поля
valuemixedВалидированное значение
draftmsOrder | nullТекущий черновик заказа

Пример использования ​

php
<?php
switch ($modx->event->name) {
    case 'msOnAddToOrder':
        $key = $scriptProperties['key'];
        $value = $scriptProperties['value'];
        /** @var \MiniShop3\Model\msOrder|null $draft */
        $draft = $scriptProperties['draft'];

        // Логирование изменений
        $modx->log(modX::LOG_LEVEL_INFO,
            "[Order] Поле {$key} установлено: {$value}"
        );

        // При выборе доставки — обновить связанные поля черновика
        if ($key === 'delivery_id' && !empty($value) && $draft) {
            // ... через сервисы MS3 при необходимости
        }
        break;
}

msOnBeforeValidateOrderValue ​

Вызывается перед валидацией значения поля.

Параметры ​

ПараметрТипОписание
keystringКлюч поля
valuemixedЗначение для валидации
orderDataarrayСнимок текущих полей заказа (доступ к смежным значениям, например к delivery_id при валидации payment_id)

Модификация данных ​

php
<?php
switch ($modx->event->name) {
    case 'msOnBeforeValidateOrderValue':
        $key = $scriptProperties['key'];
        $value = $scriptProperties['value'];

        $values = &$modx->event->returnedValues;

        // Предобработка перед валидацией
        if ($key === 'index') {
            // Удалить пробелы из индекса
            $values['value'] = str_replace(' ', '', $value);
        }
        break;
}

msOnValidateOrderValue ​

Вызывается после успешной валидации значения поля.

Параметры ​

ПараметрТипОписание
keystringКлюч поля
valuemixedВалидированное значение

Модификация данных ​

php
<?php
switch ($modx->event->name) {
    case 'msOnValidateOrderValue':
        $key = $scriptProperties['key'];
        $value = $scriptProperties['value'];

        $values = &$modx->event->returnedValues;

        // Постобработка после валидации
        if ($key === 'city') {
            // Добавить регион к городу
            $values['value'] = $value . ', Московская область';
        }
        break;
}

msOnErrorValidateOrderValue ​

Вызывается при ошибке валидации поля.

Параметры ​

ПараметрТипОписание
keystringКлюч поля
valuemixedНевалидное значение
errorarrayМассив ошибок ['field' => 'сообщение']

Модификация сообщения об ошибке ​

php
<?php
switch ($modx->event->name) {
    case 'msOnErrorValidateOrderValue':
        $key = $scriptProperties['key'];
        $error = $scriptProperties['error'];

        $values = &$modx->event->returnedValues;

        // Свои сообщения об ошибках
        if ($key === 'email') {
            $values['error'] = [$key => 'Введите корректный email для получения чека'];
        }

        if ($key === 'phone') {
            $values['error'] = [$key => 'Телефон нужен для связи курьера'];
        }
        break;
}

Отмена ошибки ​

php
<?php
switch ($modx->event->name) {
    case 'msOnErrorValidateOrderValue':
        $key = $scriptProperties['key'];
        $value = $scriptProperties['value'];

        // Для определённых полей — игнорировать ошибку
        if ($key === 'comment') {
            $values = &$modx->event->returnedValues;
            $values['error'] = []; // Пустой массив = нет ошибки
        }
        break;
}

msOnBeforeRemoveFromOrder ​

Вызывается перед удалением поля из заказа.

Параметры ​

ПараметрТипОписание
keystringКлюч удаляемого поля
draftmsOrderТекущий черновик заказа

Прерывание операции ​

php
<?php
switch ($modx->event->name) {
    case 'msOnBeforeRemoveFromOrder':
        $key = $scriptProperties['key'];

        // Запретить удаление обязательных полей
        $required = ['delivery_id', 'payment_id', 'email'];
        if (in_array($key, $required)) {
            $modx->event->output('Это поле нельзя удалить');
            return;
        }
        break;
}

msOnRemoveFromOrder ​

Вызывается после удаления поля из заказа.

Параметры ​

ПараметрТипОписание
keystringКлюч удалённого поля
draftmsOrderТекущий черновик заказа

Пример использования ​

php
<?php
switch ($modx->event->name) {
    case 'msOnRemoveFromOrder':
        $key = $scriptProperties['key'];
        /** @var \MiniShop3\Model\msOrder $draft */
        $draft = $scriptProperties['draft'];

        // При удалении способа доставки — сбросить способ оплаты
        if ($key === 'delivery_id') {
            // ... через сервисы MS3 при необходимости
        }
        break;
}

msOnBeforeEmptyOrder ​

Вызывается перед очисткой черновика заказа (OrderDraftManager::clean, API/контроллер order/clean). Плагин может прервать очистку через $modx->event->output(...).

Параметры ​

ПараметрТипОписание
draftmsOrderЧерновик, который сейчас очищают

Что остаётся после очистки ​

Ядро обнуляет поля адреса и заказа, кроме служебных: у адреса — id, order_id, user_id, createdon; у заказа — id, user_id, token, status_id, createdon.

php
<?php
switch ($modx->event->name) {
    case 'msOnBeforeEmptyOrder':
        /** @var \MiniShop3\Model\msOrder $draft */
        $draft = $scriptProperties['draft'];

        if ($draft->get('delivery_id') && /* ваш флаг «заказ в логистике» */) {
            $modx->event->output('Черновик нельзя очистить: доставка уже зарезервирована');
            return;
        }
        break;
}

msOnEmptyOrder ​

Вызывается после очистки полей черновика — поля адреса и заказа к этому моменту уже обнулены и сохранены. Ошибка плагина (output) делает ответ вызывающему коду неуспешным, но не откатывает сохранённое: черновик остаётся очищенным.

Параметры ​

ПараметрТипОписание
draftmsOrderЧерновик уже с обнулёнными полями

msOnSubmitOrder ​

Вызывается при отправке заказа (нажатие кнопки «Оформить»).

Параметры ​

ПараметрТипОписание
handler\MiniShop3\Services\Order\OrderSubmitHandlerОбработчик оформления заказа
draftmsOrderЧерновик заказа на момент отправки
orderDataarrayСнимок текущих полей заказа (включая адресные)
dataarrayДополнительные данные, переданные в запрос оформления

Прерывание операции ​

php
<?php
switch ($modx->event->name) {
    case 'msOnSubmitOrder':
        /** @var \MiniShop3\Model\msOrder $draft */
        $draft = $scriptProperties['draft'];
        $orderData = $scriptProperties['orderData'];

        // Проверка минимальной суммы заказа
        if (($draft->get('cart_cost') ?? 0) < 1000) {
            $modx->event->output('Минимальная сумма заказа — 1000 ₽');
            return;
        }

        // Проверка рабочего времени
        $hour = (int)date('G');
        if ($hour < 9 || $hour > 21) {
            $modx->event->output('Заказы принимаются с 9:00 до 21:00');
            return;
        }
        break;
}

Модификация данных ​

Дополнительные поля заказа возвращают в ключе data — ядро применит их к черновику обычной цепочкой add() ещё до создания заказа.

php
<?php
switch ($modx->event->name) {
    case 'msOnSubmitOrder':
        $data = $scriptProperties['data'];

        $values = &$modx->event->returnedValues;

        // Добавить UTM-метки из сессии в дополнительные данные сабмита
        if (!empty($_SESSION['utm'])) {
            $data['properties']['utm'] = $_SESSION['utm'];
            $values['data'] = $data;
        }

        // Добавить источник заказа
        $data['properties']['source'] = $_SERVER['HTTP_REFERER'] ?? 'direct';
        $values['data'] = $data;
        break;
}

msOnBeforeCreateOrder ​

Вызывается перед финальным созданием заказа (присвоением статуса «Новый»). Три сценария:

  1. Оформление на фронтенде — OrderSubmitHandler (в params нет origin / from_manager; есть handler, customFields)
  2. Финализация в админке — OrderFinalizeService (origin=manager, from_manager=true, service)
  3. Программное создание — ProgrammaticOrderService → finalize (origin=integration, from_manager=false, service)

Параметры ​

ПараметрТипОписание
msOrdermsOrderОбъект заказа (всегда)
handler\MiniShop3\Services\Order\OrderSubmitHandlerТолько оформление с фронтенда
service\MiniShop3\Services\Order\OrderFinalizeServiceТолько finalize-пути (админка / integration)
originstringТолько finalize-пути: manager | integration (константы OrderOrigin)
from_managerboolПрисутствует на обоих finalize-путях (true при origin=manager, false при origin=integration); отсутствует только при оформлении с фронтенда
customFieldsarrayТолько фронтенд — поля из properties._validated

Различение сценариев

php
$origin = $scriptProperties['origin'] ?? null;
$fromManager = !empty($scriptProperties['from_manager']);

if ($fromManager) {
    // карточка заказа в админке
} elseif ($origin === \MiniShop3\Services\Order\OrderOrigin::INTEGRATION) {
    // ProgrammaticOrderService / cron
} elseif (!empty($scriptProperties['handler'])) {
    // фронтенд
}

Прерывание операции ​

php
<?php
switch ($modx->event->name) {
    case 'msOnBeforeCreateOrder':
        /** @var \MiniShop3\Model\msOrder $order */
        $order = $scriptProperties['msOrder'];

        // Проверка остатка на складе (поле msProductData.stock, доступно через msProduct::get)
        foreach ($order->getMany('Products') as $product) {
            $msProduct = $product->getOne('Product');
            if (!$msProduct) {
                continue;
            }
            $stock = (float) ($msProduct->get('stock') ?? 0);
            if ($product->get('count') > $stock) {
                $modx->event->output(sprintf(
                    'Товар "%s" недоступен в нужном количестве',
                    $msProduct->get('pagetitle')
                ));
                return;
            }
        }
        break;
}

Модификация заказа ​

php
<?php
switch ($modx->event->name) {
    case 'msOnBeforeCreateOrder':
        $order = $scriptProperties['msOrder'];

        // Добавить комментарий менеджеру
        $properties = $order->get('properties') ?? [];
        $properties['manager_note'] = 'Заказ создан ' . date('d.m.Y H:i');
        $order->set('properties', $properties);
        break;
}

msOnCreateOrder ​

Вызывается после успешного создания заказа. Парный к msOnBeforeCreateOrder — те же три сценария и тот же набор параметров (на фронтенде без origin).

Параметры ​

ПараметрТипОписание
msOrdermsOrderСозданный заказ (всегда)
handler\MiniShop3\Services\Order\OrderSubmitHandlerТолько фронтенд
service\MiniShop3\Services\Order\OrderFinalizeServiceАдминка или integration
originstringТолько finalize-пути
from_managerboolПрисутствует на обоих finalize-путях (true/false), отсутствует только на фронтенде
customFieldsarrayТолько фронтенд

Пример использования ​

php
<?php
switch ($modx->event->name) {
    case 'msOnCreateOrder':
        /** @var \MiniShop3\Model\msOrder $order */
        $order = $scriptProperties['msOrder'];

        // Логирование
        $modx->log(modX::LOG_LEVEL_INFO, sprintf(
            '[Order] Создан заказ #%s, сумма: %s',
            $order->get('num'),
            $order->get('cost')
        ));

        // Отправка в CRM
        // $crm->createOrder($order->toArray());

        // Уменьшение остатков на складе
        foreach ($order->getMany('Products') as $product) {
            $msProduct = $product->getOne('Product');
            if (!$msProduct) {
                continue;
            }
            $stock = (float) ($msProduct->get('stock') ?? 0);
            $msProduct->set('stock', max(0, $stock - $product->get('count')));
            $msProduct->save();
        }

        // Начисление бонусов покупателю
        if ($customer = $order->getOne('Customer')) {
            $bonus = floor($order->get('cost') / 100);
            $currentBonus = $customer->get('bonus') ?? 0;
            $customer->set('bonus', $currentBonus + $bonus);
            $customer->save();
        }
        break;
}

Интеграция с внешними сервисами ​

php
<?php
switch ($modx->event->name) {
    case 'msOnCreateOrder':
        $order = $scriptProperties['msOrder'];
        $address = $order->getOne('Address');

        // Отправка в службу доставки
        $deliveryData = [
            'order_id' => $order->get('num'),
            'recipient' => [
                'name' => $address->get('first_name') . ' ' . $address->get('last_name'),
                'phone' => $address->get('phone'),
                'email' => $address->get('email'),
            ],
            'address' => [
                'city' => $address->get('city'),
                'street' => $address->get('street'),
                'building' => $address->get('building'),
            ],
            'items' => [],
        ];

        foreach ($order->getMany('Products') as $product) {
            $deliveryData['items'][] = [
                'name' => $product->get('name'),
                'count' => $product->get('count'),
                'price' => $product->get('price'),
            ];
        }

        // $deliveryApi->createShipment($deliveryData);
        break;
}

msOnBeforeMgrCreateOrder ​

Вызывается перед финализацией заказа из админки — когда менеджер превращает черновик в полноценный заказ кнопкой «Оформить» в карточке заказа. Это аналог msOnSubmitOrder для админки.

Связка с msOnBeforeCreateOrder

После msOnBeforeMgrCreateOrder всё равно срабатывает универсальное msOnBeforeCreateOrder с теми же четырьмя параметрами (service, msOrder, origin, from_manager). Берите msOnBeforeMgrCreateOrder, если нужна логика только для админки, и msOnBeforeCreateOrder — если одна и та же логика нужна и на фронтенде, и в админке.

Параметры ​

ПараметрТипОписание
service\MiniShop3\Services\Order\OrderFinalizeServiceСервис финализации заказа
msOrdermsOrderФинализируемый заказ
originstringmanager
from_managerboolВсегда true

msOnMgrCreateOrder ​

Вызывается после успешной финализации заказа из админки. Парный к msOnBeforeMgrCreateOrder.

Параметры ​

ПараметрТипОписание
service\MiniShop3\Services\Order\OrderFinalizeServiceСервис финализации заказа
msOrdermsOrderФинализированный заказ (статус уже изменён на «Новый»)
originstringmanager
from_managerboolВсегда true