Заказ
Как и в случае с корзиной, всем процессом оформления заказа занимается специальный класс 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()
и он сделает всю нужную работу:
- В этот метод можно прислать массив данных для заказа, они, конечно, должны будут пройти проверку и заменят, то что уже там есть. Это сделано для возможности использовать классические формы. Перед проверкой данных вызывается событие msOnSubmitOrder.
- Дальше идёт проверка наличия доставки. Если доставка не указана, или отключена в админке - ошибка.
- Затем класс получает обязательные поля для доставки. Проверяет, все ли они выставлены в заказе. Если нет - ошибка, юзеру на сайте обводятся красным инпуты.
- Дальше проверок уже не будет, всё нужное у нас есть. Получаем id юзера, по указанному email или номеру телефона. Если такого юзера нет, то регистрируем его со случайным паролем и заносим в группы, указанные в настройке ms2_order_user_groups.
- Получаем состояние корзины, узнаём стоимость доставки из класса доставки (или стандартного). Создаём объект заказа
msOrder
. - Создаём еще объекты: адрес получателя
msOrderAddress
, заказываемые товарыmsOrderProduct
, пишем в них всё что можно и цепляем к заказу. - Вызываем событие msOnBeforeCreateOrder, в которое передаём объект заказа и прицепленные к нему адрес и товары.
- Объект заказа сохраняется, вызывается событие msOnCreateOrder и переключается статус заказа на 2 ("Новый"). Отправляются письма покупателю и менеджеру, если это включено в свойствах статуса.
- Корзина и заказ очищаются
- Заключительный этап - получение объекта оплаты 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. Точно также мы можем добавить\изменить проверку и других полей, или вообще полностью поменять весь ход оформления заказа.