Помічник $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 немає, — це завдання доповнення. Воно
рахує своє і виводить через точку вбудовування або
власний тег.