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

Помічник $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