miniShop2 удаляет апостроф, украинские и белорусские буквы

В форме заказа в поле Получатель (receiver) не проходят валидацию такие буквы, как: "ґ", "є", "і", "ї", "ў", а также знак апострофа.
Сейчас в этом поле нельзя написать: O'Brian, Дієго Веласкес,…

Учитывая, что на сегодняшний день, относительно этого вопроса, информации крайне мало, а в мануалах этот вопрос затронут достаточно обобщенно, постараюсь подробно описать решение этой проблемы.


Если вы опытный разработчик на MODX и с miniShop2 на ТЫ, переходите сразу к 2. Валидация поля «receiver»

По умолчанию в miniShop2 за логику и обработку оформления заказа отвечает класс с именем msOrderHandler (файл расположен: core/components/minishop2/model/minishop2/msorderhandler.class.php). А за валидацию полей формы отвечает его метод validate().
Некоторые кулибины лезут прямо в этот файл и делают свои правки, но с первым же обновлением miniShop2 они, скорей всего, начинают больше думать…

____________1. Подключение кастомного обработчика

Итак, для того, чтоб внести свою логику и обработку в оформление заказа, нужно создать и расширить свой класс. Проще говоря, нужно создать свой класс-обработчик, который должен унаследовать класс msOrderHandler.

Для начала надо придумать название нашего класса, например myOrderHandler.
Далее надо создать php файл, в котором будет находиться наш класс-обработчик.
Например, создаем в директории core/components/minishop2/custom/order/ файл myorderhandler.class.php

В нем, пока, просто пишем имя нашего класса (myOrderHandler) и унаследываем дефолтный класс (msOrderHandler):
<?php
class myOrderHandler extends msOrderHandler {
}

Теперь нам надо сообщить miniShop2 о том, что хотим использовать свой обработчик заказов (класс).
Это делается в два этапа.
____1-й этап
Чтоб подключить сервис, можно использовать два основных варианта:
________Вариант 1. Создать свой сниппет, например myAddService
в нем написать:
<?php
if ($miniShop2 = $modx->getService('miniShop2')) {
    $miniShop2->addService('order', 'myOrderHandler',
        '{core_path}components/minishop2/custom/order/myorderhandler.class.php'
    );
}
далее на любой странице один раз вызвать наш сниппет [[!myAddService]], после чего можно смело удалять этот сниппет, так как подключение сервиса уже произошло.

________Вариант 2. Установить замечательный компонент Console (поставщик modx.com), после чего один раз вызвать вышеупомянутый код:
<?php
if ($miniShop2 = $modx->getService('miniShop2')) {
    $miniShop2->addService('order', 'myOrderHandler',
        '{core_path}components/minishop2/custom/order/myorderhandler.class.php'
    );
}

____2-й этап
Сервис подключили, теперь необходимо указать новый класс myOrderHandler в системной настройке ms2_order_handler_class. И как написано в мануалах:
Если что то пойдёт не так, то всегда можно вернуть старый класс.

Все, теперь miniShop2 знает, что мы хотим использовать свой обработчик заказов (класс). А учитывая, что наш класс унаследовал все свойства и методы дефолтного класса-обработчика msOrderHandler, то вся логика и обработка заказов магазина осталась нетронутой. Что ж, пора это дело поправить под наши нужды.

____________2. Валидация поля «receiver»

Вносим правки в наш файл myorderhandler.class.php
<?php
class myOrderHandler extends msOrderHandler {
    public function validate($key, $value) {
        //$value = trim(strip_tags($value)); //Можно смело раскомментировать
        if ($key != 'comment') {
            $value = preg_replace('/\s+/', ' ', trim($value));
        }

        $response = $this->ms2->invokeEvent('msOnBeforeValidateOrderValue', array(
            'key' => $key,
            'value' => $value,
        ));
        $value = $response['data']['value'];

        $old_value = isset($this->order[$key]) ? $this->order[$key] : '';
        switch ($key) {
            case 'receiver':
                // Transforms string from "nikolaj -  coster--Waldau jr." to "Nikolaj Coster-Waldau Jr."
                $tmp = preg_replace(
                    array('/[^-a-zа-яёЁіїєґў’ʼ`\s\.\']/iu', '/\s+/', '/\-+/', '/\.+/', '/(\'|’|ʼ|`)+/'),
                    array('', ' ', '-', '.', "'"),
                    $value
                );
                $tmp = preg_split('/\s/', $tmp, -1, PREG_SPLIT_NO_EMPTY);
                $tmp = array_map(array($this, 'ucfirst'), $tmp);
                $value = preg_replace('/\s+/', ' ', implode(' ', $tmp));
                if (empty($value)) {
                    $value = false;
                }
                break;
				// Конечно, также можно переопределить и другие валидаторы

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

        $response = $this->ms2->invokeEvent('msOnValidateOrderValue', array(
            'key' => $key,
            'value' => $value,
        ));
        $value = $response['data']['value'];

        return $value;
    }
}

Вот и все, теперь при заполнении поля «Получатель», срабатывает наш валидатор, который пропускает апостроф, украинские и белорусские буквы.
Кроме того, надеюсь, новичкам станет более ясен процесс кастомизации сервисов miniShop2.
Сuriosity
31 мая 2018, 12:46
4
401
+8

Комментарии: 13

Андрей
31 мая 2018, 16:06
0
Вот тут ничего не поломалось?)
$value = preg_match('/^[^@Р°-СЏРђ-РЇ]+@[^@Р°-СЏРђ-РЇ]+(?<!\.)\.[^\.Р°-СЏРђ-РЇ]{2,}$/m', $value)
    Сuriosity
    31 мая 2018, 16:13
    +1
    Нет, не поломалось! Очень даже рабочий вариант. Правда с регулярным выражением пришлось повозиться…
    Дело в том, что в эту регулярку нельзя прямо вставить нужные символы, приходится прописывать краказябры, как будто я написал символы в utf-8, а вижу как windows-1251.
      Андрей
      31 мая 2018, 16:15
      0
      Вот оно в чём дело, спасибо! =)
    Сuriosity
    31 мая 2018, 16:18
    0
    Более того, немного расширил проверку на апостроф.
    Т.к. апострофы народ пишет кому как удобно…
    ’, ʼ, ', ` — все это разные символы.
    Но теперь, какой бы из этих вариантов пользователь не ввел, автоматом заменит на '.
      Андрей
      31 мая 2018, 17:31
      0
      Будет лучше кстати перенести статью из Вопросы в Готовые решения.
        Сuriosity
        31 мая 2018, 17:38
        0
        Только-что перенес.
        Хотя, насколько я помню, изначально эту статью не мог занести в раздел «Готовые решения», потому как не хватало рейтинга.
        А если это так, то только-что обнаружился баг.
        Если вначале создать статью в разделе «Вопросы», то после редактирования можно, не смотря на рейтинг, перенести в раздел «Готовые решения».
        (:
          Баха Волков
          31 мая 2018, 17:51
          0
          А ещё, после переноса хлебные крошки составляются так как будто тикет остался в разделе «Вопросы» :)
          Баха Волков
          31 мая 2018, 17:54
          0
          Или это все проделки кэша?!
            Сuriosity
            31 мая 2018, 17:58
            0
            Вполне возможно или, даже, скорей всего.
          Сuriosity
          31 мая 2018, 18:00
          0
          Насчет рейтинга и переноса…
          Это не баг… Оказывается мой рейтинг поднялся, сразу после первого голоса за эту статью.
Александр
31 мая 2018, 20:57
1
+1
Спасибо, хорошее решение! Только таким способом вы отрезаете 2 события: msOnBeforeValidateOrderValue и msOnValidateOrderValue, и вызываться, насколько я понимаю, будут эти события у родителя, то есть валидация будет происходить по стандарту в miniShop2. Конечно, это только если использовать это в плагинах, но тем не менее, почему не сделать так?
public function validate($key, $value)
    {
        if ($key != 'comment') {
            $value = preg_replace('/\s+/', ' ', trim($value));
        }

        $response = $this->ms2->invokeEvent('msOnBeforeValidateOrderValue', array(
            'key' => $key,
            'value' => $value,
        ));
        $value = $response['data']['value'];

        switch ($key) {
            case 'receiver':
                // Transforms string from "nikolaj -  coster--Waldau jr." to "Nikolaj Coster-Waldau Jr."
                $tmp = preg_replace(
                    array('/[^-a-zа-яёЁіїєґў’ʼ`\s\.\']/iu', '/\s+/', '/\-+/', '/\.+/', '/(\'|’|ʼ|`)+/'),
                    array('', ' ', '-', '.', "'"),
                    $value
                );
                $tmp = preg_split('/\s/', $tmp, -1, PREG_SPLIT_NO_EMPTY);
                $tmp = array_map(array($this, 'ucfirst'), $tmp);
                $value = preg_replace('/\s+/', ' ', implode(' ', $tmp));
                if (empty($value)) {
                    $value = false;
                }
                break;
            default:
                return parent::validate($key, $value);
        }

        $response = $this->ms2->invokeEvent('msOnValidateOrderValue', array(
            'key' => $key,
            'value' => $value,
        ));
        $value = $response['data']['value'];

        return $value;
    }
    Сuriosity
    31 мая 2018, 21:20
    +1
    Александр, спасибо за внимательность. Я действительно забыл заменить концовку из мануальной на оригинальную:
    $response = $this->ms2->invokeEvent('msOnValidateOrderValue', array(
                'key' => $key,
                'value' => $value,
            ));
            $value = $response['data']['value'];
    
            return $value;
    P.S.
    Код поправил.