Всего 125 653 комментария

Олег Захаров
3 часа назад
0
Компонент очень нужный и мне кажется будет востребован.
У меня тут задача стоит сделать что-то подобное на сайте на движке на MODX 2.8 — там есть старые другие решения.
Но задумываюсь о глобальном переносе на 3-ю версию.
Олег Захаров
3 часа назад
0
ну я подумал что возможно ты сам лично уже где-то у себя на заказе рабочем внедрил и есть рабочий сайт
Олег Захаров
3 часа назад
0
А какой компонент для личного кабинета и авторизации используется?
Андрей
3 часа назад
+1
вот нашел еще проще решение для текущего пользователя, без id
$profile = $modx->user->getOne('Profile');
return $profile->get('extended')[$input];
и вызов
[[!userField? &input=`tv_name`]]
но все равно хотелось бы понять причины и может есть штатное решение.
Олег Захаров
4 часа назад
0
тут пришла мысль что никто не захочет просто так делиться своим опытом за бесплатно. Можно было бы сделать статьи и кейсы платными? Типа хочешь прочитать инструкцию или кейс по настройке того или иного компонента — ведь кто-то потратил время на написание инструкции — стоимость 100 руб.
Тут желательно конечно не переусердствовать. Но и обидно не будет тратить время на написание кейсов и инструкций.
А чтобы было понятно что больше всего интересует людей — было бы неплохо сделать список вопросов, на которые нужны ответы — типа голосование или например в описании компонентов добавить голосование за необходимость написания кейсов и готовность оплатить или сбор средств на написание инструкций. Понятно что есть стандартные инструкции, но они не всегда понятные.
И часто бывают брошенные компоненты. Можно было бы собирать средства на его дальнейшее развитие.
Николай Савин
5 часов назад
0
А ты думаешь люди прям десятками уже интернет-магазины на моих решениях клепают? Это очень оптимистично ))
Постараюсь на этой неделе добить и выложить демо-сайт. Там будет и обычный miniShop3 и поиск с фильтрами, и демонстрация вариантов в том числе.
Олег Захаров
5 часов назад
0
Можете прислать примеры живых сайтов не решении? На которых данное решение уже установлено?
Дмитрий
8 часов назад
0
и вот еще какой вопрос…

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

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';
}
Наумов Алексей
8 часов назад
0
Добрый день! Я этот компонент давно делал, и еще лет 5 не возвращался к нему… он работоспособен, все в этом плане нормально (ну по крайней мере с php не выше 8 версии я думаю).

Что касается интеграции с mSync — вам придется самим думать как это сделать, руками создавать записи в БД. Никаких других механизмов нет.

Смотрите схему БД, там всё просто: github.com/createit-ru/msProductKits/blob/master/core/components/msproductkits/model/schema/msproductkits.mysql.schema.xml
Дмитрий
9 часов назад
0
Спасибо! Но есть ряд вопросов, в рамках того, что я сильно не сведущ…
я уже как-то подключал свой обработчик доставки.
имеет ли значение в какой папке будет лежать новый файл my_msOrderHandler

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

Т.е. посути в новом файле должно остаться метод submit с теми изменениями что вы написали и мой метод, который я постил в предыдущем комментарии?
Алексей Корчагин
Сегодня в 07:22
0
Доброго времени суток.
Подскажите, как правильно составлять наборы при выгрузке товаров через mSync? Делать прямые запросы на добавление этих связей? Собираюсь делать в событии где известен id ресурса.
Тодор
Вчера в 19:45
0
  • Расширяешь класс msOrderHandler (поключение)
  • Копируете из старого submit
  • Заменяешь там
    $user_id = $this->ms2->getCustomerId();
    на
    $user_id = $this->getReceiverId();
  • Добавляешь новый метод getReceiverId в новообразованный класс и меняешь его как хочешь
Андрей
Вчера в 10:22
0
Вижу, спасибо.
Ошибочно решил, что если есть в документации minishop2, то в старых версиях есть и сам код не посмотрел.
Иван Бочкарев
Вчера в 09:27
+1
Привет, Алексей.

1. Как определяем ботов
Проверка идёт по User-Agent в ms3rv_is_bot() (helpers.php). Используется regex по типичным маркерам краулеров:
bot|crawl|slurp|spider|mediapartners|googlebot|bingbot|yandex|baiduspider|duckduckbot|teoma|ahrefs|semrush|mj12bot|dotbot|rogerbot|screaming|petalbot|bytespider
Проверка выполняется до сохранения в БД — при block_bots=true (по умолчанию) запросы от таких User-Agent не пишутся. Это не идеально (подделка UA, новые боты), но отсекает большую часть краулеров.

2. Большой каталог и много пользователей
Есть несколько механизмов, чтобы таблица не росла бесконечно:

Ограничение дублей. UNIQUE по (user_id, session_id, product_id) — для каждой пары «пользователь + товар» хранится одна строка. Повторный просмотр того же товара только обновляет viewed_at, новых строк не добавляется.
TTL и автоочистка. По умолчанию ttl_days=90 и auto_cleanup_enabled=true. Плагин на OnWebPageInit раз в день удаляет записи старше 90 дней.
Месячное архивирование. При archive_enabled=true (по умолчанию) данные за прошедший месяц агрегируются в ms3recentlyviewed_monthly (product_id, view_count, unique_users), а детальные строки из основной таблицы удаляются. Объём основной таблицы остаётся ограниченным.

Итог: Рост таблицы ограничен числом уникальных пар «пользователь × товар» за последние 90 дней, а не количеством просмотров. Архив уменьшает объём основной таблицы, сохраняя агрегаты для аналитики.
Наумов Алексей
Вчера в 07:51
+2
Привет! Дай распрошу)
как ботов отслеживаешь? Пару раз сталкивался, интересны подходы.
И вот что думаешь, большой каталог + большое кол-во пользователей не убьет таблицу просмотров в БД? Как ни крути именно просмотров может быть ну очень много, особенно если не всех ботов получится отсечь.
Дмитрий
18 февраля 2026, 21:55
0
minishop2-4.4.2-pl
Артур Шевченко
18 февраля 2026, 21:22
0
Версия минишопа какая?
Дмитрий
18 февраля 2026, 18:50
0
Во, вот так хочу, что бы было

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;
    }