
Отличия от miniShop2
Это руководство поможет разработчикам, знакомым с miniShop2, быстро освоить MiniShop3 и понять ключевые изменения.
Системные требования
| Требование | miniShop2 | MiniShop3 |
|---|---|---|
| MODX | 2.3+ | 3.0.0+ |
| PHP | 7.0+ | 8.1+ |
| MySQL | 5.5+ | 5.7+ / MariaDB 10.3+ |
| pdoTools | 2.x | 3.x |
Архитектура
Пространства имён (Namespaces)
miniShop2 использовал классы без пространств имён. В MiniShop3 все классы организованы в namespace MiniShop3\:
// miniShop2
$ms2 = $modx->getService('minishop2');
$product = $modx->getObject('msProduct', $id);
$order = $modx->getObject('msOrder', $id);
// MiniShop3
use MiniShop3\MiniShop3;
use MiniShop3\Model\msProduct;
use MiniShop3\Model\msOrder;
$ms3 = $modx->services->get('ms3');
$product = $modx->getObject(msProduct::class, $id);
$order = $modx->getObject(msOrder::class, $id);Service Container
MiniShop3 использует DI-контейнер MODX 3 для регистрации сервисов:
// miniShop2
$ms2 = $modx->getService('minishop2');
$cart = $ms2->cart;
$order = $ms2->order;
// MiniShop3
$ms3 = $modx->services->get('ms3');
$cart = $modx->services->get('ms3_cart');
$order = $modx->services->get('ms3_order');Миграции базы данных
miniShop2 управлял схемой БД через xPDO схему и build-процесс. MiniShop3 использует Phinx для версионирования миграций:
# Запуск миграций
php vendor/bin/phinx migrate -c phinx.phpПри установке компонента миграции выполняются автоматически.
Системные настройки
Все системные настройки переименованы с ms2_ на ms3_:
| miniShop2 | MiniShop3 |
|---|---|
ms2_template_product_default | ms3_template_product_default |
ms2_template_category_default | ms3_template_category_default |
ms2_category_grid_fields | Удалено. Колонки грида категории: Утилиты → Поля таблиц (ms3_grid_fields, grid_key=category-products) + Утилиты → Поля моделей |
ms2_product_extra_fields | ms3_product_extra_fields |
ms2_frontend_js | ms3_frontend_assets |
ms2_frontend_css | (объединено в ms3_frontend_assets) |
ms2_price_format | ms3_price_format |
ms2_weight_format | ms3_weight_format |
Новые настройки MiniShop3
MiniShop3 добавляет множество новых настроек:
API и безопасность:
ms3_cors_allowed_origins— разрешённые домены для CORSms3_api_debug— режим отладки APIms3_rate_limit_max_attempts— лимит запросов APIms3_customer_token_ttl— время жизни токена клиента
Клиенты (новая сущность):
ms3_customer_auto_register_on_order— авторегистрация при заказеms3_customer_auto_login_on_order— автовход после оформления заказа (не только после регистрации)ms3_customer_auto_login_after_register— автовход после регистрацииms3_customer_require_email_verification— верификация emailms3_customer_sync_enabled— синхронизация с modUser
Валюта:
ms3_currency_symbol— символ валюты (₽, $, €)ms3_currency_position— позиция символа (before/after)
REST API
Точка входа
// miniShop2 — единый action.php
/assets/components/minishop2/action.php
// MiniShop3 — раздельные endpoint'ы
/assets/components/minishop3/connector.php // Manager API (сессия MODX)
/assets/components/minishop3/api.php // Web API: ?route=/api/v1/...Manager API обслуживает Vue-админку (заказы, клиенты, утилиты). Processors в core/components/minishop3/src/Processors/ остаются для ExtJS-панелей ресурса (категория, товар). Кастомные web-маршруты: core/config/ms3_routes_web.custom.php, фрагменты аддонов: core/config/ms3.routes.d/web/*.php.
Полная карта и тела запросов: REST API. Источник роутов: config/routes/web.php.
Web API (новое в MiniShop3)
Точка входа api.php, префикс /api/v1. На всю группу висят CORS, rate limit и ServiceCheck. Токен нужен для корзины, черновика заказа и ЛК; каталог и часть auth-эндпоинтов публичные.
# Корзина (гостевой токен)
POST /api/v1/cart/add
POST /api/v1/cart/remove
POST /api/v1/cart/change
POST /api/v1/cart/change-option
GET /api/v1/cart/get
POST /api/v1/cart/clean
# Заказ / checkout (гостевой токен)
GET /api/v1/order/get
POST /api/v1/order/add
POST /api/v1/order/set
POST /api/v1/order/remove
POST /api/v1/order/submit
POST /api/v1/order/clean
GET /api/v1/order/cost
GET /api/v1/order/cost/cart
GET /api/v1/order/cost/delivery
GET /api/v1/order/cost/payment
POST /api/v1/order/address/set
POST /api/v1/order/address/clean
GET /api/v1/order/delivery/validation-rules
GET /api/v1/order/delivery/required-fields
# Клиент: публичные
GET /api/v1/customer/token/get
POST /api/v1/customer/login
POST /api/v1/customer/register
POST /api/v1/customer/forgot-password
POST /api/v1/customer/reset-password
GET /api/v1/customer/email/verify
# Клиент: с токеном (ЛК)
POST /api/v1/customer/logout
POST /api/v1/customer/add
PUT /api/v1/customer/profile
POST /api/v1/customer/changeAddress
POST /api/v1/customer/email/resend-verification
GET /api/v1/customer/addresses
GET /api/v1/customer/addresses/{id}
POST /api/v1/customer/addresses
PUT /api/v1/customer/addresses/{id}
DELETE /api/v1/customer/addresses/{id}
PUT /api/v1/customer/addresses/{id}/set-default
GET /api/v1/customer/orders
GET /api/v1/customer/orders/{id}
POST /api/v1/customer/orders/{id}/cancel
# Каталог (без токена)
GET /api/v1/product/get/{id}
GET /api/v1/product/list
# Health
GET /api/v1/healthОтдельного GET /api/v1/order/payments нет. Список доставок и оплат на витрине отдаёт сниппет msOrder (серверный рендер). Черновик: GET /api/v1/order/get — только поля заказа/адреса (delivery_id, payment_id, address_*).
Авторизация API
// miniShop2 — без токена
$.post('/assets/components/minishop2/action.php', {
action: 'cart/add',
id: 123
});
// MiniShop3 — сначала токен, потом запросы с credentials
const base = '/assets/components/minishop3/api.php';
await fetch(`${base}?route=/api/v1/customer/token/get`, {
credentials: 'include'
});
await fetch(`${base}?route=/api/v1/cart/add`, {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ id: 123, count: 1 })
});
// Headless / мобильный клиент без cookie:
await fetch(`${base}?route=/api/v1/cart/add`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token
},
body: JSON.stringify({ id: 123, count: 1 })
});Порядок разрешения токена на сервере (TokenMiddleware): Authorization: Bearer → заголовок MS3TOKEN (legacy) → cookie / ms3_token в запросе. На витрине с 1.6 токен обычно живёт в httpOnly cookie ms3_token.
JavaScript API
Глобальный объект
// miniShop2
miniShop2.Cart.add(123);
miniShop2.Order.submit();
miniShop2Config.actionUrl;
// MiniShop3
await ms3.cartAPI.add(123, 1);
await ms3.orderAPI.submit();
ms3Config.apiUrl;Callbacks → Hooks
// miniShop2 — callbacks
miniShop2.Callbacks.add('Cart.add.response.success', 'my_callback', function(response) {
console.log('Товар добавлен', response);
});
miniShop2.Callbacks.remove('Cart.add.response.success', 'my_callback');
// MiniShop3 — hooks
ms3Hooks.addHook('afterAddCart', async ({ response }) => {
console.log('Товар добавлен', response);
});Список hooks MiniShop3
| miniShop2 Callback | MiniShop3 Hook |
|---|---|
Cart.add.before | beforeAddCart |
Cart.add.response.success | afterAddCart |
Cart.remove.response.success | afterRemoveCart |
Cart.change.response.success | afterChangeCart |
Cart.change-option.response.success | afterChangeOptionCart |
Order.submit.before | beforeSubmitOrder |
Order.submit.response.success | afterSubmitOrder |
После AJAX-запросов срабатывает hook afterSendRequest, который по умолчанию вызывает ms3.cartUI.init() для обновления UI корзины.
Data-атрибуты
<!-- miniShop2 -->
<form class="ms2_form" method="post">
<button type="submit" name="ms2_action" value="cart/add">
В корзину
</button>
</form>
<!-- MiniShop3 — декларативный подход -->
<button type="button"
data-ms-action="cart/add"
data-id="123"
data-count="1">
В корзину
</button>События плагинов
Большинство событий сохранили свои имена, но изменились передаваемые параметры:
// miniShop2
switch ($modx->event->name) {
case 'msOnBeforeAddToCart':
$cart = $scriptProperties['cart']; // Класс msCartHandler
break;
}
// MiniShop3
switch ($modx->event->name) {
case 'msOnBeforeAddToCart':
$cart = $scriptProperties['cart']; // MiniShop3\Controllers\Cart\Cart
break;
}Новые события MiniShop3
msOnCustomerCreate— создание клиентаmsOnCustomerUpdate— обновление клиентаmsOnCustomerLogin— вход клиентаmsOnBeforeAPIRequest— перед API запросомmsOnAfterAPIRequest— после API запроса
Сниппеты
Имена сниппетов (совместимость сохранена)
Все сниппеты сохранили свои имена:
msProductsmsCartmsOrdermsGetOrdermsGallerymsOptionsmsProductOptions
Новые сниппеты
msCustomer— личный кабинет клиентаmsOrderTotal— итоги заказа (замена msMiniCart)
msMiniCart → msOrderTotal
Параметр formatPrices удалён. Числовые плейсхолдеры — float, для вывода используйте *_formatted.
{* miniShop2 *}
{'!msMiniCart' | snippet}
{* MiniShop3 — чанк tpl.msOrderTotal по умолчанию *}
{'!msOrderTotal' | snippet}
{* или массив для своей разметки *}
{set $cart = '!msOrderTotal' | snippet : ['return' => 'data']}
<a href="{'ms3_cart_page_id' | option | url}">
{$cart.total_positions} на {$cart.total_cost_formatted}
</a>Плейсхолдеры цен
| miniShop2 | MiniShop3 |
|---|---|
{$product.price} часто уже с валютой | {$product.price} — float, {$product.price_formatted} — строка |
formatPrices=1 у сниппетов | Удалено. Всегда float + *_formatted |
Чанки
Имена чанков изменены для консистентности:
| miniShop2 | MiniShop3 |
|---|---|
tpl.msProducts.row | tpl.msProducts.row (без изменений) |
tpl.msCart | tpl.msCart (без изменений) |
tpl.msOrder | tpl.msOrder (без изменений) |
tpl.msMiniCart | tpl.msOrderTotal |
| — | tpl.msCustomer.profile (новый) |
| — | tpl.msCustomer.orders (новый) |
Модель данных
Новая сущность: msCustomer
MiniShop3 вводит отдельную сущность для клиентов магазина:
// miniShop2 — клиент = modUser
$user = $modx->getObject('modUser', $userId);
$profile = $user->getOne('Profile');
$address = $profile->get('address');
// MiniShop3 — отдельная сущность msCustomer
use MiniShop3\Model\msCustomer;
use MiniShop3\Model\msCustomerAddress;
$customer = $modx->getObject(msCustomer::class, ['email' => $email]);
$addresses = $customer->getMany('Addresses');
// Связь с modUser (опционально, ms3_customer_sync_enabled)
$modUser = $customer->getOne('User');Покупатель авторизуется через msCustomer и cookie ms3_token, не через стандартный modUser Login (если не включена синхронизация).
Адреса клиентов
// miniShop2 — адрес в msOrderAddress (только для заказа)
$orderAddress = $order->getOne('Address');
// MiniShop3 — сохранённые адреса клиента
$addresses = $customer->getMany('Addresses');
foreach ($addresses as $address) {
echo $address->get('city') . ', ' . $address->get('street');
}Миграция с miniShop2
Это runbook по данным и коду. Параллельный MS2 и MS3 на одной БД не предполагается: сначала MODX 3, потом MS3, потом перенос.
Шаг 1: MODX 3
Обновите сайт до MODX 3.x. MS3 на MODX 2 не ставится.
Шаг 2: Бэкап
Снимите дамп БД и файлов. Зафиксируйте ID категорий, товаров, статусов, доставок и оплат MS2.
Шаг 3: Установите MiniShop3
Через менеджер пакетов или транспорт с GitHub Releases. Дождитесь Phinx-миграций.
Шаг 4: Данные каталога и заказов
Готового «одной кнопкой» мигратора MS2→MS3 в пакете нет. Типовой путь:
- Экспорт товаров/категорий в CSV (или свой скрипт по таблицам
ms2_*). - Импорт в MS3 через Утилиты → Импорт или API.
- Опции: ключи
option_*, группы после 1.11 живут вmsOptionGroup(неmodCategory). - Заказы и покупателей переносите отдельным скриптом или оставьте архив MS2 read-only.
Сверьте class_key ресурсов: категории msCategory, товары msProduct.
Шаг 5: Системные настройки
Ключи ms2_* в MS3 не читаются. Заведите ms3_* (page_id, статусы, валюта). Старые значения из MS2 перенесите вручную.
Шаг 6: JavaScript витрины
// Было
miniShop2.Cart.add(id);
// Стало
await ms3.cartAPI.add(id, 1);Шаг 7: Плагины
Перепишите подписки на события MS3 (имена и сигнатуры отличаются). См. События.
Шаг 8: Чанки и плейсхолдеры
- Цены: сырые float +
*_formatted(с 1.11, breaking). УберитеformatPricesиз вызовов. - Контакты:
first_name/last_name, неreceiver. - Комментарий заказа:
order_comment. Полеcomment— у адреса. - Остаток товара:
stock(в CSV-импортеremains— alias). - Корзина: ключ позиции
product_key, неkey. - Доставка/оплата в форме:
delivery_id/payment_id. - Опции:
group_nameвместо MS2category_name. Группы опций —msOptionGroup, неmodCategory. - Превью товара: поле
preview_file_idвmsProductData(галерея «Сделать превью»), не толькоthumb/image. - Доп. категории:
msCategoryMemberи scopeCategoryProductScopeуmsProducts. - Корзина на thanks:
msCartпо умолчанию не скрывается на?msorder=. Для старого поведения —hideOnThanks=1.msOrderна thanks всегда пустой.
<!-- Было -->
<form class="ms2_form">
<button name="ms2_action" value="cart/add">
<!-- Стало -->
<button data-ms-action="cart/add" data-id="{$id}">Шаг 9: Проверка
- Каталог и карточка товара.
- Корзина → оформление → thanks.
- ЛК: вход, адреса, заказы.
- Менеджер: заказы, клиенты, опции.
Админка
| Область | miniShop2 | MiniShop3 |
|---|---|---|
| Заказы, клиенты, уведомления, настройки | ExtJS | Vue 3 + PrimeVue без Ext-обёртки (Manager API) |
| Редактор категории/товара в дереве | ExtJS | ExtJS shell + Vue вкладки (в т.ч. Категории/Связи) |
| Колонки таблиц | Системные настройки ms2_*_grid_fields | Утилиты → Поля таблиц (ms3_grid_fields) |
События плагинов из Vue CRUD (заказы, клиенты) не стреляют так же, как при изменении через processors ресурса. Для кастомизации админки ориентируйтесь на События и Manager API.
Обратная совместимость
MiniShop3 сохраняет совместимость на уровне:
✅ Совместимо:
- Имена сниппетов
- Основные параметры сниппетов
- Большинство событий плагинов (с новыми сигнатурами params)
❌ Несовместимо / переименовано:
- Системные настройки (
ms2_→ms3_) - JavaScript API (
miniShop2→ms3/orderAPI/ hooks) - PHP-классы (namespaces)
- Точки входа API (
action.php→api.php) - Ряд плейсхолдеров (
receiver→first_name/last_name,commentзаказа →order_comment,remains→stock)
