Документация

Помощник $shop: списки магазина

Категории, страницы, подборки товаров, бренды, метки, отзывы и корзина — прямо из шаблона, без подготовки данных.

На этой странице

$shop — помощник, который есть в каждом шаблоне. Через него тема берёт то, что ей нужно, не дожидаясь, пока данные передадут: категории для меню, подборки товаров на главную, бренды, метки, отзывы, состояние корзины.

Помощник живёт один на запрос и запоминает результаты — повторный вызов с теми же параметрами в базу не ходит. Звать {$shop->categories()} в шапке, в меню каталога и в плитках на главной не накладно.

Сводка

Метод Что отдаёт
contacts() контакты магазина
locales() языки витрины со ссылками на текущую страницу
categories($parentId, $depth, $media) дерево категорий
pages($parentId) меню страниц, созданных владельцем
products($code, $preset, $limit, $sort) карточки товаров подборки
tags($limit) метки каталога
brands($limit) бренды с логотипами
reviews($limit) последние одобренные отзывы
accessories($productId) группы аксессуаров товара
cartCount() число позиций в корзине
cartTotal() сумма корзины
cartPath() куда ведёт значок корзины
isOnepageCheckout() включено ли оформление в один шаг
favoritesCount() число товаров в избранном
compareCount() число товаров в сравнении
inCart() что уже лежит в корзине
cartItem($productId, $skuId) позиция корзины с этим товаром
inCartQty($productId, $skuId) сколько этого товара в корзине
cardVariantsEnabled() показывает ли тема варианты покупки на карточках

Категории

{$shop->categories()}                    верхний уровень
{$shop->categories(null, 2)}             два уровня
{$shop->categories(null, 2, true)}       два уровня, с картинками
{$shop->categories($category.id, 1)}     подкатегории текущей
Параметр По умолчанию Что задаёт
parentId пусто корень поддерева; пусто — верхний уровень
depth 1 на сколько уровней вглубь
media нет добавить ли картинки

Поля узла: id, name, initial (первая буква — для плитки без картинки), slug, url, children. С media добавляются logo, icon, cover — либо адрес файла, либо пусто.

{foreach $shop->categories(null, 2, true) as $cat}
    <a href="{$cat.url}">
        {if $cat.icon}<img src="{$cat.icon}" alt="">{/if}
        {$cat.name}
    </a>
    {foreach $cat.children as $sub}
        <a href="{$sub.url}">{$sub.name}</a>
    {/foreach}
{/foreach}

Выключенные категории не приходят.

Информационные страницы

{foreach $shop->pages() as $page}
    <a href="{$page.url}">{$page.title}</a>
{/foreach}

Поля: id, slug, title, url. Выключенные не приходят, порядок — как задан в панели. Страниц нет — пустой список.

Подборки товаров

Два способа получить список товаров на главную.

По коду подборки, заведённой владельцем:

{$shop->products('new-arrivals', null, 12)}

Код задаётся в панели, в разделе подборок. Подборки с таким кодом нет или она выключена — пустой список. Это основной способ: владелец сам решает, что показывать, не трогая шаблон.

По готовому набору правил:

{$shop->products(null, 'discount', 8)}
Набор Что отбирает
new добавленные за последние 30 дней
discount только со скидкой
low_stock остаток от 1 до 5
random случайные
bestsellers по продажам
popular по просмотрам
top_rated по оценкам

Неизвестное имя молча превращается в new.

⚠️ Честно про три последних: bestsellers, popular и top_rated сейчас сортируют по новизне. Товары придут правильные, порядок — как у новинок. Сортировка по продажам, просмотрам и оценкам объявлена, но пока не сделана.

Возвращаются полные карточки товара — тот же набор полей, что у товаров категории. Он описан на странице Данные товара.

Метки и бренды

{foreach $shop->tags(20) as $tag}<a href="{$tag.url}">{$tag.name}</a>{/foreach}

{foreach $shop->brands(30) as $brand}
    <a href="{$brand.url}">
        {if $brand.logo}<img src="{$brand.logo}" alt="{$brand.name}">{else}{$brand.name}{/if}
    </a>
{/foreach}

Метки: id, slug, name, url, по алфавиту. Бренды: то же плюс logo. Логотип берётся уменьшенным, при отсутствии уменьшенной копии — оригинал. По умолчанию — 50 штук.

Отзывы

{foreach $shop->reviews(6) as $r}
    <blockquote>
        <p>{$r.text}</p>
        <cite>{$r.name}, <a href="{$r.product_url}">{$r.product}</a>, {$r.date}</cite>
    </blockquote>
{/foreach}

Последние одобренные отзывы о товарах — для блока на главной. Поля: name, initial, product, product_url (ведёт на #, если товар удалён), rating, text, date в формате дд.мм.

Аксессуары

{foreach $shop->accessories($product.id) as $group}
    <h3>{$group.title}</h3>
    {foreach $group.items as $item}
        <label>
            <input type="checkbox" value="{$item.sku_id}">
            <img src="{$item.image}" alt="">
            {$item.name}
            {money amount=$item.final_price currency=$storefront.currency}
        </label>
    {/foreach}
{/foreach}

Группа: id, title, multiple (можно выбрать несколько), items. Аксессуар: product_id, sku_id, name, url, image, in_stock, is_available, discount (процент группы), price, final_price (со скидкой группы), compare_price, default_quantity, unit, qty_step, qty_min, is_default.

Аксессуары выключены в настройках — пустой список. Группа, в которой не осталось ни одного доступного товара, не приходит вовсе.

На странице товара то же самое уже лежит в $product.accessories — отдельно звать не нужно. И помните: живой пересчёт комплекта делает сам магазин, тема только размечает блок — см. Атрибуты data-*.

Языки

{foreach $shop->locales() as $l}
    <a href="{$l.url}"{if $l.active} class="is-active"{/if}>{$l.short}</a>
{/foreach}

Поля: code (ru), prefix (uk или ua — задаётся в справочнике языков), name (название на родном языке), short (префикс заглавными), active, url.

urlпуть текущей страницы на этом языке, а не полный адрес: переключатель обязан остаться на том домене, по которому покупатель зашёл.

Корзина

<a href="{$shop->cartPath()}">
    Корзина
    <span data-badge="cart">{$shop->cartCount()}</span>
    <span data-cart-total-header>{money amount=$shop->cartTotal() currency=$storefront.currency precision=0}</span>
</a>

cartPath() отдаёт /checkout, если у витрины включено оформление в один шаг, иначе /cart — значок корзины должен вести туда, куда владелец настроил.

cartTotal() берёт готовый итог корзины, а не складывает позиции: своя формула в шапке разъедется со страницей корзины и с оформлением. Корзины ещё нет — ноль.

favoritesCount() и compareCount() — то же самое для избранного и сравнения.

Все четыре дёшевы, зовите откуда угодно.

Что уже в корзине

inCart(), cartItem(), inCartQty() отвечают, лежит ли товар в корзине. Позиция: item_id, product_id, sku_id, qty, qty_text, unit, packs, lines. Товар лежит несколькими вариантами — количества складываются, а item_id, sku_id и packs приходят пустыми: позиция уже не одна.

⚠️ Обычный путь другой. Эти методы помечают страницу как личную и выключают для неё общий кэш витрины: страница перестаёт отдаваться из кэша и собирается заново каждому покупателю.

Штатный способ показать «уже в корзине» — атрибут на карточке:

<article class="card" data-in-cart="{$product.id}">
    …
    <span class="card__in-cart">
        В корзине <b data-in-cart-qty></b> <span data-in-cart-unit></span>
    </span>
</article>

Количество проставит магазин уже в браузере, и страница останется кэшируемой. Подробности — на странице Атрибуты data-*.

Варианты покупки на карточках

cardVariantsEnabled() читает настройку темы card_variants — показывать ли выбор цвета и размера прямо в списке товаров.

⚠️ Настройка обязана быть объявлена в theme.json. У базовой темы её нет, поэтому базовая тема варианты на карточках не показывает. Пока поля нет в схеме, магазин не тратит запросы на подготовку вариантов — и правка шаблона ничего не изменит. Как объявить поле, написано на странице Файл theme.json.

Чего у $shop нет

Помощник отдаёт списки и состояние, а не всё подряд. Заказов покупателя, произвольной выборки товаров по своим условиям, чужих сущностей магазина в нём нет — и обойти это, обратившись из шаблона к базе, нельзя: шаблон работает в песочнице.

Если теме нужны данные, которых у $shop нет, — это задача дополнения. Оно считает своё и выводит через точку встраивания или собственный тег.

Обновлено 19 августа 2026