Skip to content
MiniShop3
MiniShop3
Современный компонент интернет-магазина для MODX 3
  1. Компоненты
  2. MiniShop3
  3. Отличия от miniShop2

Отличия от miniShop2

Это руководство поможет разработчикам, знакомым с miniShop2, быстро освоить MiniShop3 и понять ключевые изменения.

Системные требования

ТребованиеminiShop2MiniShop3
MODX2.3+3.0.0+
PHP7.0+8.1+
MySQL5.5+5.7+ / MariaDB 10.3+
pdoTools2.x3.x

Архитектура

Пространства имён (Namespaces)

miniShop2 использовал классы без пространств имён. В MiniShop3 все классы организованы в namespace MiniShop3\:

php
// 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 для регистрации сервисов:

php
// 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 для версионирования миграций:

bash
# Запуск миграций
php vendor/bin/phinx migrate -c phinx.php

При установке компонента миграции выполняются автоматически.

Системные настройки

Все системные настройки переименованы с ms2_ на ms3_:

miniShop2MiniShop3
ms2_template_product_defaultms3_template_product_default
ms2_template_category_defaultms3_template_category_default
ms2_category_grid_fieldsУдалено. Колонки грида категории: Утилиты → Поля таблиц (ms3_grid_fields, grid_key=category-products) + Утилиты → Поля моделей
ms2_product_extra_fieldsms3_product_extra_fields
ms2_frontend_jsms3_frontend_assets
ms2_frontend_css(объединено в ms3_frontend_assets)
ms2_price_formatms3_price_format
ms2_weight_formatms3_weight_format

Новые настройки MiniShop3

MiniShop3 добавляет множество новых настроек:

API и безопасность:

  • ms3_cors_allowed_origins — разрешённые домены для CORS
  • ms3_api_debug — режим отладки API
  • ms3_rate_limit_max_attempts — лимит запросов API
  • ms3_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 — верификация email
  • ms3_customer_sync_enabled — синхронизация с modUser

Валюта:

  • ms3_currency_symbol — символ валюты (₽, $, €)
  • ms3_currency_position — позиция символа (before/after)

REST API

Точка входа

text
// 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-эндпоинтов публичные.

http
# Корзина (гостевой токен)
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

javascript
// 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

Глобальный объект

javascript
// 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

javascript
// 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 CallbackMiniShop3 Hook
Cart.add.beforebeforeAddCart
Cart.add.response.successafterAddCart
Cart.remove.response.successafterRemoveCart
Cart.change.response.successafterChangeCart
Cart.change-option.response.successafterChangeOptionCart
Order.submit.beforebeforeSubmitOrder
Order.submit.response.successafterSubmitOrder

После AJAX-запросов срабатывает hook afterSendRequest, который по умолчанию вызывает ms3.cartUI.init() для обновления UI корзины.

Data-атрибуты

html
<!-- 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>

События плагинов

Большинство событий сохранили свои имена, но изменились передаваемые параметры:

php
// 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 запроса

Сниппеты

Имена сниппетов (совместимость сохранена)

Все сниппеты сохранили свои имена:

  • msProducts
  • msCart
  • msOrder
  • msGetOrder
  • msGallery
  • msOptions
  • msProductOptions

Новые сниппеты

  • msCustomer — личный кабинет клиента
  • msOrderTotal — итоги заказа (замена msMiniCart)

msMiniCart → msOrderTotal

Параметр formatPrices удалён. Числовые плейсхолдеры — float, для вывода используйте *_formatted.

fenom
{* 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>

Плейсхолдеры цен

miniShop2MiniShop3
{$product.price} часто уже с валютой{$product.price} — float, {$product.price_formatted} — строка
formatPrices=1 у сниппетовУдалено. Всегда float + *_formatted

Чанки

Имена чанков изменены для консистентности:

miniShop2MiniShop3
tpl.msProducts.rowtpl.msProducts.row (без изменений)
tpl.msCarttpl.msCart (без изменений)
tpl.msOrdertpl.msOrder (без изменений)
tpl.msMiniCarttpl.msOrderTotal
tpl.msCustomer.profile (новый)
tpl.msCustomer.orders (новый)

Модель данных

Новая сущность: msCustomer

MiniShop3 вводит отдельную сущность для клиентов магазина:

php
// 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 (если не включена синхронизация).

Адреса клиентов

php
// 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 в пакете нет. Типовой путь:

  1. Экспорт товаров/категорий в CSV (или свой скрипт по таблицам ms2_*).
  2. Импорт в MS3 через Утилиты → Импорт или API.
  3. Опции: ключи option_*, группы после 1.11 живут в msOptionGroup (не modCategory).
  4. Заказы и покупателей переносите отдельным скриптом или оставьте архив MS2 read-only.

Сверьте class_key ресурсов: категории msCategory, товары msProduct.

Шаг 5: Системные настройки

Ключи ms2_* в MS3 не читаются. Заведите ms3_* (page_id, статусы, валюта). Старые значения из MS2 перенесите вручную.

Шаг 6: JavaScript витрины

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 вместо MS2 category_name. Группы опций — msOptionGroup, не modCategory.
  • Превью товара: поле preview_file_id в msProductData (галерея «Сделать превью»), не только thumb/image.
  • Доп. категории: msCategoryMember и scope CategoryProductScope у msProducts.
  • Корзина на thanks: msCart по умолчанию не скрывается на ?msorder=. Для старого поведения — hideOnThanks=1. msOrder на thanks всегда пустой.
html
<!-- Было -->
<form class="ms2_form">
    <button name="ms2_action" value="cart/add">

<!-- Стало -->
<button data-ms-action="cart/add" data-id="{$id}">

Шаг 9: Проверка

  1. Каталог и карточка товара.
  2. Корзина → оформление → thanks.
  3. ЛК: вход, адреса, заказы.
  4. Менеджер: заказы, клиенты, опции.

Админка

ОбластьminiShop2MiniShop3
Заказы, клиенты, уведомления, настройкиExtJSVue 3 + PrimeVue без Ext-обёртки (Manager API)
Редактор категории/товара в деревеExtJSExtJS shell + Vue вкладки (в т.ч. Категории/Связи)
Колонки таблицСистемные настройки ms2_*_grid_fieldsУтилиты → Поля таблиц (ms3_grid_fields)

События плагинов из Vue CRUD (заказы, клиенты) не стреляют так же, как при изменении через processors ресурса. Для кастомизации админки ориентируйтесь на События и Manager API.

Обратная совместимость

MiniShop3 сохраняет совместимость на уровне:

Совместимо:

  • Имена сниппетов
  • Основные параметры сниппетов
  • Большинство событий плагинов (с новыми сигнатурами params)

Несовместимо / переименовано:

  • Системные настройки (ms2_ms3_)
  • JavaScript API (miniShop2ms3 / orderAPI / hooks)
  • PHP-классы (namespaces)
  • Точки входа API (action.phpapi.php)
  • Ряд плейсхолдеров (receiverfirst_name/last_name, comment заказа → order_comment, remainsstock)