Заказ

01 мая 2019, 07:00

Заказ

Как и в случае с корзиной, всем процессом оформления заказа занимается специальный класс msOrderHandler, который реализует интерфейс msOrderInterface. Конечно, вы можете подключить собственный класс для заказов и указать его в системной настройке ms2_order_handler_class

Итак, обязательные методы интерфейса msOrderInterface:

  • initialize - Инициализирует класс в контекст. Может загружать скрипты и стили.
  • add - Добавляет одно поле в заказ.
  • remove - Удаляет одно поле из заказа.
  • validate - Проводит проверку поля при добавлении и возвращает фильтрованное значение.
  • get - Возвращает содержимое заказа, целиком.
  • set - Перезаписывает содержимое заказа полученным массивом.
  • submit - Оформление заказа, в нём уже должна быть доставка и обязательные поля.
  • clean - Полная очистка заказа.
  • getcost - Получение стоимости доставки. Запрос передаётся в msDeliveryInterface.

Алгоритм работы

По умолчанию заказ - это массив данных в сессии. С помощью метода add() вы добавляете в него различные данные, которые должны пройти проверку через validate(). Расположение заказа в сессии даёт нам минимум два плюса: возможность оформления поэтапно и защиту от случайного обновления страницы. Что и как вы накидаете в заказ не важно, главное, чтобы к моменту вызова submit() в заказе находились обязательные поля + delivery. Обязательные поля вы устанавливаете в админке сами:

Как видно на картинке, к доставке привязываются возможные методы оплаты. Это необязательно, но стандартная форма заказа рассчитана на их использование.

Форма на сайте

На самом деле, на сайте нет никакой формы. Из-за того, что при каждом изменении поля, оно отправляется на сервер через ajax, форма просто не нужна. При отправке поля на сервер, оно проходит функцию validate(), а полученное значение возвращается обратно в форму. Таким образом, мы сразу проверяем и сохраняем данные в заказ. Пользователь получает мгновенное сохранение любых изменений и сразу видит, что он набрал не так. В стандартном классе валидация очень бережная. Она не просто говорит "да" или "нет", она пытается привести полученные данные к нужному виду, и только в редких случаях возвращает отказ. Например, вырезаются все лишние пробелы, имя покупателя пишется с заглавных букв, из телефона убираются все лишние символы и т.д. При переключении доставки и оплаты проверяется, активны ли они и соответствуют ли друг другу.

Ну и, как я уже говорил, у каждого метода доставки есть свои обязательные поля. То есть, для почты нужен индекс, а для доставки курьером - только адрес. А при самовывозе нас и вовсе, интересуют только имя покупателя и email. Зачем требовать ненужные данные? Эти правила вступают в силу сразу же, при переключении доставки. Конечно, стоимость заказа тоже пересчитывается в зависимости от её параметров.

Логика работы

Итак, все данные уже лежат в сессии, осталось только вызвать метод submit() и он сделает всю нужную работу:

  1. В этот метод можно прислать массив данных для заказа, они, конечно, должны будут пройти проверку и заменят, то что уже там есть. Это сделано для возможности использовать классические формы. Перед проверкой данных вызывается событие msOnSubmitOrder.
  2. Дальше идёт проверка наличия доставки. Если доставка не указана, или отключена в админке - ошибка.
  3. Затем класс получает обязательные поля для доставки. Проверяет, все ли они выставлены в заказе. Если нет - ошибка, юзеру на сайте обводятся красным инпуты.
  4. Дальше проверок уже не будет, всё нужное у нас есть. Получаем id юзера, по указанному email. Если такого юзера нет, то регистрируем его со случайным паролем и заносим в группы, указанные в настройке ms2_order_user_groups.
  5. Получаем состояние корзины, узнаём стоимость доставки из класса доставки (или стандартного). Создаём объект заказа msOrder.
  6. Создаём еще объекты: адрес получателя msOrderAddress, заказываемые товары msOrderProduct, пишем в них всё что можно и цепляем к заказу.
  7. Вызываем событие msOnBeforeCreateOrder, в которое передаём объект заказа и прицепленные к нему адрес и товары.
  8. Объект заказа сохраняется, вызывается событие msOnCreateOrder и переключается статус заказа на 2 ("Новый"). Отправляются письма покупателю и менеджеру, если это включено в свойствах статуса.
  9. Корзина и заказ очищаются
  10. Заключительный этап - получение объекта оплаты msPayment и запуск его класса, если указан. Дальше заказом рулит уже он, отправляя покупателя на оплату, или на другую страницу сайта.

Покупатель, конечно, этого всего не видит и не знает, а просто получает ответ от сервера, в котором указано, куда ему дальше перейти. Если в заказе не указан способ оплаты, или у него не указан особый класс, то просто перезагружается текущая страница с параметром ?msorder=idзаказа, для вывода чанка об успешной отправке заказа. При этом вывод корзины прячется, проверяется авторизация юзера в контексте и его соответствие заказу, для защиты выводимых данных.

То есть, либо класс оплаты перехватывает работу с заказом, либо покпатель видит чанк со свойствами своего заказа.

Системные события

Класс msOrderHandler генерирует определённые события при работе с заказом. Для удобства, вот они в виде заготовки-плагина:

<?php
switch ($modx->event->name) {
    // Добавление полей в заказ
    case 'msOnBeforeAddToOrder'; break; // получает $key с именем поля, $value - значение поля
    case 'msOnAddToOrder'; break; // получает $key с именем поля, $value - значение поля

    // Удаление полей из заказа
    case 'msOnBeforeRemoveFromOrder'; break; // получает $key с именем поля
    case 'msOnRemoveFromOrder'; break; // получает $key с именем поля

    // Отправка заказа
    case 'msOnSubmitOrder'; break; // необязательный массив $data с переназначаемыми полями

    // Создание заказа
    case 'msOnBeforeCreateOrder'; break; // получает готовый объект $order со всеми прицепленными объектами
    case 'msOnCreateOrder'; break; // тоже самое

    // Очистка заказа
    case 'msOnBeforeEmptyOrder'; break; // получает только объект $order
    case 'msOnEmptyOrder'; break; // получает только объект $order

    // Смена статуса заказа (оплата, отмена и т.д.)
    case 'msOnBeforeChangeOrderStatus': break; // получает объект $order и id статуса в $status
    case 'msOnChangeOrderStatus': break; // получает объект $order и id статуса в $status
}

Все события также получают объект $order с заказом.

Пример

Давайте подключим свой класс заказа и переопределим в нём проверку email.

<?php
class myOrderHandler extends msOrderHandler {
}

Это мы унаследовали оригинальный класс оформления заказа.

  • Теперь указываем новый класс myOrderHandler в системной настройке ms2_order_handler_class. Если что то пойдёт не так, то всегда можно вернуть старый класс.

После этого ms2 уже использует ваш класс для работы, а тот, в свою очередь наследует свои методы от стандартного.

  • Теперь мы можем изменить любой метод создания заказа, чем и пользуемся — меняем msOrderHandler::validate().
<?php
class myOrderHandler extends msOrderHandler {
    public function validate($key, $value) {
        switch ($key) {
            case 'email':
                // меняем filter_var() на простую регулярку
                // $value = filter_var($value, FILTER_VALIDATE_EMAIL) ? $value : @$this->order[$key];
                $value = preg_match('/.+@.+\..+/i', $value) ? trim($value) : @$this->order[$key];
            break;
            // Конечно, также можно переопределить и другие валидаторы

            // Если прислано поле, которого тут нет - отправляем в дефолтный класс
            default:
                return parent::validate($key, $value);
        }
        if ($value === false) {
            $value = '';
        }

        return $value;
    }
}

Вот и всё. Мы унаследовали метод и изменили проверку email. Точно также мы можем добавить\изменить проверку и других полей, или вообще полностью поменять весь ход оформления заказа.