
Каталог товаров
Как вывести варианты в карточках каталога и добавлять в корзину выбранный вариант.
Подключение вариантов
{'msProducts' | snippet : [
'parents' => 0,
'usePackages' => 'ms3Variants',
'tpl' => 'ms3_products_row_variants'
]}С параметром usePackages каждый товар получает плейсхолдеры вариантов. В каталог попадают только активные варианты в порядке position.
Значение ms3Variants — с учётом регистра
При ms3variants или без usePackages плейсхолдеров нет: условие {if $has_variants} ложно, и карточка продаёт товар без варианта.
Второй вызов msProducts на той же странице получает плейсхолдеры вариантов от первого, даже без usePackages. У товаров из первого списка — их варианты, у остальных — пустые значения.
Модуль ProductCardUI
Для чанка с вариантами нужен модуль ProductCardUI.js из ms3Variants вместо стандартного модуля MiniShop3. Без замены у списка вариантов нет обработчика: виден и покупается только первый вариант, а состояние корзины пишется в формы первого варианта.
Замените в системной настройке ms3_frontend_assets (JSON-массив путей) элемент со стандартным модулем на путь к модулю ms3Variants:
| Было | Стало |
|---|---|
"[[+jsUrl]]web\/ui\/ProductCardUI.js" | "/assets/components/ms3variants/js/web/ProductCardUI.js" |
- В настройке слеши стандартного пути экранированы (
web\/ui\/) — ищите строку в таком виде. - Путь к модулю ms3Variants указан от корня сайта. Если MODX установлен в подкаталог, поправьте его.
Чем модуль ms3Variants отличается от стандартного:
- селекторы карточек прописаны в нём жёстко (
.ms3-product-card,.qty-input), настройкиms3Config.selectorsне учитываются; - кнопки «−/+» обрабатывает он сам во всех карточках, включая карточки без вариантов;
- хуки MiniShop3 при «−/+» называются
beforeChangeCart/afterChangeCartвместоbeforeQuantityChange/afterQuantityChange. Обработчики подключаются черезms3.hooks.addHook('beforeChangeCart', fn);fnполучает{productKey, count}(вafterChangeCart— ещёresponse),cancel = trueв этом объекте отменяет изменение; - после «−/+» в карточке блоки корзины, выведенные сервером (например, мини-корзина в чанке), показывают прежнее содержимое до перезагрузки страницы;
- в карточках, добавленных на страницу после загрузки (подгрузка, фильтры), выбор варианта не работает, и штатного способа подключить их нет.
Обновление MiniShop3 эту настройку не меняет: модуль ms3Variants остаётся, а исправления стандартного модуля до карточек не доходят.
Перед удалением ms3Variants верните стандартный модуль
При удалении пакета файл модуля удаляется, а настройка ms3_frontend_assets остаётся прежней. Корзина перестанет работать на всём сайте, в консоли браузера будет ReferenceError: ProductCardUI is not defined. Сначала верните в настройку "[[+jsUrl]]web/ui/ProductCardUI.js", затем удаляйте пакет.
Чанк ms3_products_row_variants
Стандартный чанк ms3_products_row_variants — готовая карточка. Свой чанк работает с модулем ProductCardUI, если в нём есть эта разметка:
| Элемент | Разметка | Что делает |
|---|---|---|
| Карточка | class="ms3-product-card", data-product-id="{$id}" | Без них модуль карточку не видит |
| Изображение | <img class="product-image"> — первый такой элемент в карточке | Меняется при выборе варианта |
| Обёртка | .ms3v-catalog | Внутри — список и блоки вариантов |
| Список | <select class="ms3v-select">, у пунктов value="{$variant.id}" | Выбор варианта |
| Блок варианта | .ms3v-variant-block, data-variant-id, data-variant-image | Показывается блок выбранного варианта |
| Форма добавления | .ms3_form.ms3v-add-form, data-cart-state="add", скрытое options = {"_variant_id": ID} | Добавляет вариант в корзину |
| Форма изменения | .ms3v-change-form, data-cart-state="change", поля product_key и count (.qty-input) — оставьте пустыми, модуль заполнит их данными позиции корзины, кнопки .qty-btn с data-action="decrease" / "increase" | Показывается, когда вариант в корзине |
Когда вариант в корзине, модуль показывает его форму изменения и добавляет « ✓» к пункту списка.
Если data-variant-image пустой, у карточки остаётся изображение предыдущего варианта. Пишите data-variant-image="{$variant.small ?: $variant.image_url}": эскиз small есть, только если его имя передано в параметре msProducts includeThumbs (например, 'includeThumbs' => 'small', имена — из настроек источника файлов товаров), иначе берётся оригинал.
Поле options с JSON имеет приоритет над полями options[...], поэтому стандартные опции MiniShop3 в форме варианта не передаются — см. Страница товара.
У товара без вариантов в стандартном чанке нет формы изменения: после добавления в корзину «−/+» в его карточке не появляются. В стандартном чанке MiniShop3 ms3_products_row они есть.
Минимальный чанк
Карточка без модуля ProductCardUI: список вариантов внутри формы меняет _variant_id в поле options.
<div>
{$pagetitle}
<form method="post" class="ms3_form">
<input type="hidden" name="id" value="{$id}">
<input type="hidden" name="ms3_action" value="cart/add">
{if $has_variants}
<input type="hidden" name="options" value='{["_variant_id" => $variants[0].id] | json_encode}'>
<select onchange="this.form.elements.options.value = JSON.stringify({_variant_id: this.value})">
{foreach $variants as $v}
<option value="{$v.id}">{$v.options_array | join : ', '} — {$v.price}</option>
{/foreach}
</select>
{/if}
<button type="submit">В корзину</button>
</form>
</div>У списка нет атрибута name, поэтому сам он в форму не отправляется. Закончившиеся варианты в этом чанке не блокируются, состояние «в корзине» не показывается.
Плейсхолдеры товара
| Плейсхолдер | Тип | Описание |
|---|---|---|
{$has_variants} | bool | Есть ли у товара варианты для вывода |
{$variants_count} | int | Количество вариантов |
{$variants} | array | Варианты — поля в разделе Поля варианта |
{$variants_json} | string | Те же данные JSON-массивом. Структура отличается от {$variants_json} сниппета msProductVariants |
У товара без вариантов: {$has_variants} = false, {$variants_count} = 0, {$variants} — пустой массив, {$variants_json} — [].
Поля варианта
[
'id' => 1,
'product_id' => 42,
'sku' => 'ABC-123-red', // может быть пустым
'price' => 1500.00,
'old_price' => 2000.00, // может быть пустым
'count' => 10,
'weight' => 0.5,
'active' => true,
'position' => 0,
'in_stock' => true,
'file_id' => 7, // может быть пустым
'image_url' => '/assets/images/42/product.jpg', // пусто без изображения
'small' => '/assets/images/42/small/product.jpg', // только с includeThumbs
'options' => [['key' => 'color', 'value' => 'Красный']],
'options_array' => ['color' => 'Красный'],
'created_at' => '2026-01-01 12:00:00',
'updated_at' => '2026-01-01 12:00:00',
]price — цена из записи варианта. Модификаторы цены MiniShop3 (событие msOnGetProductPrice, скидки групп покупателей) к ней не применяются.
Фильтрация по наличию
Зависит от настроек ms3variants_show_out_of_stock и ms3variants_check_stock:
- Показ закончившихся = Да (по умолчанию) — показываются все активные варианты. В стандартном чанке кнопка закончившегося варианта заблокирована, только если включён контроль остатков. При
ms3variants_check_stock= Нет вариант с остатком 0 считается «в наличии» и покупается. - Показ закончившихся = Нет — скрыты варианты с остатком 0. При
ms3variants_check_stock= Нет такие варианты считаются «в наличии», и скрывать нечего.
Товар, у которого закончились все варианты, покупается по базовой цене
При показе закончившихся = Нет у такого товара {$has_variants} = false. Чанк рисует обычную форму без варианта, и товар уходит в корзину по базовой цене, без проверки остатков вариантов.
Производительность
Не указывайте usePackages там, где варианты не выводятся. Запросы на страницу:
- один запрос — варианты всех товаров;
- по запросу опций на каждый вариант;
- по запросу файла на каждый вариант с изображением;
- ещё один запрос эскизов при
includeThumbs.
