К содержанию

Кастомизация minishop'a (номер телефона вместо емейла у пользователя при совершении заказа)

Доброго дня!
Стоит задача изменить исходник файла живущего по адресу:
/core/components/minishop2/model/minishop2/minishop2.class.php

По сути, что может быть проще, поменять код и дело с концом, но при первом же обновлении, все это канет в лету, а это не корпоративно!

Как можно подцепить свой файл?

upd

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

И так, задача в том, когда пользователь на сайте делает заказ, автоматически регистрируется новый пользователь с username = email

Уж не знаю, как у вас, а у нас в 2026 год — прям целая проблема с почтой, ни у кого нет, и порой это становится целой проблемой.

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

И уже по обычаю, сперва на тестовом сайте правлю исходники, убеждаюсь, что все работает, и потом уже делаю «красиво» подключаю кастомный файл со своими изменениями и все работает щикарно, но не в этот раз.

Логика работы живет потипо в ядре и подрубить вместо него другой файл, как мне объяснили уже нельзя.

Поэтому действовать нужно через расширение класса Handlers.

И так, погнали!

создаю свой класс
core/components/minishop2/custom/order/my_orderhandler.class.php

<?php
if (!class_exists('msOrderHandler')) {
    require_once MODX_CORE_PATH . 'components/minishop2/handlers/msorderhandler.class.php';
}

class my_OrderHandler extends msOrderHandler {
  
 public function submit($data = [])
    {
        $response = $this->ms2->invokeEvent('msOnSubmitOrder', [
            'data' => $data,
            'order' => $this,
        ]);
        if (!$response['success']) {
            return $this->error($response['message']);
        }
        if (!empty($response['data']['data'])) {
            $this->set($response['data']['data']);
        }

        $response = $this->getDeliveryRequiresFields();
        if ($this->ms2->config['json_response']) {
            $response = json_decode($response, true);
        }
        if (!$response['success']) {
            return $this->error($response['message']);
        }
        $requires = $response['data']['requires'];

        $errors = [];
        foreach ($requires as $v) {
            if (!empty($v) && empty($this->order[$v])) {
                $errors[] = $v;
            }
        }
        if (!empty($errors)) {
            return $this->error('ms2_order_err_requires', $errors);
        }

        //$user_id = $this->ms2->getCustomerId();
        $user_id = $this->getReceiverId();
        if (empty($user_id) || !is_int($user_id)) {
            return $this->error(is_string($user_id) ? $user_id : 'ms2_err_user_nf');
        }

        $cart_status = $this->ms2->cart->status();
        if (empty($cart_status['total_count'])) {
            return $this->error('ms2_order_err_empty');
        }

        $delivery_cost = $this->getCost(false, true);
        $cart_cost = $this->getCost(true, true) - $delivery_cost;
        $num = $this->getNewOrderNum();

        /** @var msOrder $msOrder */
        $msOrder = $this->storageHandler->getForSubmit(
            compact('user_id', 'num', 'cart_cost', 'cart_status', 'delivery_cost')
        );

        $response = $this->ms2->invokeEvent('msOnBeforeCreateOrder', [
            'msOrder' => $msOrder,
            'order' => $this,
        ]);
        if (!$response['success']) {
            return $this->error($response['message']);
        }

        if ($msOrder->save()) {
            $response = $this->ms2->invokeEvent('msOnCreateOrder', [
                'msOrder' => $msOrder,
                'order' => $this,
            ]);
            if (!$response['success']) {
                return $this->error($response['message']);
            }

            if ($this->storage === 'session') {
                $this->ms2->cart->clean();
                $this->clean();
            }
            if (empty($_SESSION['minishop2']['orders'])) {
                $_SESSION['minishop2']['orders'] = [];
            }
            $_SESSION['minishop2']['orders'][] = $msOrder->get('id');

            // Trying to set status "new"
            $status_new = $this->modx->getOption('ms2_status_new', null, 1);
            $response = $this->ms2->changeOrderStatus($msOrder->get('id'), $status_new);
            if ($response !== true) {
                return $this->error($response, ['msorder' => $msOrder->get('id')]);
            }

            // Reload order object after changes in changeOrderStatus method
            /** @var msOrder $msOrder */
            $msOrder = $this->modx->getObject('msOrder', ['id' => $msOrder->get('id')]);

            /** @var msPayment $payment */
            $payment = $this->modx->getObject(
                'msPayment',
                ['id' => $msOrder->get('payment'), 'active' => 1]
            );
            if ($payment) {
                $response = $payment->send($msOrder);
                if ($this->config['json_response']) {
                    @session_write_close();
                    echo is_array($response) ? json_encode($response) : $response;
                    die();
                }
                if (!empty($response['data']['redirect'])) {
                    $this->modx->sendRedirect($response['data']['redirect']);
                }
                if (!empty($response['data']['msorder'])) {
                    $redirect = $this->modx->context->makeUrl(
                        $this->modx->resource->id,
                        ['msorder' => $response['data']['msorder']]
                    );
                    $this->modx->sendRedirect($redirect);
                }
                $this->modx->sendRedirect($this->modx->context->makeUrl($this->modx->resource->id));
            } else {
                if ($this->config['json_response']) {
                    return $this->success('', ['msorder' => $msOrder->get('id')]);
                }
                $redirect = $this->modx->context->makeUrl(
                    $this->modx->resource->id,
                    ['msorder' => $msOrder->get('id')]
                );
                $this->modx->sendRedirect($redirect);
            }
            return $this->success();
        }

        return $this->error();
    } 
  
  
  
  
  
  
  //=============================
  
  
  
  
    public function getReceiverId() {
        error_log("=== getReceiverId: НАЧАЛО ===");
        
        $customer = null;

        // Вместо $this->invokeEvent используем $this->ms2->invokeEvent
        // Вместо $this->order передаем $this (сам обработчик)
        $response = $this->ms2->invokeEvent('msOnBeforeGetOrderCustomer', [
            'order' => $this,  // передаем себя, а не $this->order
            'customer' => $customer,
        ]);
        
        if (!$response['success']) {
            return $response['message'];
        }

        if (!$customer) {
            // Вместо $this->order->get() используем $this->get()
            $data = $this->get();
            
            $email = $data['email'] ?? '';
            $receiver = $data['receiver'] ?? '';
            $phone = $data['phone'] ?? '';
            
            // Функция для приведения телефона к международному формату
            $formatPhone = function($phone) {
                if (empty($phone)) return '';
                
                $phone = preg_replace('/[^0-9]/', '', $phone);
                $defaultCountryCode = '7';
                
                if (substr($phone, 0, 1) == '8') {
                    $phone = $defaultCountryCode . substr($phone, 1);
                }
                elseif (strlen($phone) == 10) {
                    $phone = $defaultCountryCode . $phone;
                }
                elseif (substr($phone, 0, 1) == '7' && strlen($phone) == 11) {
                    // Оставляем как есть
                }
                elseif (strlen($phone) > 11 || (strlen($phone) == 11 && substr($phone, 0, 1) != '7')) {
                    // Оставляем как есть
                }
                
                return '+' . $phone;
            };
            
            $formattedPhone = $formatPhone($phone);
            
            if ($formattedPhone !== $phone) {
                $data['phone'] = $formattedPhone;
                // Вместо $this->order->set() используем $this->set()
                $this->set($data);
                $phone = $formattedPhone;
            }
            
            if (empty($receiver)) {
                if (!empty($phone)) {
                    $receiver = preg_replace('/[^0-9]/', '', $phone);
                } elseif (!empty($email)) {
                    $receiver = substr($email, 0, strpos($email, '@'));
                } else {
                    $receiver = uniqid('user_', false);
                }
            }
            
            if (empty($email)) {
                if (!empty($phone)) {
                    $cleanPhone = preg_replace('/[^0-9]/', '', $phone);
                    $email = 'user_' . $cleanPhone . '@' . $this->modx->getOption('http_host');
                } else {
                    $email = $receiver . '@' . $this->modx->getOption('http_host');
                }
            }

            if ($this->modx->user->isAuthenticated()) {
                $profile = $this->modx->user->Profile;
                
                if (!$profile->get('email')) {
                    $profile->set('email', $email);
                }
                
                if (!empty($phone) && $profile->get('mobilephone') != $phone) {
                    $profile->set('mobilephone', $phone);
                }
                
                if (!empty($receiver) && $profile->get('fullname') != $receiver) {
                    $profile->set('fullname', $receiver);
                }
                
                $profile->save();
                $customer = $this->modx->user;
                
            } else {
                $c = $this->modx->newQuery('modUser');
                $c->leftJoin('modUserProfile', 'Profile');
                
                $filter = [];
                
                if (!empty($phone)) {
                    $filter['modUser.username'] = $phone;
                    $filter['OR:Profile.mobilephone:='] = $phone;
                }
                
                if (!empty($email)) {
                    $filter['OR:Profile.email:='] = $email;
                }
                
                $c->where($filter);
                $c->select('modUser.id');
                
                if (!$customer = $this->modx->getObject('modUser', $c)) {
                    $userData = [
                        'username' => !empty($phone) ? $phone : $email,
                        'password' => md5(rand()),
                        'active' => 1
                    ];
                    
                    $customer = $this->modx->newObject('modUser', $userData);
                    
                    $profileData = [
                        'fullname' => $receiver,
                    ];
                    
                    if (!empty($email)) {
                        $profileData['email'] = $email;
                    }
                    
                    if (!empty($phone)) {
                        $profileData['mobilephone'] = $phone;
                    }
                    
                    $profile = $this->modx->newObject('modUserProfile', $profileData);
                    $customer->addOne($profile);
                    
                    $setting = $this->modx->newObject('modUserSetting');
                    $setting->fromArray([
                        'key' => 'cultureKey',
                        'value' => $this->modx->getOption('cultureKey', null, 'en', true),
                    ], '', true);
                    $customer->addMany($setting);
                    
                    if (!$customer->save()) {
                        $customer = null;
                    } elseif ($groups = $this->modx->getOption('ms2_order_user_groups', null, false)) {
                        $groupRoles = array_map('trim', explode(',', $groups));
                        foreach ($groupRoles as $groupRole) {
                            $groupRole = explode(':', $groupRole);
                            $roleId = null;
                            if (count($groupRole) > 1 && !empty($groupRole[1])) {
                                $roleId = is_numeric($groupRole[1]) ? (int)$groupRole[1] : $groupRole[1];
                            }
                            $customer->joinGroup($groupRole[0], $roleId);
                        }
                    }
                } else {
                    $profile = $customer->getOne('Profile');
                    if ($profile) {
                        $changed = false;
                        
                        if (!empty($phone) && $profile->get('mobilephone') != $phone) {
                            $profile->set('mobilephone', $phone);
                            $changed = true;
                        }
                        
                        if (!empty($email) && $profile->get('email') != $email) {
                            $profile->set('email', $email);
                            $changed = true;
                        }
                        
                        if (!empty($receiver) && $profile->get('fullname') != $receiver) {
                            $profile->set('fullname', $receiver);
                            $changed = true;
                        }
                        
                        if ($changed) {
                            $profile->save();
                        }
                    }
                }
            }
        }

        // Вместо $this->invokeEvent используем $this->ms2->invokeEvent
        $response = $this->ms2->invokeEvent('msOnGetOrderCustomer', [
            'order' => $this,  // передаем себя
            'customer' => $customer,
        ]);
        
        if (!$response['success']) {
            return $response['message'];
        }

        $result = $customer instanceof modUser ? $customer->get('id') : 0;
        error_log("=== getReceiverId: РЕЗУЛЬТАТ = $result ===");
        
        return $result;
    }
}
По сути здесь полностью скопированный метод submit из оригинального файла (core/components/minishop2/handlers/msorderhandler.class.php). Изменения минимальны:
вот это
$user_id = $this->ms2->getCustomerId();
меняем на это
$user_id = $this->getReceiverId();


и второй метод public function getCustomerId(), он так же скопирован из недр оригинального файла (core/components/minishop2/model/minishop2/minishop2.class.php)

тут уже посложнее.

Во первых, нужно сменить имя метода. В оригинале он getCustomerId, у меня стал getReceiverId

теперь сам код… как я понял, но это не точно, раньше этот метод жил в ядре, а теперь он переехал в класс, и доступ к объектам(?) поменялся.
Например. Если в оригинальном файле
$response = $this->invokeEvent('msOnBeforeGetOrderCustomer', [
            'order' => $this->order,
            'customer' => $customer,
        ]);
то в классе Handlers должно выглядеть вот так:
$response = $this->ms2->invokeEvent('msOnBeforeGetOrderCustomer', [//вместо $this->
    'order' => $this,//Вместо $this->order,
    'customer' => $customer,
  ]);
а вот собственно оригинал сообщения, который мне помог с этим (Тодор)

public function getReceiverId() {
error_log("🔥🔥🔥 getCustomerId() НАЧАЛО 🔥🔥🔥");
$customer = null;
$response = $this->ms2->invokeEvent('msOnBeforeGetOrderCustomer', [//вместо $this->
'order' => $this,//Вместо $this->order,
'customer' => $customer,
]);
if (!$response['success']) {
return $response['message'];
}

if (!$customer) {
$data = $this->get();//Вместо $this->order->get();

…
$this->set($data);//Вмесо $this->order->set($data);
…
}

$response = $this->ms2->invokeEvent('msOnGetOrderCustomer', [//Вместо $this->
'order' => $this, // Вместо $this->order,
'customer' => $customer,
]);
if (!$response['success']) {
return $response['message'];
}

return $customer instanceof modUser? $customer->get('id'): 0;
}
возможно что-то упустил
Если я ни где не напутал ни в чем, то по части кода все готово.

теперь нужно подключить нашу службу.

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

идем в системные настройки, и находим там по ключам ms2_services и ms2_order_handler_class две настройки.

в моем случае значение ms2_services
прописываем вот так:
{
"cart":[],
"order":{"my_OrderHandler":"{core_path}components\/minishop2\/custom\/order\/my_orderhandler.class.php"},
"payment":[],
"delivery":{"mydelivery":"{core_path}components\/minishop2\/custom\/delivery\/my_msdeliveryhandler.class.php"}
}
а ms2_order_handler_class даем вот это значение:
my_OrderHandler
т.е. название нашего кастомного класса.

собственно все, после этого, при регистрации пользователя вместо емейла в юзернэйм будет записываться телефон.
Дмитрий
Дмитрий
18 февраля 2026, 12:05
modx.pro
1 383
Реклама

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

Артур Шевченко
18 февраля 2026, 14:14
Никак. Если расскажешь задачу, возможно, подскажут другое решение.
    Дмитрий
    Дмитрий Старожил
    18 февраля 2026, 18:18
    хм… хочу что бы при формировании заказа минишоп генерировал username не email пользователя, а номер телефона
      Артур Шевченко
      18 февраля 2026, 21:22
      Версия минишопа какая?
        Дмитрий
        Дмитрий Старожил
        18 февраля 2026, 21:55
        minishop2-4.4.2-pl
      Дмитрий
      Дмитрий Старожил
      18 февраля 2026, 18:50
      Во, вот так хочу, что бы было

      public function getCustomerId() {
              $customer = null;
      
              $response = $this->invokeEvent('msOnBeforeGetOrderCustomer', [
                  'order' => $this->order,
                  'customer' => $customer,
              ]);
              if (!$response['success']) {
                  return $response['message'];
              }
      
              if (!$customer) {
                  $data = $this->order->get();
                  $email = $data['email'] ?? '';
                  $receiver = $data['receiver'] ?? '';
                  $phone = $data['phone'] ?? '';
                  
                  // Функция для приведения телефона к международному формату +КодСтраныНомер
                  $formatPhone = function($phone) {
                      if (empty($phone)) return '';
                      
                      // Удаляем все нецифровые символы
                      $phone = preg_replace('/[^0-9]/', '', $phone);
                      
                      // Определяем код страны по умолчанию (Россия)
                      $defaultCountryCode = '7';
                      
                      // Если номер начинается с 8 (Россия)
                      if (substr($phone, 0, 1) == '8') {
                          $phone = $defaultCountryCode . substr($phone, 1);
                      }
                      // Если номер без кода (10 цифр) - добавляем код по умолчанию
                      elseif (strlen($phone) == 10) {
                          $phone = $defaultCountryCode . $phone;
                      }
                      // Если номер начинается с 7 (уже есть код России)
                      elseif (substr($phone, 0, 1) == '7' && strlen($phone) == 11) {
                          // Оставляем как есть
                      }
                      // Если номер с другим кодом страны
                      elseif (strlen($phone) > 11 || (strlen($phone) == 11 && substr($phone, 0, 1) != '7')) {
                          // Оставляем как есть (уже содержит код страны)
                      }
                      
                      // Добавляем знак + в начало
                      return '+' . $phone;
                  };
                  
                  // Приводим телефон к единому формату
                  $formattedPhone = $formatPhone($phone);
                  
                  // Обновляем телефон в данных заказа
                  if ($formattedPhone !== $phone) {
                      $data['phone'] = $formattedPhone;
                      $this->order->set($data);
                      $phone = $formattedPhone;
                  }
                  
                  // Формируем receiver, если он пустой
                  if (empty($receiver)) {
                      if (!empty($phone)) {
                          $receiver = preg_replace('/[^0-9]/', '', $phone);
                      } elseif (!empty($email)) {
                          $receiver = substr($email, 0, strpos($email, '@'));
                      } else {
                          $receiver = uniqid('user_', false);
                      }
                  }
                  
                  // Формируем email, если он пустой
                  if (empty($email)) {
                      if (!empty($phone)) {
                          $cleanPhone = preg_replace('/[^0-9]/', '', $phone);
                          $email = 'user_' . $cleanPhone . '@' . $this->modx->getOption('http_host');
                      } else {
                          $email = $receiver . '@' . $this->modx->getOption('http_host');
                      }
                  }
      
                  // Если пользователь авторизован
                  if ($this->modx->user->isAuthenticated()) {
                      $profile = $this->modx->user->Profile;
                      
                      if (!$profile->get('email')) {
                          $profile->set('email', $email);
                      }
                      
                      if (!empty($phone) && $profile->get('mobilephone') != $phone) {
                          $profile->set('mobilephone', $phone);
                      }
                      
                      if (!empty($receiver) && $profile->get('fullname') != $receiver) {
                          $profile->set('fullname', $receiver);
                      }
                      
                      $profile->save();
                      $customer = $this->modx->user;
                      
                  } else {
                      // Поиск существующего пользователя
                      $c = $this->modx->newQuery('modUser');
                      $c->leftJoin('modUserProfile', 'Profile');
                      
                      $filter = [];
                      
                      if (!empty($phone)) {
                          $filter['modUser.username'] = $phone;
                          $filter['OR:Profile.mobilephone:='] = $phone;
                      }
                      
                      if (!empty($email)) {
                          $filter['OR:Profile.email:='] = $email;
                      }
                      
                      $c->where($filter);
                      $c->select('modUser.id');
                      
                      // Пытаемся найти пользователя
                      if (!$customer = $this->modx->getObject('modUser', $c)) {
                          // Создаем нового пользователя
                          $userData = [
                              'username' => !empty($phone) ? $phone : $email,
                              'password' => md5(rand()),
                              'active' => 1
                          ];
                          
                          $customer = $this->modx->newObject('modUser', $userData);
                          
                          $profileData = [
                              'fullname' => $receiver,
                          ];
                          
                          if (!empty($email)) {
                              $profileData['email'] = $email;
                          }
                          
                          if (!empty($phone)) {
                              $profileData['mobilephone'] = $phone;
                          }
                          
                          $profile = $this->modx->newObject('modUserProfile', $profileData);
                          $customer->addOne($profile);
                          
                          // Добавляем настройку языка
                          $setting = $this->modx->newObject('modUserSetting');
                          $setting->fromArray([
                              'key' => 'cultureKey',
                              'value' => $this->modx->getOption('cultureKey', null, 'en', true),
                          ], '', true);
                          $customer->addMany($setting);
                          
                          if (!$customer->save()) {
                              $customer = null;
                          } elseif ($groups = $this->modx->getOption('ms2_order_user_groups', null, false)) {
                              $groupRoles = array_map('trim', explode(',', $groups));
                              foreach ($groupRoles as $groupRole) {
                                  $groupRole = explode(':', $groupRole);
                                  $roleId = null;
                                  if (count($groupRole) > 1 && !empty($groupRole[1])) {
                                      $roleId = is_numeric($groupRole[1]) ? (int)$groupRole[1] : $groupRole[1];
                                  }
                                  $customer->joinGroup($groupRole[0], $roleId);
                              }
                          }
                      } else {
                          // Пользователь найден - обновляем данные
                          $profile = $customer->getOne('Profile');
                          if ($profile) {
                              $changed = false;
                              
                              if (!empty($phone) && $profile->get('mobilephone') != $phone) {
                                  $profile->set('mobilephone', $phone);
                                  $changed = true;
                              }
                              
                              if (!empty($email) && $profile->get('email') != $email) {
                                  $profile->set('email', $email);
                                  $changed = true;
                              }
                              
                              if (!empty($receiver) && $profile->get('fullname') != $receiver) {
                                  $profile->set('fullname', $receiver);
                                  $changed = true;
                              }
                              
                              if ($changed) {
                                  $profile->save();
                              }
                          }
                      }
                  }
              }
      
              $response = $this->invokeEvent('msOnGetOrderCustomer', [
                  'order' => $this->order,
                  'customer' => $customer,
              ]);
              if (!$response['success']) {
                  return $response['message'];
              }
      
              return $customer instanceof modUser ? $customer->get('id') : 0;
          }
        Тодор
        Тодор Старожил
        19 февраля 2026, 19:45
        • Расширяешь класс msOrderHandler (поключение)
        • Копируете из старого submit
        • Заменяешь там
          $user_id = $this->ms2->getCustomerId();
          на
          $user_id = $this->getReceiverId();
        • Добавляешь новый метод getReceiverId в новообразованный класс и меняешь его как хочешь
          Дмитрий
          Дмитрий Старожил
          20 февраля 2026, 09:44
          Спасибо! Но есть ряд вопросов, в рамках того, что я сильно не сведущ…
          я уже как-то подключал свой обработчик доставки.
          имеет ли значение в какой папке будет лежать новый файл my_msOrderHandler

          метод это когда начинается с «public function»?

          Т.е. посути в новом файле должно остаться метод submit с теми изменениями что вы написали и мой метод, который я постил в предыдущем комментарии?
            Дмитрий
            Дмитрий Старожил
            20 февраля 2026, 10:59
            и вот еще какой вопрос…

            в документации прописано вот так:

            if (!class_exists('msDeliveryInterface')) {
              require_once dirname(dirname(dirname(__FILE__))) . '/model/minishop2/msdeliveryhandler.class.php';
            }
            
            class msDeliveryHandlerMsk extends msDeliveryHandler implements msDeliveryInterface {
            пример подключения собственного класса доставки.

            ИИ говорит мне, что интерфейс не нужно подключать для msOrderHandler

            т.е. этот кусок получается лишний в моем случае?
            if (!class_exists('msDeliveryInterface')) {
              require_once dirname(dirname(dirname(__FILE__))) . '/model/minishop2/msdeliveryhandler.class.php';
            }
              Дмитрий
              Дмитрий Старожил
              20 февраля 2026, 19:28
              В общем убил целый день, но… так у меня ничего и не вышло.
              не могу зарегистрировать класс.

              содержание самого файла my_msorderhandler.class.php:

              <?php
              if (!class_exists('msOrderHandler')) {  // проверяем интерфейс заказа
                  require_once MODX_CORE_PATH . 'components/minishop2/handlers/msorderhandler.class.php';
              }
              
              class my_OrderHandler extends msOrderHandler {  // implements msOrderInterface не нужен
                  public function submit($data = [])
                      {
                          
                          $response = $this->ms2->invokeEvent('msOnSubmitOrder', [
                              'data' => $data,
                              'order' => $this,
                          ]);
                          if (!$response['success']) {
                              return $this->error($response['message']);
                          }
                          if (!empty($response['data']['data'])) {
                              $this->set($response['data']['data']);
                          }
                  
                          $response = $this->getDeliveryRequiresFields();
                          if ($this->ms2->config['json_response']) {
                              $response = json_decode($response, true);
                          }
                          if (!$response['success']) {
                              return $this->error($response['message']);
                          }
                          $requires = $response['data']['requires'];
                  
                          $errors = [];
                          foreach ($requires as $v) {
                              if (!empty($v) && empty($this->order[$v])) {
                                  $errors[] = $v;
                              }
                          }
                          if (!empty($errors)) {
                              return $this->error('ms2_order_err_requires', $errors);
                          }
                  
                          $user_id = $this->getReceiverId();
                          if (empty($user_id) || !is_int($user_id)) {
                              return $this->error(is_string($user_id) ? $user_id : 'ms2_err_user_nf');
                          }
                  
                          $cart_status = $this->ms2->cart->status();
                          if (empty($cart_status['total_count'])) {
                              return $this->error('ms2_order_err_empty');
                          }
                  
                          $delivery_cost = $this->getCost(false, true);
                          $cart_cost = $this->getCost(true, true) - $delivery_cost;
                          $num = $this->getNewOrderNum();
                  
                          /** @var msOrder $msOrder */
                          $msOrder = $this->storageHandler->getForSubmit(
                              compact('user_id', 'num', 'cart_cost', 'cart_status', 'delivery_cost')
                          );
                  
                          $response = $this->ms2->invokeEvent('msOnBeforeCreateOrder', [
                              'msOrder' => $msOrder,
                              'order' => $this,
                          ]);
                          if (!$response['success']) {
                              return $this->error($response['message']);
                          }
                  
                          if ($msOrder->save()) {
                              $response = $this->ms2->invokeEvent('msOnCreateOrder', [
                                  'msOrder' => $msOrder,
                                  'order' => $this,
                              ]);
                              if (!$response['success']) {
                                  return $this->error($response['message']);
                              }
                  
                              if ($this->storage === 'session') {
                                  $this->ms2->cart->clean();
                                  $this->clean();
                              }
                              if (empty($_SESSION['minishop2']['orders'])) {
                                  $_SESSION['minishop2']['orders'] = [];
                              }
                              $_SESSION['minishop2']['orders'][] = $msOrder->get('id');
                  
                              // Trying to set status "new"
                              $status_new = $this->modx->getOption('ms2_status_new', null, 1);
                              $response = $this->ms2->changeOrderStatus($msOrder->get('id'), $status_new);
                              if ($response !== true) {
                                  return $this->error($response, ['msorder' => $msOrder->get('id')]);
                              }
                  
                              // Reload order object after changes in changeOrderStatus method
                              /** @var msOrder $msOrder */
                              $msOrder = $this->modx->getObject('msOrder', ['id' => $msOrder->get('id')]);
                  
                              /** @var msPayment $payment */
                              $payment = $this->modx->getObject(
                                  'msPayment',
                                  ['id' => $msOrder->get('payment'), 'active' => 1]
                              );
                              if ($payment) {
                                  $response = $payment->send($msOrder);
                                  if ($this->config['json_response']) {
                                      @session_write_close();
                                      echo is_array($response) ? json_encode($response) : $response;
                                      die();
                                  }
                                  if (!empty($response['data']['redirect'])) {
                                      $this->modx->sendRedirect($response['data']['redirect']);
                                  }
                                  if (!empty($response['data']['msorder'])) {
                                      $redirect = $this->modx->context->makeUrl(
                                          $this->modx->resource->id,
                                          ['msorder' => $response['data']['msorder']]
                                      );
                                      $this->modx->sendRedirect($redirect);
                                  }
                                  $this->modx->sendRedirect($this->modx->context->makeUrl($this->modx->resource->id));
                              } else {
                                  if ($this->config['json_response']) {
                                      return $this->success('', ['msorder' => $msOrder->get('id')]);
                                  }
                                  $redirect = $this->modx->context->makeUrl(
                                      $this->modx->resource->id,
                                      ['msorder' => $msOrder->get('id')]
                                  );
                                  $this->modx->sendRedirect($redirect);
                              }
                              return $this->success();
                          }
                  
                          return $this->error();
                      }
                      
                      
                      public function getCustomerId() {
                  error_log("🔥🔥🔥 getCustomerId() НАЧАЛО 🔥🔥🔥");
              
                      $customer = null;
              
                      $response = $this->invokeEvent('msOnBeforeGetOrderCustomer', [
                          'order' => $this->order,
                          'customer' => $customer,
                      ]);
                      if (!$response['success']) {
                          return $response['message'];
                      }
              
                      if (!$customer) {
                          $data = $this->order->get();
                          $email = $data['email'] ?? '';
                          $receiver = $data['receiver'] ?? '';
                          $phone = $data['phone'] ?? '';
                          
                          // Функция для приведения телефона к международному формату +КодСтраныНомер
                          $formatPhone = function($phone) {
                              if (empty($phone)) return '';
                              
                              // Удаляем все нецифровые символы
                              $phone = preg_replace('/[^0-9]/', '', $phone);
                              
                              // Определяем код страны по умолчанию (Россия)
                              $defaultCountryCode = '7';
                              
                              // Если номер начинается с 8 (Россия)
                              if (substr($phone, 0, 1) == '8') {
                                  $phone = $defaultCountryCode . substr($phone, 1);
                              }
                              // Если номер без кода (10 цифр) - добавляем код по умолчанию
                              elseif (strlen($phone) == 10) {
                                  $phone = $defaultCountryCode . $phone;
                              }
                              // Если номер начинается с 7 (уже есть код России)
                              elseif (substr($phone, 0, 1) == '7' && strlen($phone) == 11) {
                                  // Оставляем как есть
                              }
                              // Если номер с другим кодом страны
                              elseif (strlen($phone) > 11 || (strlen($phone) == 11 && substr($phone, 0, 1) != '7')) {
                                  // Оставляем как есть (уже содержит код страны)
                              }
                              
                              // Добавляем знак + в начало
                              return '+' . $phone;
                          };
                          
                          // Приводим телефон к единому формату
                          $formattedPhone = $formatPhone($phone);
                          
                          // Обновляем телефон в данных заказа
                          if ($formattedPhone !== $phone) {
                              $data['phone'] = $formattedPhone;
                              $this->order->set($data);
                              $phone = $formattedPhone;
                          }
                          
                          // Формируем receiver, если он пустой
                          if (empty($receiver)) {
                              if (!empty($phone)) {
                                  $receiver = preg_replace('/[^0-9]/', '', $phone);
                              } elseif (!empty($email)) {
                                  $receiver = substr($email, 0, strpos($email, '@'));
                              } else {
                                  $receiver = uniqid('user_', false);
                              }
                          }
                          
                          // Формируем email, если он пустой
                          if (empty($email)) {
                              if (!empty($phone)) {
                                  $cleanPhone = preg_replace('/[^0-9]/', '', $phone);
                                  $email = 'user_' . $cleanPhone . '@' . $this->modx->getOption('http_host');
                              } else {
                                  $email = $receiver . '@' . $this->modx->getOption('http_host');
                              }
                          }
              
                          // Если пользователь авторизован
                          if ($this->modx->user->isAuthenticated()) {
                              $profile = $this->modx->user->Profile;
                              
                              if (!$profile->get('email')) {
                                  $profile->set('email', $email);
                              }
                              
                              if (!empty($phone) && $profile->get('mobilephone') != $phone) {
                                  $profile->set('mobilephone', $phone);
                              }
                              
                              if (!empty($receiver) && $profile->get('fullname') != $receiver) {
                                  $profile->set('fullname', $receiver);
                              }
                              
                              $profile->save();
                              $customer = $this->modx->user;
                              
                          } else {
                              // Поиск существующего пользователя
                              $c = $this->modx->newQuery('modUser');
                              $c->leftJoin('modUserProfile', 'Profile');
                              
                              $filter = [];
                              
                              if (!empty($phone)) {
                                  $filter['modUser.username'] = $phone;
                                  $filter['OR:Profile.mobilephone:='] = $phone;
                              }
                              
                              if (!empty($email)) {
                                  $filter['OR:Profile.email:='] = $email;
                              }
                              
                              $c->where($filter);
                              $c->select('modUser.id');
                              
                              // Пытаемся найти пользователя
                              if (!$customer = $this->modx->getObject('modUser', $c)) {
                                  // Создаем нового пользователя
                                  $userData = [
                                      'username' => !empty($phone) ? $phone : $email,
                                      'password' => md5(rand()),
                                      'active' => 1
                                  ];
                                  
                                  $customer = $this->modx->newObject('modUser', $userData);
                                  
                                  $profileData = [
                                      'fullname' => $receiver,
                                  ];
                                  
                                  if (!empty($email)) {
                                      $profileData['email'] = $email;
                                  }
                                  
                                  if (!empty($phone)) {
                                      $profileData['mobilephone'] = $phone;
                                  }
                                  
                                  $profile = $this->modx->newObject('modUserProfile', $profileData);
                                  $customer->addOne($profile);
                                  
                                  // Добавляем настройку языка
                                  $setting = $this->modx->newObject('modUserSetting');
                                  $setting->fromArray([
                                      'key' => 'cultureKey',
                                      'value' => $this->modx->getOption('cultureKey', null, 'en', true),
                                  ], '', true);
                                  $customer->addMany($setting);
                                  
                                  if (!$customer->save()) {
                                      $customer = null;
                                  } elseif ($groups = $this->modx->getOption('ms2_order_user_groups', null, false)) {
                                      $groupRoles = array_map('trim', explode(',', $groups));
                                      foreach ($groupRoles as $groupRole) {
                                          $groupRole = explode(':', $groupRole);
                                          $roleId = null;
                                          if (count($groupRole) > 1 && !empty($groupRole[1])) {
                                              $roleId = is_numeric($groupRole[1]) ? (int)$groupRole[1] : $groupRole[1];
                                          }
                                          $customer->joinGroup($groupRole[0], $roleId);
                                      }
                                  }
                              } else {
                                  // Пользователь найден - обновляем данные
                                  $profile = $customer->getOne('Profile');
                                  if ($profile) {
                                      $changed = false;
                                      
                                      if (!empty($phone) && $profile->get('mobilephone') != $phone) {
                                          $profile->set('mobilephone', $phone);
                                          $changed = true;
                                      }
                                      
                                      if (!empty($email) && $profile->get('email') != $email) {
                                          $profile->set('email', $email);
                                          $changed = true;
                                      }
                                      
                                      if (!empty($receiver) && $profile->get('fullname') != $receiver) {
                                          $profile->set('fullname', $receiver);
                                          $changed = true;
                                      }
                                      
                                      if ($changed) {
                                          $profile->save();
                                      }
                                  }
                              }
                          }
                      }
              
                      $response = $this->invokeEvent('msOnGetOrderCustomer', [
                          'order' => $this->order,
                          'customer' => $customer,
                      ]);
                      if (!$response['success']) {
                          return $response['message'];
                      }
              
                      return $customer instanceof modUser ? $customer->get('id') : 0;
                  }
                      
              }

              согласно священописанию в консоле делаю так:
              <?php
              if ($miniShop2 = $modx->getService('miniShop2')) {
                $miniShop2->addService('order', 'my_OrderHandler',
                    '{core_path}components/minishop2/custom/order/my_msorderhandler.class.php');
              }
              Пробую, и ничего не меняется. На пару с ИИ добрел до системной настройки, с ключем ms2_order_handler_class и там указал свой класс. Перестало работать вообще. Т.е. тыкаю на кнопку «сделать заказ» на самом сайте и ничего не происходит.
              Из чего делаю вывод, что класс мой не зарегистрировался.

              Дальше меняю настройку ms2_services.
              она у меня выглядела так:
              {«cart»:[],«order»:[«my_OrderHandler»],«payment»:[],«delivery»:{«mydelivery»:"{core_path}components\/minishop2\/custom\/delivery\/my_msdeliveryhandler.class.php"}}

              т.к. я уже ранее подрубал собственную службу доставки, и она вроде как исправно работает, делаю по образу и подобию с order, хотя ИИ визжит, что так делать нельзя, и у этой настройки должно быть только название класса.

              В логах живет вот такая ошибка:

              (ERROR @ /home/c/cz42644/test-iflower/public_html/core/components/minishop2/model/minishop2/minishop2.class.php: 484) [miniShop2] Could not load custom class at "/home/c/cz42644/test-iflower/public_html/my_OrderHandler"
              Т.е. я так понимаю, он ищет файл с моей службой почему-то не так где надо, по сути в корне. И как ему указать, где искать — не ведаю.

              Вообщем, итог такой, что либо ничего не меняется, либо не работает вовсе.

              Что и где я делаю не так?
                Тодор
                Тодор Старожил
                21 февраля 2026, 23:53
                Етот кусок кода отвечает за то что бы менялась настройка ms2_services
                <?php
                if ($miniShop2 = $modx->getService('miniShop2')) {
                  $miniShop2->addService('order', 'my_OrderHandler',
                      '{core_path}components/minishop2/custom/order/my_msorderhandler.class.php');
                }
                После чего у тебя ms2_services должен быть таким
                {"cart":[],"order":{"my_OrderHandler":"{core_path}components\/minishop2\/custom\/order\/my_msorderhandler.class.php"},"payment":[],"delivery":{"mydelivery":"{core_path}components\/minishop2\/custom\/delivery\/my_msdeliveryhandler.class.php"}}
                Тебе надо делать только одно из двух либо регистировать клас через консоль либо в сис. настройках.

                В зависимости от версии minishopа может быть разный путь к msOrderHandler, убедись что етот файл существует
                require_once MODX_CORE_PATH . 'components/minishop2/handlers/msorderhandler.class.php';
                А еще в submit ты поменял $user_id = $this->ms2->getCustomerId(); на $user_id = $this->getReceiverId(); Но новый метод ты не назвал getReceiverId
                  Дмитрий
                  Дмитрий Старожил
                  22 февраля 2026, 16:46
                  ух, спасибо за разъяснения. Т.е. фактически понятие «регистрация службы» это просто изменение системных настроек?

                  с разным расположением файла в зависимости от версии, я уже споткнулся на этом моменте когда подключал свою службу доставки, кстати, в мануале, как раз таки не правильно указано расположения файла (по всей видимости для старой версии)
                  Я разобрался почему у меня не подключался мой класс, в системных настройках в значении переменной ms2_order_handler_class, у меня было указано «my_OrderHandler » с пробелом на конце, очень было обидно осознать это, когда я два дня пытался разобраться, чой-то мой класс не подключается.

                  Теперь что касается, кастомного класса. Или, наверное даже больше кастомного метода.

                  Я начал с того что поменял исходники, и собственно, все у меня работало так как надо.
                  Телефон приводился к нужному формату, вместо емейла записывался номер телефона, и вообще жизнь прекрасна и удивительна, но!
                  Когда, все же заработал мой класс, и я сделал, как Вы советовали:
                  я скопировал метод submit, в нем изменил $user_id = $this->ms2->getCustomerId() на $user_id = $this->getReceiverId();

                  и новый метод, поменял у него название на getReceiverId()…
                  тут все работать перестало.

                  По сути в исходниках я изменял только метод getCustomerId
                  И был совершенно уверен, что тут у меня не возникнет ни каких проблем.

                  Собственно в связи с этим вопрос. То был файл в ядре и возможно там различные объекты и прочее достаются иначе, чем это нужно делать через класс, как это есть сейчас у меня?

                  Помогите, я снова в тупике.
                    Тодор
                    Тодор Старожил
                    22 февраля 2026, 17:10
                    Ето ты сделал?
                    public function getReceiverId() {
                      error_log("🔥🔥🔥 getCustomerId() НАЧАЛО 🔥🔥🔥");
                      $customer = null;
                      $response = $this->ms2->invokeEvent('msOnBeforeGetOrderCustomer', [//вместо $this->
                        'order' => $this,//Вместо $this->order,
                        'customer' => $customer,
                      ]);
                      if (!$response['success']) {
                        return $response['message'];
                      }
                    
                      if (!$customer) {
                        $data = $this->get();//Вместо $this->order->get();
                        
                        ...
                          $this->set($data);//Вмесо $this->order->set($data);
                        ...
                      }
                    
                      $response = $this->ms2->invokeEvent('msOnGetOrderCustomer', [//Вместо $this->
                        'order' => $this, // Вместо $this->order,
                        'customer' => $customer,
                      ]);
                      if (!$response['success']) {
                        return $response['message'];
                      }
                    
                      return $customer instanceof modUser ? $customer->get('id') : 0;
                    }
                    возможно что-то упустил
                      Дмитрий
                      Дмитрий Старожил
                      24 февраля 2026, 18:29
                      это сделал ИИ.
                      Я взял код из файла /core/components/minishop2/model/minishop2/minishop2.class.php
                      я скопировал этот метод function getReceiverId(), скинул ИИ и описал свои хотелки.
                      ИИ сгенерировал новый код, я его вставил обратно в файл /core/components/minishop2/model/minishop2/minishop2.class.php и все прекрасно заработало, но когда я расширяю хендлер, у меня перестает работать.

                      Поправил код по вашему мануалу — все работает, спасибо большое!
          Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
          13