Новости MiniShop3, mSearch, mFilter
За четыре релиза — с 1.6.0 до 1.10.0 — MiniShop3 заметно похорошел: галерея и опции полностью на Vue, кастомные вкладки заказа из плагинов, inline-редактирование в каталоге, свои поля у производителя. Параллельно двигались и компоненты экосистемы — mSearch и mFilter, так что про них тоже будет. Рассказываю по порядку, что изменилось снаружи и что стоит учесть при обновлении.
Старая ExtJS-прослойка (панель, тулбар, диалоги) ушла целиком. На её месте — Vue-компоненты: сетка с drag-n-drop для сортировки, контекстное меню, поиск и пагинация. Массовые действия вынесены в тулбар, отдельный диалог редактирования вызывается по клику. Uppy-загрузчик встроен прямо в бандл вкладок товара — больше никаких отдельных точек входа. В совокупности — минус ~900 строк ExtJS.

До последнего не хотел лезть в страницу Настройки → Опции — слишком много функционала, который нужно перевезти без регрессий. Собрался с духом и сделал. Теперь страница Настройки полностью избавлена от ExtJS.
Что получили:



Отдельной строкой хочу отметить, что теперь для каждой категории опцию можно назвать по своему.
Например option_color в одной категории может быть подписана как Цвет плафона, в другой как Цвет рамки, в третьей просто Цвет.

Справедливости ради, с опциями, работы впереди еще много. Мы уже успели зафиксировать парочку багов, которые предстоит закрыть.
На экране заказа теперь можно добавлять свои вкладки из плагина — хоть на Vue, хоть на ExtJS, хоть вперемешку. Регистрация через MS3OrderTabsRegistry.register() в обработчике msOnManagerCustomCssJs. Ядро ничего не правим, вкладки подхватываются на лету. Можно скрыть вкладку в режиме создания заказа, зарезервированные ключи защищены — чужой плагин не сломает «Информацию» или «Товары».

Двойной клик по ячейке в таблице товаров — и вводите цену (или артикул, или вес) прямо там, не открывая карточку. Работает для текста, чисел и чекбоксов. Какие поля редактируются и каким редактором — настраивается в Утилиты → Поля таблицы для грида «Товары категории». Поле published дополнительно дёргает события OnDocPublished / OnDocUnPublished, так что всё, что подписано на публикацию ресурса, продолжит работать.

Раньше любой компонент, которому нужно было что-то делать через API, писал собственный роутинг. Теперь можно пользоваться роутингом MiniShop3: складываете PHP-фрагмент в core/config/ms3.routes.d/web/ (или /manager/), и ядро подхватит его автоматически при следующем запросе. Переопределение — по ключу METHOD:PATTERN. В поставке лежит example-addon.php.dist — скопировали, подправили, работает.
Документация к разделу
Форма производителя переехала на универсальный рендерер полей, и теперь в Утилиты → Свои поля можно выбрать модель msVendor и добавить любое поле — текст, число, чекбокс, файл через FileBrowser, выпадающий список. Поле появится в форме редактирования и отдастся через API вместе со стандартными. Это задел на будущее: постепенно так же будут расширяться и другие страницы админки.
В версии 1.7.0 плейсхолдер cost_formatted в списке заказов личного кабинета стал включать символ валюты. Если в вашем кастомном чанке валюта дописана руками — получите двойной символ. Лечится удалением ручной валюты из шаблона.
Полный список изменений по каждому релизу — в CHANGELOG. Сами релизы и транспортные пакеты — на странице releases. Вопросы, предложения и баг-репорты — в issues.
MiniShop3 — не единственное, что двигалось. Компонент mSearch за эти месяцы прошёл путь до версии 1.2.0-beta с двумя большими релизами.
Главное снаружи:
Второй крупный компонент экосистемы— mFilter — с марта доехал до версии 1.3.2-beta. Главное, что увидит пользователь:
Также запустил демо-сайт minishop3.ru/ где вы можете увидеть работу самого MiniShop3 а также поиска и фильтров.
Галерея товаров — полностью на Vue
Старая ExtJS-прослойка (панель, тулбар, диалоги) ушла целиком. На её месте — Vue-компоненты: сетка с drag-n-drop для сортировки, контекстное меню, поиск и пагинация. Массовые действия вынесены в тулбар, отдельный диалог редактирования вызывается по клику. Uppy-загрузчик встроен прямо в бандл вкладок товара — больше никаких отдельных точек входа. В совокупности — минус ~900 строк ExtJS.

Опции — тоже на Vue, включая страницу настроек
До последнего не хотел лезть в страницу Настройки → Опции — слишком много функционала, который нужно перевезти без регрессий. Собрался с духом и сделал. Теперь страница Настройки полностью избавлена от ExtJS.
Что получили:
- Настройки → Опции — грид с сортировкой, дерево категорий с независимыми чекбоксами и контекстным меню, диалог редактирования со всеми типами значений (включая удобный выбор цвета и drag-n-drop сортировку значений в списке).
- Категория → Опции — drag-n-drop сортировка привязок, inline-редактор значения по умолчанию, массовые действия и удобный диалог «Копировать опции из другой категории» (сам не знал, что такой сценарий уже был заложен).
- Карточка товара → Опции товара — все десять типов опций переехали в новом исполнении, старые задумки сохранены. Для comboOptions появились чипы с автодобавлением по Enter или запятой и кликабельными подсказками под полем.



Отдельной строкой хочу отметить, что теперь для каждой категории опцию можно назвать по своему.
Например option_color в одной категории может быть подписана как Цвет плафона, в другой как Цвет рамки, в третьей просто Цвет.

Справедливости ради, с опциями, работы впереди еще много. Мы уже успели зафиксировать парочку багов, которые предстоит закрыть.
Кастомные вкладки на странице заказа
На экране заказа теперь можно добавлять свои вкладки из плагина — хоть на Vue, хоть на ExtJS, хоть вперемешку. Регистрация через MS3OrderTabsRegistry.register() в обработчике msOnManagerCustomCssJs. Ядро ничего не правим, вкладки подхватываются на лету. Можно скрыть вкладку в режиме создания заказа, зарезервированные ключи защищены — чужой плагин не сломает «Информацию» или «Товары».

Inline-редактирование товаров в каталоге
Двойной клик по ячейке в таблице товаров — и вводите цену (или артикул, или вес) прямо там, не открывая карточку. Работает для текста, чисел и чекбоксов. Какие поля редактируются и каким редактором — настраивается в Утилиты → Поля таблицы для грида «Товары категории». Поле published дополнительно дёргает события OnDocPublished / OnDocUnPublished, так что всё, что подписано на публикацию ресурса, продолжит работать.

Модульные роуты для сторонних компонентов
Раньше любой компонент, которому нужно было что-то делать через API, писал собственный роутинг. Теперь можно пользоваться роутингом MiniShop3: складываете PHP-фрагмент в core/config/ms3.routes.d/web/ (или /manager/), и ядро подхватит его автоматически при следующем запросе. Переопределение — по ключу METHOD:PATTERN. В поставке лежит example-addon.php.dist — скопировали, подправили, работает.
Документация к разделу
Кастомные поля для производителя
Форма производителя переехала на универсальный рендерер полей, и теперь в Утилиты → Свои поля можно выбрать модель msVendor и добавить любое поле — текст, число, чекбокс, файл через FileBrowser, выпадающий список. Поле появится в форме редактирования и отдастся через API вместе со стандартными. Это задел на будущее: постепенно так же будут расширяться и другие страницы админки.
Ещё — по мелочи, но заметно
- Форматированные плейсхолдеры валюты и веса. В сниппетах и чанках появились price_formatted, cost_formatted, weight_formatted и так далее — больше не нужно дописывать рубли и килограммы руками. Источник истины — системные настройки ms3_currency_symbol, ms3_currency_position, ms3_weight_unit.
- Корзина синхронизируется с плагинами. Если ваш плагин на msOnGetStatusCart пересчитывает скидку или добавляет стоимость доставки — итоги в чанке msCart теперь корректно отразят эти изменения. В чанке и return=data доступен отдельный массив status.
- Кастомные поля в валидации заказа. Правила доставки вида agreement: required|accepted сохраняются между шагами оформления и доезжают до событий msOnBeforeCreateOrder / msOnCreateOrder.
- Самолечащаяся миграция. Если после обновления админка встретила вас ошибкой ms3_model_fields_empty — при следующем накате миграций всё починится автоматически, пользовательские настройки секций и ширин колонок не затирая.
Что учесть при обновлении
В версии 1.7.0 плейсхолдер cost_formatted в списке заказов личного кабинета стал включать символ валюты. Если в вашем кастомном чанке валюта дописана руками — получите двойной символ. Лечится удалением ручной валюты из шаблона.
Что дальше
Полный список изменений по каждому релизу — в CHANGELOG. Сами релизы и транспортные пакеты — на странице releases. Вопросы, предложения и баг-репорты — в issues.
Экосистема: mSearch 1.1.0 → 1.2.0-beta
MiniShop3 — не единственное, что двигалось. Компонент mSearch за эти месяцы прошёл путь до версии 1.2.0-beta с двумя большими релизами.
Главное снаружи:
- Событие mseOnRegisterAdapters — теперь сторонние компоненты могут регистрировать свои адаптеры индексации. Это открывает дорогу к индексации товаров MiniShop3, комментариев форума и любых других моделей без правки ядра mSearch.
- Поиск с учётом контекста. Параметр &contexts= в сниппете mSearch ограничивает результаты выбранными контекстами MODX. Полезно для мультиязычных и мультиконтекстных сайтов.
- Headless-режим и hook-система. Появился window.msearch для интеграции с React, Vue или кастомным фронтом. Через хуки beforeSearch, afterSearch, beforeSuggest можно вмешиваться в поиск без форка.
- Оптимизация использования памяти при поиске по большим базам. Раньше при выдаче сотен результатов по тяжёлым моделям (msProduct) PHP-воркер ложился на 512 MB из-за xPDO-кэша. Запросы переведены на простой PDO, intro грузится потоково.
Экосистема: mFilter — что нового после 1.2.0
Второй крупный компонент экосистемы— mFilter — с марта доехал до версии 1.3.2-beta. Главное, что увидит пользователь:
- Поддержка дополнительных категорий MS3. Товары, привязанные к категории через msCategoryMember, теперь корректно учитываются во всех фильтрах — в счётчиках, пагинации и выдаче. Раньше они просто выпадали.
- Защита от SEO-дублей. Фильтрованные URL без концевого слэша по умолчанию возвращают 404, поведение настраивается — 404, 301 или выключить. Больше не нужно городить правила в nginx, чтобы не плодить дубли.
- Поддержка цен с копейками в range-фильтрах. Слайдер больше не округляет 17.6 до 18, в SEO URL легально живут значения вида price_99.50_500.
- Точка расширения для стороннего JS. После каждого обновления выдачи mFilter выбрасывает событие mfilter:contentLoaded. Лейзи-лоад картинок, трекеры, обработчики кликов на новые карточки — всё это теперь навешивается без танцев.
Демо-сайт
Также запустил демо-сайт minishop3.ru/ где вы можете увидеть работу самого MiniShop3 а также поиска и фильтров.
Поблагодарить автора
Отправить деньги
Комментарии: 21
Шикарная работа!
Коль, расскажи, схема базы данных (особенно в опциях товара) не менялась? На этом стоит уточнять внимание, ибо существующие компоненты могут её использовать.
Коль, расскажи, схема базы данных (особенно в опциях товара) не менялась? На этом стоит уточнять внимание, ибо существующие компоненты могут её использовать.
Нет. изменился только интерфейс админки. Модели, состав таблиц, работа сниппетов на фронте — все осталось прежним
Только у меня сильно виснет фильтр? Если особенно меняешь цену все disable и тишина, через 30 сек фильтр сработал(
да что то очень долго работает.
Переработал подход к скорости. Стало получше
modx.pro/components/25571
modx.pro/components/25571
ms2_product_options как хранит опции товаров?
У меня на сайте рабочем на MODX REVO 2.8.8 + Minishop 3.0.7 заметил вот что —
для многих товаров требуется указание от 10 до 30 опций — по факту заполняют не все. Причем тут пришлось делать конфигуратор, а для него еще создать штук 20 опций.
А по факту у меня часто не все опции заполняются. И получается многие записи хранятся пустые. Т.е. на каждый товар создается запись вида product_id — key опции с пустым value. А можно же не хранить пустые опции? Не знаю почему и как, но вызывает сомнение что так надо было делать.
Всего на рабочем проекте будет более 100 000 товаров (будет больше).
В итоге в таблице ms2_product_options на текущий момент уже вбито 35 000 строк, а когда зальем все товары со всеми опциями, то там будет просто жесть как много строк с пустыми значениями.
У меня на сайте рабочем на MODX REVO 2.8.8 + Minishop 3.0.7 заметил вот что —
для многих товаров требуется указание от 10 до 30 опций — по факту заполняют не все. Причем тут пришлось делать конфигуратор, а для него еще создать штук 20 опций.
А по факту у меня часто не все опции заполняются. И получается многие записи хранятся пустые. Т.е. на каждый товар создается запись вида product_id — key опции с пустым value. А можно же не хранить пустые опции? Не знаю почему и как, но вызывает сомнение что так надо было делать.
Всего на рабочем проекте будет более 100 000 товаров (будет больше).
В итоге в таблице ms2_product_options на текущий момент уже вбито 35 000 строк, а когда зальем все товары со всеми опциями, то там будет просто жесть как много строк с пустыми значениями.
Тут так то тред про minishop3, а не про minishop2
Я потому и задал вопрос о том как реализовано в Minishop3?
Добрый день
[2026-04-30 15:50:22] (ERROR @ /home/core/components/minishop3/src/Controllers/Api/Manager/CategoryOptionsController.php : 372) PHP warning: Undefined array key "id" и не отображаются товары в доп категориях админке
Добрый день!
Это ворнинг на работу не влияет, а что в консоли браузера и какая версия php?
Это ворнинг на работу не влияет, а что в консоли браузера и какая версия php?
и периодически пропадают доп категории из отмеченных, приходиться заново проставлять чекбоксы.
Похоже баг с привязкой опций к категориям: disk.yandex.ru/i/Wq3XfDwgS0dkJw.
Не работает getTree в core/components/minishop3/src/Controllers/Api/Manager/OptionsController.php.
В качестве корня ($parent) передаётся 0, поэтому дерево будет строиться только в том случае, если корневой ресурс имеет класс msCategory. Вероятно нужно добавить настройку, определяющую корень каталога товаров.
Не работает getTree в core/components/minishop3/src/Controllers/Api/Manager/OptionsController.php.
В качестве корня ($parent) передаётся 0, поэтому дерево будет строиться только в том случае, если корневой ресурс имеет класс msCategory. Вероятно нужно добавить настройку, определяющую корень каталога товаров.
Ещё момент: ошибка при создании покупателя через js CustomerAPI
Пробуем выполнить:
отправляющий запрос на роут /api/v1/customer/add disk.yandex.ru/i/QLb00kmT6NItWg, которого не существует: disk.yandex.ru/i/G1mWoPwSw0DmbA
Логично, т.к. такого роута нет в core/components/minishop3/config/routes/web.php
Возможно вместо handleAdd в CustomerUI должен быть метод handleRegister, использующий соответствующий роут. Я обратил на него внимание именно потому, что не понял, зачем нужен handleAdd.
Логично, что нужно зарегистрировать клиента через register, а если менять его поля, то это или PUT profile или PUT addresses если поля адресные. Ну или я что-то не понял.
Пробуем выполнить:
await ms3.customerUI.handleAdd('email', 'user@example.com')В классе CustomerAPI (assets/components/minishop3/js/web/core/CustomerAPI.js) выполняется метод add, отправляющий запрос на роут /api/v1/customer/add disk.yandex.ru/i/QLb00kmT6NItWg, которого не существует: disk.yandex.ru/i/G1mWoPwSw0DmbA
Логично, т.к. такого роута нет в core/components/minishop3/config/routes/web.php
Возможно вместо handleAdd в CustomerUI должен быть метод handleRegister, использующий соответствующий роут. Я обратил на него внимание именно потому, что не понял, зачем нужен handleAdd.
Логично, что нужно зарегистрировать клиента через register, а если менять его поля, то это или PUT profile или PUT addresses если поля адресные. Ну или я что-то не понял.
В массиве $order (сниппет msOrder) значения cost, cart_cost (+возможно delivery_cost) содержат нечисловые значения: disk.yandex.ru/i/ezWUv4Sd6lyucA. Т.е. вместо чистого числа там строка с пробелом.
Это приводит к разным нюансам, а главное — к фатальной ошибке в случае применения к ним модификатора number pdoTools.
Пока лучше делать так, если используем эти параметры:
Это приводит к разным нюансам, а главное — к фатальной ошибке в случае применения к ним модификатора number pdoTools.
Пока лучше делать так, если используем эти параметры:
{$order.cost | replace : ' ' : '' | number : 0 : '.' : ' '}
Нюанс со сниппетом msCart: прерывание работы сниппета при наличии $_GET['msorder']: disk.yandex.ru/i/fOiDPWuW8xgXsg
Мне кажется, это ошибочное решение, т.к. данный сниппет, например может применяться для вывода мини-корзины и тогда будет просто пустота — ни чанка, на массива.
Крайне неожиданное поведение — ведь логика отображения должна быть исключительно в чанке.
Не думаю, что нужно просто глушить вывод сниппета от наличия этой переменной — может (и точно будет) приводить к проблемам и постоянным вопросам пользователей.
Предполагаю, что это сделано для случая вывода корзины и формы заказа на одной странице, но лучше таки оставить логику на стороне чанков/шаблона, чтобы это было контролируемо.
Например:
Мне кажется, это ошибочное решение, т.к. данный сниппет, например может применяться для вывода мини-корзины и тогда будет просто пустота — ни чанка, на массива.
Крайне неожиданное поведение — ведь логика отображения должна быть исключительно в чанке.
Не думаю, что нужно просто глушить вывод сниппета от наличия этой переменной — может (и точно будет) приводить к проблемам и постоянным вопросам пользователей.
Предполагаю, что это сделано для случая вывода корзины и формы заказа на одной странице, но лучше таки оставить логику на стороне чанков/шаблона, чтобы это было контролируемо.
Например:
{if $.get.msorder is empty} // выводим корзину {/if}
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.