Skip to content
  1. Extras
  2. MiniShop3
  3. Development
  4. Events
  5. Order

Order events ​

Adding, validating and removing order fields, and submitting the order.

Which events can abort the operation ​

Unlike the cart events, the core reads the plugin response almost everywhere here — after-events included. $modx->event->output(...) returns an error from every event on this page except msOnMgrCreateOrder, which is called without reading the response.

So msOnRemoveFromOrder, msOnEmptyOrder and msOnCreateOrder can fail an operation that already happened: the field is removed or the order is created, yet the client gets an error.

The validation events use the response differently — not to abort the operation but to substitute the value: msOnBeforeValidateOrderValue and msOnValidateOrderValue return value, msOnErrorValidateOrderValue returns error.

msOnBeforeAddToOrder ​

Fired before adding or changing an order field.

Parameters ​

ParameterTypeDescription
controller\MiniShop3\Controllers\Order\OrderOrder controller
keystringField key
valuemixedField value

Aborting the operation ​

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

        // Disallow selecting a specific delivery method
        if ($key === 'delivery_id' && $value == 5) {
            $modx->event->output('Delivery is temporarily unavailable');
            return;
        }
        break;
}

Modifying data ​

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);
        }
        if ($key === 'email') {
            $values['value'] = strtolower(trim($value));
        }
        break;
}

msOnAddToOrder ​

Fired after adding a field to the order (after successful validation).

Parameters ​

ParameterTypeDescription
controller\MiniShop3\Controllers\Order\OrderOrder controller
keystringField key
valuemixedValidated value

Example ​

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

        $modx->log(modX::LOG_LEVEL_INFO, "[Order] Field {$key} set: {$value}");

        if ($key === 'delivery_id' && !empty($value)) {
            $controller = $scriptProperties['controller'];
            // Auto-select first payment...
        }
        break;
}

msOnBeforeValidateOrderValue ​

Fired before validating a field value.

Parameters ​

ParameterTypeDescription
controller\MiniShop3\Controllers\Order\OrderOrder controller
keystringField key
valuemixedValue to validate

Modifying data ​

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 ​

Fired after successful field validation.

Parameters ​

ParameterTypeDescription
controller\MiniShop3\Controllers\Order\OrderOrder controller
keystringField key
valuemixedValidated value

Modifying data ​

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

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

        if ($key === 'city') {
            $values['value'] = $value . ', Moscow Region';
        }
        break;
}

msOnErrorValidateOrderValue ​

Fired on validation error. Lets you change the error message or clear the error.

Parameters ​

ParameterTypeDescription
controller\MiniShop3\Controllers\Order\OrderOrder controller
keystringField key
valuemixedInvalid value
errorarrayErrors array ['field' => 'message']

Customizing error message ​

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

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

        if ($key === 'email') {
            $values['error'] = [$key => 'Enter a valid email to receive the receipt'];
        }
        if ($key === 'phone') {
            $values['error'] = [$key => 'Phone is required for courier contact'];
        }
        break;
}

Clearing the error ​

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

        if ($key === 'comment') {
            $values = &$modx->event->returnedValues;
            $values['error'] = [];
        }
        break;
}

msOnBeforeRemoveFromOrder ​

Fired before removing a field from the order.

Parameters ​

ParameterTypeDescription
controller\MiniShop3\Controllers\Order\OrderOrder controller
keystringField key to remove

Aborting the operation ​

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('This field cannot be removed');
            return;
        }
        break;
}

msOnRemoveFromOrder ​

Fired after removing a field from the order.

Parameters ​

ParameterTypeDescription
controller\MiniShop3\Controllers\Order\OrderOrder controller
keystringRemoved field key

Example ​

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

        if ($key === 'delivery_id') {
            $controller = $scriptProperties['controller'];
            // Reset payment...
        }
        break;
}

msOnBeforeEmptyOrder ​

Fired before clearing the order draft (OrderDraftManager::clean, API/controller order/clean). Abort with $modx->event->output(...).

Parameters ​

ParameterTypeDescription
draftmsOrderDraft about to be cleared

What stays after cleanup ​

Core nulls address and order fields except: address id, order_id, user_id, createdon; order 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') && /* your “already booked” flag */) {
            $modx->event->output('Cannot clear draft: delivery already reserved');
            return;
        }
        break;
}

msOnEmptyOrder ​

Fired after the draft fields are cleared. A plugin output makes the caller treat the operation as failed.

Parameters ​

ParameterTypeDescription
draftmsOrderDraft with fields already nulled

msOnSubmitOrder ​

Fired on order submission (Submit button). Lets you validate data and add extra info.

Parameters ​

ParameterTypeDescription
controller\MiniShop3\Controllers\Order\OrderOrder controller
dataarrayExtra form data

Aborting the operation ​

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

        $response = $controller->getCost();
        if ($response['success'] && $response['data']['cart_cost'] < 1000) {
            $modx->event->output('Minimum order amount is 1000');
            return;
        }

        $hour = (int)date('G');
        if ($hour < 9 || $hour > 21) {
            $modx->event->output('Orders accepted from 9:00 to 21:00');
            return;
        }
        break;
}

Modifying data ​

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

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

        if (!empty($_SESSION['utm'])) {
            $data['properties']['utm'] = $_SESSION['utm'];
            $values['data'] = $data;
        }

        $data['properties']['source'] = $_SERVER['HTTP_REFERER'] ?? 'direct';
        $values['data'] = $data;
        break;
}

msOnBeforeCreateOrder ​

Fired before final order creation (assigning "New" status). Three scenarios:

  1. Storefront submit — OrderSubmitHandler (params have no origin / from_manager; has handler, customFields)
  2. Manager finalization — OrderFinalizeService (origin=manager, from_manager=true, service)
  3. Programmatic creation — ProgrammaticOrderService → finalize (origin=integration, from_manager=false, service)

Parameters ​

ParameterTypeDescription
msOrdermsOrderOrder object (always)
handler\MiniShop3\Services\Order\OrderSubmitHandlerStorefront submit only
service\MiniShop3\Services\Order\OrderFinalizeServiceManager or integration
originstringFinalize paths only: manager | integration (OrderOrigin constants)
from_managerboolPresent on both finalize paths (true with origin=manager, false with origin=integration); missing only on the storefront submit
customFieldsarrayStorefront only — fields from properties._validated

Distinguishing scenarios

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

if ($fromManager) {
    // order card in mgr
} elseif ($origin === \MiniShop3\Services\Order\OrderOrigin::INTEGRATION) {
    // ProgrammaticOrderService / cron
} elseif (!empty($scriptProperties['handler'])) {
    // storefront
}

Aborting the operation ​

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

        // Stock check (msProductData.stock field, available via 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(
                    'Product "%s" is not available in the requested quantity',
                    $msProduct->get('pagetitle')
                ));
                return;
            }
        }
        break;
}

Modifying the order ​

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

        $properties = $order->get('properties') ?? [];
        $properties['manager_note'] = 'Order created ' . date('d.m.Y H:i');
        $order->set('properties', $properties);
        break;
}

msOnCreateOrder ​

Fired after successful order creation. Pair to msOnBeforeCreateOrder — same three scenarios and the same parameter set (storefront has no origin).

Parameters ​

ParameterTypeDescription
msOrdermsOrderCreated order (always)
handler\MiniShop3\Services\Order\OrderSubmitHandlerStorefront only
service\MiniShop3\Services\Order\OrderFinalizeServiceManager or integration
originstringFinalize paths only
from_managerboolPresent on both finalize paths (true/false); missing only on the storefront submit
customFieldsarrayStorefront only

Example ​

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

        $modx->log(modX::LOG_LEVEL_INFO, sprintf(
            '[Order] Created order #%s, amount: %s',
            $order->get('num'),
            $order->get('cost')
        ));

        // $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;
}

External service integration ​

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 ​

Fired before finalizing an order in the manager — when a manager turns a draft into a full order with the "Submit" button on the order page. This is the manager counterpart of msOnSubmitOrder.

Relation to msOnBeforeCreateOrder

After msOnBeforeMgrCreateOrder, the universal msOnBeforeCreateOrder still runs with the same three parameters. Take msOnBeforeMgrCreateOrder for manager-only logic, and msOnBeforeCreateOrder when the same logic is needed on both the frontend and the manager.

Parameters ​

ParameterTypeDescription
service\MiniShop3\Services\Order\OrderFinalizeServiceOrder finalization service
msOrdermsOrderOrder being finalized
originstringmanager
from_managerboolAlways true

msOnMgrCreateOrder ​

Fired after an order has been finalized in the manager. Pair to msOnBeforeMgrCreateOrder.

Parameters ​

ParameterTypeDescription
service\MiniShop3\Services\Order\OrderFinalizeServiceOrder finalization service
msOrdermsOrderFinalized order (status already changed to "New")
originstringmanager
from_managerboolAlways true