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

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

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

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

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

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

Сводка

Метод Что отдаёт
contacts() контакты магазина
locales() языки витрины со ссылками на текущую страницу
categories($parentId, $depth, $media) дерево категорий
pages($parentId) меню страниц, созданных владельцем
products($code, $preset, $limit, $sort) карточки товаров подборки
catalogProducts($categoryId, $depth, $limit) товары в порядке каталога — для прайс-листа
tags($limit) метки каталога
brands($limit) бренды с логотипами
reviews($limit) последние одобренные отзывы
blogs($full) блоги витрины со всеми полями; true — и с описаниями
blogCategories($blog, $full) рубрики блога деревом — для меню блога
posts($options) записи блогов по условиям
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 сейчас сортируют по новизне. Товары придут правильные, порядок — как у новинок. Сортировка по продажам, просмотрам и оценкам объявлена, но пока не сделана.

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

Товары в порядке каталога

Для прайс-листа и похожих страниц: товары идут раздел за разделом, как разделы стоят в меню, а внутри раздела — в порядке, который вы расставили в панели. Новые товары попадают в список сами.

{$shop->catalogProducts()}                   верхний уровень, до 100 товаров
{$shop->catalogProducts(null, 2, 500)}       с подразделами, до 500 товаров
{$shop->catalogProducts($category.id, 3)}    ветка одного раздела на три уровня
Параметр По умолчанию Что задаёт
categoryId пусто с какого раздела начать; пусто — весь каталог
depth 1 сколько уровней раскрыть: 1 — только сам раздел, 2 — с подразделами
limit 100 сколько товаров отдать, не больше 1000

Товар из нескольких разделов приходит один раз — там, где встретился первым. Скрытые разделы и разделы «по условиям» не участвуют. Отсутствующие товары витрина прячет или опускает в конец раздела — по своей настройке, как в остальных списках.

Строки лёгкие, без фото и характеристик, — их хватает на таблицу «название, цена, Купить» даже на большом каталоге. Поля: id, name, url, sku, sku_id (основной вариант), price, compare_price, unit, in_stock, is_available, preorder.

{foreach $shop->catalogProducts(null, 2, 500) as $p}
    <div>
        {$p.name|escape} — {money amount=$p.price currency=$storefront.currency}
        {if $p.is_available}
            <button type="button" data-add="{$p.id}" data-sku="{$p.sku_id}">Купить</button>
        {/if}
    </div>
{/foreach}

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

{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->storeReviews(20) as $r}
    <blockquote>
        <p>{$r.text}</p>
        <cite>{$r.name}{if $r.product}, {$r.product}{/if}, {$r.date}</cite>
    </blockquote>
{/foreach}

Отзывы о покупке в целом, а не о товаре: о доставке, упаковке, работе менеджера. Список отдаёт одобренные отзывы этой витрины и общие для всех витрин, новые первыми, не больше 200. Поля: id, name, initial, rating, title, text, date в формате дд.мм.гггг, date_long — дата словами на языке страницы («1 апреля 2025»), date_iso — та же дата для разметки поисковиков (datePublished), и product — подпись покупателя «что брал». Это текст, а не ссылка: такого товара в каталоге может уже не быть.

В replies лежат одобренные ответы под отзывом: name, is_admin (ответил магазин), text, date_long. Текст отзыва и ответа выводите с экранированием: {$r.text|escape|nl2br nofilter}.

Итог для шапки блока даёт $shop->storeReviewsSummary(): count — сколько всего одобренных отзывов о магазине, average — средняя оценка с одним знаком после запятой. Считается по всем отзывам, а не только по показанным:

{$s = $shop->storeReviewsSummary()}
{if $s.count}{$s.average} на основе {$s.count} отзывов{/if}

Чтобы покупатель оставил отзыв о магазине, форма отправляет на CartiX.urls.reviewSubmit поле kind=store вместо product_id и, по желанию, product_label с подписью товара. Остальные поля — как у отзыва о товаре.

Статьи

{foreach $shop->blogs() as $blog}
    <a href="{$blog.url}">{$blog.name}</a>
{/foreach}

blogs() отдаёт блоги этой витрины с теми же полями, что у blog на странице блога (id, slug, name, summary, url, icon, cover, cover_thumb, banner), и rss — адрес ленты подписки. Полное описание description приходит по $shop->blogs(true). blogCategories('news') отдаёт рубрики блога деревом, с теми же полями, что categories на странице блога; блог называют номером или адресом, blogCategories('news', true) добавляет описания. Почему описания приходят по запросу — на странице Данные страниц. Ссылку на статьи ставят в шапку и в подвал; блогов может быть несколько — например «Новости» и «Гайды».

{$shop->posts(4)}                                   четыре свежие записи
{$shop->posts(['blog' => 'news', 'limit' => 3])}    только из блога «Новости»
{$shop->posts(['category' => 'guides', 'sort' => 'random', 'limit' => 3])}

posts() отдаёт карточки записей — те же, что приходят в ленту блога. Условия подборки перечисляются набором: blog, category, with_children, tag, ids, exclude, limit, offset, sort, pinned, full. Блог, рубрика и метка называются номером или адресом. Что каждое из условий означает, написано на странице Теги магазина — помощник и тег {posts} понимают их одинаково.

Чаще всего проще поставить тег: {posts limit=3} сразу нарисует карточки шаблоном темы, а {posts limit=3 assign=fresh} положит записи в переменную. Помощник нужен там, где записи уходят в другой шаблон параметром:

{include file="blocks/news.html" posts=$shop->posts(3) title='storefront.news'}

Аксессуары

{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, count (сколько штук предложенного варианта есть в наличии, с ядра 2.53.4).

Остаток аксессуара count считается так же, как остаток на странице товара: без товара, отложенного под чужие заказы, а минус показывается нулём. Если остаток у товара не ведётся, count пустой — товар есть всегда, и число лучше не показывать вовсе:

{if $item.count !== null}{$item.count} {$item.unit}{/if}

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

На странице товара то же самое уже лежит в $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 нет, — это задача дополнения. Оно считает своё и выводит через точку встраивания или собственный тег.

Обновлено 13 октября 2026