Skip to content
MiniShop3
MiniShop3
Современный компонент интернет-магазина для MODX 3
  1. Компоненты
  2. MiniShop3
  3. Разработка
  4. События
  5. Покупатель

События покупателя

События для управления данными покупателя: добавление полей, валидация, создание покупателя, управление адресами.

msOnBeforeGetOrderCustomer

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

Параметры

ПараметрТипОписание
controller\MiniShop3\Controllers\Order\OrderКонтроллер заказа
msCustomer`msCustomer\null`Объект покупателя (может быть null)

Отмена не блокирует оформление заказа

Привязка msCustomer к заказу опциональна — OrderSubmitHandler продолжает оформление, даже если клиент не был создан/найден (данные остаются в msOrderAddress). Отмена этого события через output() не «запрещает checkout для неавторизованных» — она лишь оставляет заказ без привязанного msCustomer.

Подмена клиента

Плагин может вернуть готовый msCustomer через returnedValues['msCustomer'] — например, для кастомного резолва по внешнему ID:

php
<?php
switch ($modx->event->name) {
    case 'msOnBeforeGetOrderCustomer':
        // На входе msCustomer обычно null — резолвим сами по внешнему признаку
        $externalId = $_SESSION['crm_customer_id'] ?? null;
        if ($externalId) {
            $found = $modx->getObject(\MiniShop3\Model\msCustomer::class, [
                'external_id' => $externalId,
            ]);
            if ($found) {
                $modx->event->returnedValues = ['msCustomer' => $found];
            }
        }
        break;
}

msOnGetOrderCustomer

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

Ошибка плагина отменяет уже найденную привязку

Хотя событие называется «после», отмена через output() здесь всё ещё имеет эффект: customer_id не будет проставлен заказу, даже если msCustomer к этому моменту уже создан/найден и, возможно, залогинен через сессию.

Параметры

ПараметрТипОписание
controller\MiniShop3\Controllers\Order\OrderКонтроллер заказа
msCustomer`msCustomer\null`Объект покупателя

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

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

        if ($customer) {
            // Обновить статистику посещений
            $visits = $customer->get('visits') ?? 0;
            $customer->set('visits', $visits + 1);
            $customer->set('last_visit', date('Y-m-d H:i:s'));
            $customer->save();
        }
        break;
}

msOnBeforeAddToCustomer

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

Параметры

ПараметрТипОписание
customer\MiniShop3\Controllers\Customer\CustomerКонтроллер покупателя
keystringКлюч поля
valuemixedЗначение поля

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

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

        // Запретить определённые домены email
        if ($key === 'email') {
            $blockedDomains = ['tempmail.com', 'throwaway.com'];
            $domain = substr($value, strpos($value, '@') + 1);

            if (in_array($domain, $blockedDomains)) {
                $modx->event->output('Временные email адреса не принимаются');
                return;
            }
        }
        break;
}

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

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

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

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

        // Капитализация имени
        if ($key === 'first_name' || $key === 'last_name') {
            $values['value'] = mb_convert_case($value, MB_CASE_TITLE, 'UTF-8');
        }
        break;
}

msOnAddToCustomer

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

Ошибка плагина возвращает клиенту error, хотя поле уже сохранено

msCustomer->save() вызывается до этого события. Если обработчик отменит его через output(), add() вернёт ошибку вызывающей стороне — но поле в БД уже сохранено. Состояние API-ответа и БД могут разойтись.

Параметры

ПараметрТипОписание
customer\MiniShop3\Controllers\Customer\CustomerКонтроллер покупателя
keystringКлюч поля
valuemixedСохранённое значение
msCustomermsCustomerОбъект покупателя
isNewboolНовый ли покупатель

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

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

        // Логирование новых покупателей
        if ($isNew) {
            $modx->log(modX::LOG_LEVEL_INFO, sprintf(
                '[Customer] Создан покупатель #%d: %s = %s',
                $customer->get('id'),
                $key,
                $value
            ));
        }
        break;
}

msOnBeforeValidateCustomerValue

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

Параметры

ПараметрТипОписание
customer\MiniShop3\Controllers\Customer\CustomerКонтроллер покупателя
keystringКлюч поля
valuemixedЗначение для валидации

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

Ошибка плагина здесь превращается в ошибку валидации самого поля:

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

        // Кастомное правило до штатной валидации
        if ($key === 'inn' && !empty($value) && !preg_match('/^\d{10,12}$/', $value)) {
            $modx->event->output('ИНН должен содержать 10 или 12 цифр');
            return;
        }
        break;
}

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

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

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

        // Нормализация email перед валидацией
        if ($key === 'email') {
            $values['value'] = strtolower(trim($value));
        }
        break;
}

msOnValidateCustomerValue

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

Может отменить уже пройденную валидацию

Несмотря на «после успешной валидации» — ошибка плагина здесь (output()) всё ещё превращается в ошибку валидации поля, точно так же, как в msOnBeforeValidateCustomerValue.

Параметры

ПараметрТипОписание
customer\MiniShop3\Controllers\Customer\CustomerКонтроллер покупателя
keystringКлюч поля
valuemixedВалидированное значение

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

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

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

        // Постобработка телефона
        if ($key === 'phone') {
            // Форматирование для отображения
            $values['value'] = preg_replace(
                '/(\d{1})(\d{3})(\d{3})(\d{2})(\d{2})/',
                '+$1 ($2) $3-$4-$5',
                preg_replace('/\D/', '', $value)
            );
        }
        break;
}

msOnErrorValidateCustomerValue

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

Параметры

ПараметрТипОписание
customer\MiniShop3\Controllers\Customer\CustomerКонтроллер покупателя
keystringКлюч поля
valuemixedНевалидное значение
errorsarrayМассив ошибок

Замена набора ошибок

Плагин может полностью заменить массив ошибок через returnedValues['errors'], либо свернуть его в одно сообщение, отменив событие через output():

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

        // Кастомизация сообщений об ошибках
        $modx->log(modX::LOG_LEVEL_WARN, sprintf(
            '[Customer] Ошибка валидации %s: %s',
            $key,
            json_encode($errors)
        ));

        // Заменить набор ошибок своим (иначе используется исходный $errors)
        if ($key === 'phone') {
            $modx->event->returnedValues = [
                'errors' => [$key => 'Укажите телефон в формате +7XXXXXXXXXX'],
            ];
        }
        break;
}

msOnBeforeCreateCustomer

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

Параметры

ПараметрТипОписание
customer\MiniShop3\Controllers\Customer\CustomerКонтроллер покупателя
customerDataarrayДанные для создания

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

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

        // Проверка на дублирование
        $existing = $modx->getObject(\MiniShop3\Model\msCustomer::class, [
            'email' => $data['email'],
        ]);

        if ($existing) {
            $modx->event->output('Покупатель с таким email уже существует');
            return;
        }
        break;
}

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

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

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

        // Добавить дополнительные поля
        $data['createdon'] = date('Y-m-d H:i:s');
        $data['source'] = $_SERVER['HTTP_REFERER'] ?? 'direct';
        $data['ip'] = $_SERVER['REMOTE_ADDR'] ?? '';

        $values['customerData'] = $data;
        break;
}

msOnCreateCustomer

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

Параметры

ПараметрТипОписание
customer\MiniShop3\Controllers\Customer\CustomerКонтроллер покупателя
customerDataarrayДанные покупателя
msCustomermsCustomerСозданный объект покупателя

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

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

        // Логирование
        $modx->log(modX::LOG_LEVEL_INFO, sprintf(
            '[Customer] Создан покупатель #%d: %s %s (%s)',
            $customer->get('id'),
            $customer->get('first_name'),
            $customer->get('last_name'),
            $customer->get('email')
        ));

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

        // Подписка на рассылку
        // $mailService->subscribe($customer->get('email'));
        break;
}

msOnBeforeAddCustomerAddress

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

Параметры

ПараметрТипОписание
addressDataarrayДанные адреса

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

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

        // Ограничение количества адресов
        $count = $modx->getCount(\MiniShop3\Model\msCustomerAddress::class, [
            'customer_id' => $data['customer_id'],
        ]);

        if ($count >= 5) {
            $modx->event->output('Максимум 5 адресов на покупателя');
            return;
        }
        break;
}

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

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

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

        // Геокодирование адреса
        $fullAddress = implode(', ', array_filter([
            $data['city'],
            $data['street'],
            $data['building'],
        ]));

        // $coordinates = $geocoder->geocode($fullAddress);
        // $data['lat'] = $coordinates['lat'];
        // $data['lng'] = $coordinates['lng'];

        $values['addressData'] = $data;
        break;
}

msOnAddCustomerAddress

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

Параметры

ПараметрТипОписание
addressDataarrayДанные адреса
msCustomerAddressmsCustomerAddressСозданный объект адреса

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

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

        $modx->log(modX::LOG_LEVEL_INFO, sprintf(
            '[Customer] Добавлен адрес #%d для покупателя #%d: %s',
            $address->get('id'),
            $address->get('customer_id'),
            $address->get('name')
        ));
        break;
}

msOnBeforeUpdateCustomer

Процессор не используется в MS3

Событие зарегистрировано в реестре (_build/elements/events.php) и корректно вызывается процессором MiniShop3\Processors\Customer\Update (наследует MODX UpdateProcessor), но на данный момент ни один встроенный admin UI или Web API MS3 не вызывает Customer/Update — ни один грид или контроллер не делает $modx->runProcessor('Customer/Update', [...]). Событие существует как точка расширения: кастомная интеграция может вызвать процессор напрямую, и оно отработает штатно.

Вызывается перед сохранением существующего покупателя — стандартный fireBeforeSaveEvent() из MODX UpdateProcessor.

Параметры

ПараметрТипОписание
modestringВсегда modSystemEvent::MODE_UPD ('upd')
dataarrayПоля msCustomer — уже НОВЫЕ значения ($object->toArray())
idintID сохраняемого покупателя
msCustomermsCustomerСсылка на объект покупателя
objectmsCustomerТа же ссылка, что и msCustomer (MS2-style алиас)

Возврат значения, а не $modx->event->output()

В отличие от событий Customer/* выше (они построены на Utils::invokeEvent() и читают output()/returnedValues), это событие — часть стандартного MODX UpdateProcessor и читает непосредственно возвращаемое значение обработчика. Верните непустую строку или непустой массив сообщений, чтобы отменить сохранение.

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

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

        // Запретить смену email на уже занятый другим покупателем
        if (!empty($data['email'])) {
            $existing = $modx->getObject(\MiniShop3\Model\msCustomer::class, [
                'email' => $data['email'],
                'id:!=' => $id,
            ]);

            if ($existing) {
                return 'Email уже используется другим покупателем';
            }
        }
        break;
}

msOnUpdateCustomer

Процессор не используется в MS3

Как и msOnBeforeUpdateCustomer — событие зарегистрировано и корректно вызывается процессором MiniShop3\Processors\Customer\Update, но пока не вызывается ни из одного встроенного UI/API MS3. Подробности — в предупреждении выше.

Вызывается после успешного сохранения покупателя (fireAfterSaveEvent()). Только уведомление — возвращаемое значение игнорируется, сохранение уже выполнено.

Параметры

ПараметрТипОписание
modestringВсегда modSystemEvent::MODE_UPD ('upd')
idintID сохранённого покупателя
msCustomermsCustomerСсылка на сохранённый объект покупателя
objectmsCustomerТа же ссылка, что и msCustomer (MS2-style алиас)

Обратите внимание: в отличие от msOnBeforeUpdateCustomer, здесь нет ключа data — снимок полей передаётся только в Before-событии.

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

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

        $modx->log(modX::LOG_LEVEL_INFO, sprintf(
            '[Customer] Покупатель #%d обновлён: %s',
            $customer->get('id'),
            $customer->get('email')
        ));
        break;
}

Полный пример: верификация покупателя

php
<?php
/**
 * Плагин: Верификация покупателя
 * События: msOnBeforeCreateCustomer, msOnCreateCustomer
 */

switch ($modx->event->name) {

    case 'msOnBeforeCreateCustomer':
        $data = $scriptProperties['customerData'];

        // Проверка email на существование
        $existing = $modx->getObject(\MiniShop3\Model\msCustomer::class, [
            'email' => $data['email'],
        ]);

        if ($existing) {
            // Обновляем токен существующего покупателя
            $existing->set('token', $data['token']);
            $existing->save();
            $modx->event->output('Покупатель найден по email');
            return;
        }

        // Проверка телефона
        if (!empty($data['phone'])) {
            $existingByPhone = $modx->getObject(\MiniShop3\Model\msCustomer::class, [
                'phone' => $data['phone'],
            ]);

            if ($existingByPhone) {
                $existingByPhone->set('token', $data['token']);
                $existingByPhone->save();
                $modx->event->output('Покупатель найден по телефону');
                return;
            }
        }

        // Добавляем метаданные
        $values = &$modx->event->returnedValues;
        $data['verified'] = false;
        $data['verification_code'] = substr(md5(uniqid()), 0, 6);
        $data['createdon'] = date('Y-m-d H:i:s');
        $values['customerData'] = $data;
        break;

    case 'msOnCreateCustomer':
        $customer = $scriptProperties['msCustomer'];
        $data = $scriptProperties['customerData'];

        // Отправка кода верификации
        if (!empty($data['verification_code']) && !empty($customer->get('email'))) {
            // Отправка email с кодом
            // $mailer->send($customer->get('email'), 'Код верификации: ' . $data['verification_code']);

            $modx->log(modX::LOG_LEVEL_INFO, sprintf(
                '[Verification] Код %s отправлен на %s',
                $data['verification_code'],
                $customer->get('email')
            ));
        }
        break;
}

msOnBeforeGetOrderUser

Вызывается перед разрешением системного пользователя MODX (modUser) для заказа. Срабатывает в OrderUserResolver при сабмите заказа, когда системная настройка ms3_order_register_user_on_submit включена.

Чем modUser отличается от msCustomer

modUser — это системный пользователь MODX (логин, пароль, профиль). msCustomer — отдельная сущность покупателя магазина (имя, телефон, токен сессии). Резолвер modUser нужен для регистрации заказчика в системе авторизации MODX, а не для управления его покупательским профилем.

Параметры

ПараметрТипОписание
resolver\MiniShop3\Services\Order\OrderUserResolverСервис разрешения пользователя
user\MODX\Revolution\modUser \nullТекущий вариант пользователя — на входе обычно null
orderDataarrayСнимок полей заказа (адресные address_email, address_phone, address_first_name и т. д.)

Подмена пользователя

Плагин может вернуть готового modUser через returnedValues['user'], чтобы обойти стандартный поиск/создание:

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

        // Кастомный поиск по внешнему ID, например из CRM
        if (!empty($orderData['address_external_id'])) {
            $found = $modx->getObject(
                \MODX\Revolution\modUser::class,
                ['username' => 'crm_' . $orderData['address_external_id']]
            );

            if ($found) {
                $modx->event->returnedValues = ['user' => $found];
            }
        }
        break;
}

msOnGetOrderUser

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

Параметры

ПараметрТипОписание
resolver\MiniShop3\Services\Order\OrderUserResolverСервис разрешения пользователя
user\MODX\Revolution\modUser \nullФинальный пользователь (или null, если разрешить не удалось)