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.
31 мая 2018, 15:46    Сuriosity   
4    319 +8


Комментарии ()

  1. Андрей 31 мая 2018, 16:06 # 0
    Вот тут ничего не поломалось?)
    $value = preg_match('/^[^@Р°-СЏРђ-РЇ]+@[^@Р°-СЏРђ-РЇ]+(?<!\.)\.[^\.Р°-СЏРђ-РЇ]{2,}$/m', $value)
    1. Сuriosity 31 мая 2018, 16:13 # +1
      Нет, не поломалось! Очень даже рабочий вариант. Правда с регулярным выражением пришлось повозиться…
      Дело в том, что в эту регулярку нельзя прямо вставить нужные символы, приходится прописывать краказябры, как будто я написал символы в utf-8, а вижу как windows-1251.
      1. Андрей 31 мая 2018, 16:15 # 0
        Вот оно в чём дело, спасибо! =)
      2. Сuriosity 31 мая 2018, 16:18 # 0
        Более того, немного расширил проверку на апостроф.
        Т.к. апострофы народ пишет кому как удобно…
        ’, ʼ, ', ` — все это разные символы.
        Но теперь, какой бы из этих вариантов пользователь не ввел, автоматом заменит на '.
        1. Андрей 31 мая 2018, 17:31 # 0
          Будет лучше кстати перенести статью из Вопросы в Готовые решения.
          1. Сuriosity 31 мая 2018, 17:38 # 0
            Только-что перенес.
            Хотя, насколько я помню, изначально эту статью не мог занести в раздел «Готовые решения», потому как не хватало рейтинга.
            А если это так, то только-что обнаружился баг.
            Если вначале создать статью в разделе «Вопросы», то после редактирования можно, не смотря на рейтинг, перенести в раздел «Готовые решения».
            (:
            1. Баха Волков 31 мая 2018, 17:51 # 0
              А ещё, после переноса хлебные крошки составляются так как будто тикет остался в разделе «Вопросы» :)
              1. Сuriosity 31 мая 2018, 17:52 # 0
                да да (:
              2. Баха Волков 31 мая 2018, 17:54 # 0
                Или это все проделки кэша?!
                1. Сuriosity 31 мая 2018, 17:58 # 0
                  Вполне возможно или, даже, скорей всего.
                2. Сuriosity 31 мая 2018, 18:00 # 0
                  Насчет рейтинга и переноса…
                  Это не баг… Оказывается мой рейтинг поднялся, сразу после первого голоса за эту статью.
          2. Александр 31 мая 2018, 20:57 # +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;
                }
            1. Сuriosity 31 мая 2018, 21:20 # +1
              Александр, спасибо за внимательность. Я действительно забыл заменить концовку из мануальной на оригинальную:
                      $response = $this->ms2->invokeEvent('msOnValidateOrderValue', array(
                          'key' => $key,
                          'value' => $value,
                      ));
                      $value = $response['data']['value'];
              
                      return $value;
              
              P.S.
              Код поправил.
            Вы должны авторизоваться, чтобы оставлять комментарии.