Теги шаблонів: довідник
Гроші, кількості, посилання, переклади, ескізи фотографій, підключення стилів і скриптів — усі теги з прикладами.
На цій сторінці
- Гроші
- Кількості
- Адреси розділів
- Написи теми
- Ескізи фотографій
- Записи блогів
- Блок «Новини» на головній
- Що є в запису
- Часті помилки
- Товари картками
- Блоки
- Форми з CRM
- Шаблон partials/crm-form.html
- Форми
- Друковані форми замовлення
- Друк одразу після натискання
- Документ іншою мовою
- Стилі і скрипти
- Місце для доповнень
- Подивитися, що прийшло в шаблон
- Теги доповнень
- Обробники значень
- Екранування
- Чого в темі немає
Крім звичайних підстановок, умов і циклів Smarty, темі доступні теги самого магазину. Нижче — усі вони.
| Тег | Для чого |
|---|---|
{money} |
сума з валютою |
{qty} |
дробова кількість |
{url} |
адреса розділу вітрини |
{trans} |
напис теми, зокрема з відмінюванням |
{thumb} |
ескіз фотографії потрібного розміру |
{posts} |
записи блогів: на головній, у підвалі, де завгодно |
{products} |
вибрані товари картками |
{content_block} |
блок, який власник пише один раз для багатьох сторінок |
{crm_form} |
форма на сайті з розділу «CRM → Форми» |
{csrf_field} {csrf_token} |
захист форми |
{captcha} |
перевірка «я не робот» |
{asset_css} {asset_js} |
підключити свій стиль або скрипт |
{assets_head} {assets_footer} |
вивести зібраний список файлів |
{hook} |
місце для доповнень |
{dump} {dumpc} |
подивитися, що прийшло в шаблон |
Гроші
{money amount=$product.price currency=$storefront.currency}
{money amount=$order.total currency=$order.currency}
{money amount=$shop->cartTotal() currency=$storefront.currency precision=0}
| Параметр | За замовчуванням | Що задає |
|---|---|---|
amount |
0 |
сума |
currency |
USD |
валюта |
locale |
поточна мова | як розділяти розряди і дробову частину |
precision |
як у валюті | скільки знаків після коми |
⚠️ Валюту передавайте завжди. За замовчуванням тег форматує суму доларами, і на тестовому магазині з валютою за замовчуванням помилку не видно — а в гривневому магазині ціна поїде з доларовим знаком.
precision=0 знадобиться там, де копійки ламають верстку: сума кошика в шапці,
підпис на картці.
Тег тільки форматує число. Рахувати в шаблоні не потрібно і не можна — чому, написано на сторінці Ціни, кількості і валюта.
Кількості
{qty value=$item.qty} {$item.unit} → 30,8 м²
Товари бувають дробовими: метри, кілограми, площа. Тег виводить число по-місцевому («30,8», а не «30.8»), відкидає хвостові нулі («2», а не «2,000») і не ставить розділювач розрядів — це саме число підставляється в поле введення кількості, а «1 200» браузер прочитав би як «1».
Адреси розділів
<a href="{url path='/search'}">Пошук</a>
<a href="{url path='/'}">На головну</a>
<a href="{url path='/cart' locale='uk'}">Кошик</a>
| Параметр | За замовчуванням | Що задає |
|---|---|---|
path |
/ |
шлях розділу |
locale |
поточна мова | мова, якою відкрити розділ |
absolute |
ні | повна адреса з доменом замість шляху |
Тег сам додає мовний префікс і приводить адресу до вигляду, прийнятого в магазині. Повна адреса потрібна тільки пошуковику — для посилань усередині сайту беріть звичайну.
Адреси товарів, категорій, брендів, міток і сторінок уже приходять готовими в
даних ($product.url, $cat.url). Склеювати їх руками не можна: схему адрес
обирає власник магазину. Подробиці — на сторінці
Адреси розділів і мови.
Написи теми
{trans key="shop.cart"}
{trans key="shop.products_count" count=$total}
{trans key="shop.hello" name=$customer.first_name}
Рядок береться з файлу теми locale/<мова>.json, а якщо ключа там немає — з
написів самого магазину. Не знайшлося ніде — виведеться сам ключ: сторінка не
падає, але у верстці висітиме shop.cart.
Усе, крім key, вважається підстановкою: {trans key="shop.hello" name="Іван"}
підставить ім'я замість :name у рядку.
Параметр count вмикає вибір форми за числом — «1 товар», «2 товари»,
«5 товарів». Як записати такий рядок, написано на сторінці
Тексти теми і мови.
Ескізи фотографій
<img src="{thumb src=$product.image size=320}" alt="{$product.name}">
<img src="{thumb src=$product.image size=120 type='crop'}" alt="">
type |
Що робить |
|---|---|
max (за замовчуванням) |
вписує картинку цілком у квадрат зі стороною size |
crop |
обрізає до квадрата size × size |
width |
задає ширину, висота рахується сама |
height |
задає висоту, ширина рахується сама |
Ескізи нарізаються на вимогу: перший покупець, який відкрив сторінку, чекає нарізку, далі файл віддає веб-сервер напряму. Якщо можливість вимкнена в налаштуваннях або файл лежить не на публічному диску, тег поверне вихідну адресу — картинка покажеться, просто в повному розмірі.
Записи блогів
Тег {posts} ставить статті блогу в будь-яке місце оформлення: на головну, у
підвал, на сторінку товару, у текст інформаційної сторінки. Працює він одним
із двох способів, і спосіб обирає параметр assign. Сплутати їх — найчастіша
помилка з цим тегом, тому спершу про них.
Спосіб перший: тег малює сам. Без assign тег одразу виводить картки
статей там, де стоїть:
{posts limit=3}
Кожен запис тег малює шаблоном partials/blog-card.html — тим самим, яким
тема малює стрічку блогу, — і загортає список у <div class="posts-embed">.
Якщо такого шаблону у вашій темі немає, картка береться з базової теми
default. Змінну тег при цьому не створює, і перебирати після нього в
{foreach} нічого.
Спосіб другий: тег віддає дані. З assign тег нічого не виводить, а
кладе список записів у змінну з указаним ім'ям. Розмітку ви пишете самі:
{posts limit=3 assign=freshPosts}
{foreach $freshPosts as $post}
<a href="{$post.url}">{$post.title}</a>
{/foreach}
Той самий список віддає помічник $shop->posts():
{assign var=freshPosts value=$shop->posts(3)}
{assign var=freshPosts value=$shop->posts(3, 'news')}
{assign var=freshPosts value=$shop->posts(['blog' => 'news', 'sort' => 'random', 'limit' => 3])}
Помічнику аргументи передаються за порядком: першим — скільки записів
потрібно, другим — адреса блогу. Решта умов передається набором у квадратних
дужках, з тими самими іменами, що й у тегу. Іменованих параметрів на кшталт
$shop->posts(limit=3) Smarty у виклику методу не розуміє й повідомляє про
помилку в шаблоні. Якщо записи потрібні лише в цьому ж шаблоні, простіше
поставити тег з assign.
Умови добірки в тегу й помічника однакові:
{posts limit=3} три свіжі записи картками
{posts blog="news" limit=4} лише з блогу «Новини»
{posts category="guides" sort="random" limit=3} упереміш із рубрики
{posts limit=4 assign=fresh} віддати записи в змінну
| Параметр | За замовчуванням | Що задає |
|---|---|---|
blog |
усі блоги вітрини | блог: номер або адреса; кілька — через кому |
category |
усі рубрики | рубрика: номер або адреса; кілька — через кому |
with_children |
так | чи брати записи вкладених рубрик |
full |
ні | віддати рубрики записів з повним описом |
tag |
усі мітки | мітка: номер або адреса; кілька — через кому |
ids |
— | номери записів; за sort за замовчуванням показуються саме в цьому порядку |
exclude |
— | номери записів, які показувати не треба |
limit |
4 |
скільки записів показати |
offset |
0 |
скільки перших записів пропустити |
sort |
feed |
у якому порядку (нижче) |
pinned |
— | true — лише закріплені записи |
assign |
— | ім'я змінної: тег віддає записи, а не розмітку |
template |
partials/blog-card.html |
свій шаблон картки; запис приходить у нього змінною $post |
class |
posts-embed |
клас обгортки |
sort |
Порядок |
|---|---|
feed |
як у стрічці блогу: закріплені зверху, далі від свіжих до старих |
new |
найсвіжіші |
old |
найстаріші |
popular |
які читають частіше |
random |
упереміш: щоразу свій набір |
title |
за абеткою |
Магазин показує лише те, що бачить покупець: чернетки, записи з архіву, записи
з майбутньою датою публікації та записи вимкненого блогу в добірку не
потрапляють. Беруться записи блогів цієї вітрини — блог сусідньої вітрини
не покажеться, навіть якщо написати його адресу. Вимкнена рубрика в умові
category не знайдеться, а якщо вимкнена головна рубрика самого запису, запис
потрапить у добірку, але без рубрики: поле category у нього буде порожнім.
Записів за умовами не знайшлося — тег без assign не виводить нічого, і {if}
навколо нього не потрібен. З assign у змінну потрапить порожній список, тому
свою розмітку загортайте в {if $freshPosts}, щоб на сторінці не лишився
порожній заголовок блоку.
Блог, рубрику й мітку називають як зручніше — адресою або номером:
{posts blog="news" limit=3} адреса видно в посиланні: /news/
{posts blog=3 limit=3} номер написано в панелі, поряд із готовим кодом
Адреса наочніша в шаблоні, номер надійніший: посилання на блог власник може змінити, а номер залишиться тим самим. Номер блогу написано на його сторінці в розділі «Статті», номер рубрики — на сторінці рубрики, там само стоїть і готовий рядок із кнопкою «Копіювати».
Якщо на вітрині немає жодного ввімкненого блогу, тег без assign нічого не
виводить, а з assign віддає порожній список.
Блок «Новини» на головній
Так влаштований блок свіжих статей у базовій темі, файл blocks/news.html.
Блок бере три останні записи з блогів вітрини, ставить поруч посилання на всі
новини й не показується взагалі, доки статей немає. Його можна взяти за основу
свого блоку: класи й розмітку замініть своїми, а звернення до записів
залиште як є.
{posts limit=3 assign=freshPosts}
{assign var=blogs value=$shop->blogs()}
{if $freshPosts}
<section class="news">
<div class="section-head">
<h2 class="section-title">{trans key="storefront.news"}</h2>
{if $blogs}
<a class="section-more" href="{$blogs[0].url}">
{trans key="storefront.all_news"}
<i class="fa-solid fa-arrow-right"></i>
</a>
{/if}
</div>
<div class="news__grid">
{foreach $freshPosts as $p}
<article class="news-card">
<a class="news-card__cover" href="{$p.url}">
{if $p.category}<span class="news-card__cat">{$p.category.name}</span>{/if}
{if $p.cover_thumb}
<img class="news-card__img" src="{$p.cover_thumb}" alt="{$p.title|escape}" loading="lazy">
{else}
<i class="fa-regular fa-newspaper news-card__ico"></i>
{/if}
</a>
<div class="news-card__body">
{if $p.date_label}
<span class="news-card__date"><i class="fa-regular fa-calendar"></i> {$p.date_label}</span>
{/if}
<a class="news-card__title" href="{$p.url}">{$p.title}</a>
{if $p.summary}<p class="news-card__excerpt">{$p.summary}</p>{/if}
<a class="news-card__more" href="{$p.url}">
{trans key="storefront.read_more"}
<i class="fa-solid fa-arrow-right"></i>
</a>
</div>
</article>
{/foreach}
</div>
</section>
{/if}
$shop->blogs() віддає блоги вітрини в тому порядку, в якому вони стоять у
панелі, тому $blogs[0].url — адреса першого блогу. Якщо блогів кілька й
посилання потрібне на певний, знайдіть його за адресою:
{foreach $shop->blogs() as $b}
{if $b.slug == 'news'}<a href="{$b.url}">{trans key="storefront.all_news"}</a>{/if}
{/foreach}
Написи storefront.news, storefront.all_news і storefront.read_more взято
з файлів написів базової теми. У своїй темі заведіть такі самі або свої — як
це робиться, написано вище, у розділі «Написи теми».
Що є в запису
Кожен запис із {posts … assign=…} і $shop->posts() — це картка того самого
вигляду, що отримує стрічка блогу й шаблон partials/blog-card.html:
| Поле | Що всередині |
|---|---|
url |
адреса статті на вітрині — її ставлять у href |
title |
заголовок |
summary |
анонс, короткий текст для картки; порожньо, якщо власник його не написав |
cover |
обкладинка в повному розмірі; порожньо, якщо не завантажена |
cover_thumb |
ескіз обкладинки для картки; порожньо, якщо обкладинки немає |
category.name |
назва головної рубрики запису |
category.url |
адреса сторінки цієї рубрики |
date_label |
дата підписом мовою вітрини: «17 вересня 2026» |
date_iso |
повна дата з часом — для <time datetime="…"> |
date |
дата у вигляді 2026-09-17 — якщо потрібен свій формат |
author |
автор; порожньо, якщо не вказаний |
reading_time |
скільки хвилин займе читання |
views |
скільки разів статтю прочитали |
is_pinned |
запис закріплений угорі стрічки |
id, slug |
номер і адреса запису |
Поле category цілком порожнє, якщо в запису немає рубрики або рубрику
вимкнено. Тому {$p.category.name} завжди загортають у
{if $p.category}, як у прикладі вище: без перевірки запис без рубрики
зламає сторінку. Дати рахуються в часовому поясі магазину. Решту даних
сторінок блогу описано на сторінці Дані сторінок.
Часті помилки
Тег без assign і {foreach} по своїй змінній. Це найчастіша причина
скарги «limit не працює». Шаблон написано так:
{$posts = [[ 'title' => '…', 'date' => '07 липня', 'cat' => 'Огляди' ]]}
{posts limit=3}
<div class="news__grid">
{foreach $posts as $p}
<a href="#">{$p.title}</a> {$p.cat} {$p.date}
{/foreach}
</div>
Тег {posts limit=3} без assign чесно намалював три картки — шаблоном
partials/blog-card.html, над сіткою новин, а якщо стилі теми цих карток не
знають, їх легко не помітити. У змінну тег нічого не поклав, тому {foreach}
перебирає масив, написаний рядком вище, і скільки б ви не змінювали limit, на
сторінці нічого не змінюється. Правильно — попросити дані й перебирати саме їх:
{posts limit=3 assign=freshPosts}
{if $freshPosts}
<div class="news__grid">
{foreach $freshPosts as $p}
<a href="{$p.url}">{$p.title}</a>
{if $p.category}{$p.category.name}{/if}
<time datetime="{$p.date_iso}">{$p.date_label}</time>
{/foreach}
</div>
{/if}
Заглушка з макета лишилася в шаблоні. Масив на кшталт
{$posts = [[ 'title' => … ]]} або {assign var=posts value=[…]} потрібен
лише верстальнику, доки в магазині немає статей. У готовій темі його треба
видалити: якщо він лишиться, покупці бачитимуть вигадані новини, а справжні
статті не з'являться, скільки б їх не написали.
Змінна $posts. На сторінках стрічки блогу, рубрики й мітки
(blog.html, blog-category.html, blog-tag.html) у $posts уже лежать
записи поточної сторінки стрічки. Якщо блок новин стоїть у шапці чи підвалі й
перебирає $posts, на сторінках блогу він покаже стрічку, а не свою добірку.
А {posts … assign=posts}, поставлений просто в шаблон сторінки блогу,
підмінить саму стрічку. Беріть для добірки своє ім'я, наприклад freshPosts.
Імена полів із макета. У макеті поля зазвичай називаються по-своєму, а в запису магазину імена такі:
| У макеті | У запису магазину |
|---|---|
cat |
category.name, перевіряти через {if $p.category} |
date з готовим підписом на кшталт «07 липня» |
date_label; у date лежить дата вигляду 2026-07-07 |
href="#" |
href="{$p.url}" |
img, image |
cover_thumb для картки, cover у повному розмірі |
text, excerpt |
summary |
Поле, якого в запису немає, — це помилка в шаблоні, і сторінка з таким блоком
не відкриється. Якщо після переходу на справжні статті головна перестала
відкриватися, насамперед звірте імена полів із таблицею: найчастіше в
розмітці лишився {$p.cat} або {$p.category.name} без перевірки
{if $p.category}.
Записів менше, ніж limit. limit — це верхня межа, а не обіцянка. Якщо
в блогах вітрини опубліковано дві статті, за limit=3 їх буде дві. Чернетки,
записи з архіву, записи з датою публікації в майбутньому й записи блогу іншої
вітрини в добірку не потрапляють. Перевірте, що статтю опубліковано і дата її
публікації вже настала.
Товари картками
{products ids="12,7,3"}
{products ids="12,7,3" template="partials/product-card-small.html"}
Тег показує вибрані товари картками теми. Найчастіше його ставить не верстальник, а власник магазину: у редакторі тексту він позначає товари, і в опис категорії або в статтю потрапляє така вставка. Товари виводяться в тому порядку, в якому їх вибрали.
| Параметр | За замовчуванням | Що задає |
|---|---|---|
ids |
— | номери товарів через кому |
limit |
24 |
скільки товарів показати |
template |
partials/product-card.html |
свій шаблон картки |
class |
products-embed |
клас обгортки |
Добірки товарів для головної беруться інакше — у помічника $shop->products(),
щоб власник міняв їхній склад у панелі, не чіпаючи шаблон. Про це написано на
сторінці Помічник $shop.
Блоки
Блок — шматок тексту чи верстки, який власник пише один раз і показує на багатьох сторінках: переваги магазину, умови доставки, банер акції. Заводиться він у панелі, у розділі «Вітрини та вміст → Блоки», у кожного блоку є код. Зміна блоку одразу діє всюди, де він стоїть.
{content_block code="ship-info"}
{content_block code="custom_products" list="viagra" limit=12}
{content_block code="category-`$category.slug`-info"}
У шаблон теми блок ставиться тегом. У текст сторінки, опис товару чи категорії власник вставляє його кнопкою «Блок» у редакторі — параметри, якщо потрібні, пишуться там само, під «Додатково».
Який текст показати, магазин вирішує сам: спершу власний текст поточної вітрини мовою сторінки, далі загальний текст блоку цією мовою, далі власний текст вітрини основною мовою магазину й наостанок загальний текст основною мовою. Якщо тексту немає ніде, блок вимкнено або блоку з таким кодом немає, тег нічого не виводить — перевіряти заздалегідь не потрібно. Тому зручно заводити блоки за шаблоном коду, як у третьому рядку: блок з’явиться в того розділу, для якого його завели.
Параметри. Усе, крім code, стає змінними всередині блоку: при
list="viagra" limit=12 у тексті блоку доступні {$list} і {$limit}, а всі
параметри разом — {$params}. Так один блок показує на різних сторінках різні
дані. Приклад блоку «Товари списку» (текст блоку — вставка Smarty):
{$found = $shop->productList($list)}
{if $found}
<ul>
{foreach $found.products as $p}
{if $p@iteration > $limit|default:12}{break}{/if}
<li><a href="{$p.url}">{$p.name|escape}</a></li>
{/foreach}
</ul>
{/if}
Параметри видно лише самому блоку: на сторінку й у вкладені блоки вони не
потрапляють. Імена params, shop, storefront, theme, seo, customer,
contacts, cookies, get, request, action і smarty зайняті — такий
параметр пропускається.
Усередині блоку працюють вставки Smarty: теги доповнень, {products}, {posts},
помічник $shop. Коли блок викликано з шаблону теми, йому видно змінні цієї
сторінки й працює {include} шаблонів теми. Блок не може вставити сам себе —
така вставка просто пропускається. Зміна теми блоки не зачіпає: вони зберігаються
в магазині, а не в темі.
Форми з CRM
Форми «Поставити запитання», «Зворотний зв’язок» та інші власник збирає в панелі, у розділі «CRM → Форми»: поля, тексти кожною мовою і що робити із заявкою. Як це влаштовано для власника, написано на сторінці Форми на сайті. У тему форма ставиться тегом:
{crm_form code="question"}
{crm_form code="question" class="form-wide"}
{crm_form code="wholesale" template="partials/crm-form-compact.html"}
| Параметр | За замовчуванням | Що задає |
|---|---|---|
code |
— | код форми, його видно на сторінці форми під кнопкою «Код для теми» |
class |
порожньо | свій клас, приходить у шаблон як $form.class |
template |
partials/crm-form.html |
свій шаблон форми в темі |
У текст сторінки, опис товару чи блок власник вставляє форму кнопкою «Форма» в
редакторі, і туди потрапляє той самий тег. Тег нічого не виводить, якщо форми з
таким кодом немає, її вимкнено або вона не працює на цій вітрині, а також якщо
шлях у template не схожий на шлях до шаблону: допустимі латинські літери,
цифри, _, - і /, наприкінці .html. Якщо такого шаблону немає ні в темі,
ні в стандартній темі, тег теж нічого не виводить. Збій форми сторінку не
кладе.
Надсилання без перезавантаження, помилки біля полів і «дякуємо» дає сам
магазин: разом із формою він сам підключає /storefront/crm-form.js і
/storefront/crm-form.css. Тому форма працює в будь-якій темі, а тема відповідає лише
за розмітку й оформлення.
Шаблон partials/crm-form.html
Форму малює шаблон теми partials/crm-form.html або той, що заданий у
template. Якщо в темі такого шаблону немає, магазин бере його зі стандартної
теми default: форма з’явиться й у темі, написаній до форм. Крім $form,
шаблону видно $storefront, $theme, $auth, $min_order, $below_min,
$free_shipping_from, $free_shipping_left, $cookies, $get, $request
і змінні, які додають доповнення. Змінних сторінки ($shop, $customer, $product та
інших) у шаблоні форми немає. Усі ключі $form є завжди.
| Ключ | Що в ньому |
|---|---|
$form.code |
код форми |
$form.id |
id для розмітки: crm-form-question; друга копія тієї самої форми на сторінці — crm-form-question--2 |
$form.class |
клас із параметра class |
$form.title |
«Заголовок над формою», може бути порожнім |
$form.submit |
напис на кнопці |
$form.action |
адреса надсилання, уже з мовою сторінки |
$form.fields |
поля списком (прихованих полів у ньому немає) |
$form.consent |
позначка згоди на обробку даних: name, id, text, checked, error; null, якщо форма згоду не питає |
$form.sent |
форму щойно надіслали без скрипту, час показати «дякуємо» |
$form.again_url |
ця сама сторінка з порожньою формою — для посилання «надіслати ще одне» без скрипту |
$form.success |
текст «дякуємо» |
$form.error |
загальна помилка після надсилання без скрипту, інакше null |
$form.protection |
службові поля форми одним рядком |
$form.i18n |
написи мовою сторінки: required, sending, failed, choose, again |
Кожне поле в $form.fields:
| Ключ | Що в ньому |
|---|---|
key |
ключ поля, сталий |
name |
ім’я для <input name>: fields[ключ], у прапорців fields[ключ][] |
id |
id поля: id форми, дефіс і ключ |
type |
text, textarea, phone, email, number, date, select, radio, checkboxes або consent |
input |
вид <input type>: tel, email, number, date або text |
autocomplete |
name, tel, email або off — за тим, куди поле записується в картці |
label, placeholder |
підпис і підказка мовою сторінки |
required |
чи обов’язкове поле |
width |
full або half |
maxlength |
ліміт довжини, null у видів без ліміту |
value |
значення: після невдалого надсилання без скрипту — те, що ввів покупець, а покупцю, який увійшов, — його ім’я, телефон і пошта |
checked |
чи позначено прапорець-згоду |
options |
варіанти списку, перемикачів і прапорців: value, label, selected |
error |
помилка поля після надсилання без скрипту, інакше null |
Що обов’язково в розмітці. Оформлення змінюйте вільно, але залиште:
{$form.protection nofilter}усередині<form>. Там ключ захисту форми, позначка часу, адреса сторінки, невидима пастка для роботів і капча, якщо її ввімкнено. Без цього рядка надсилання не пройде або потрапить у спам. Друкувати його треба зnofilter, інакше замість полів на сторінці опиниться їхній текст.- Імена полів введення — рівно
{$f.name}, у позначки згоди —{$form.consent.name}зі значенням1. - Опори
data-crm-*, за якими скрипт і стилі магазину знаходять частини форми:
| Опора | Де стоїть |
|---|---|
data-crm-form="{$form.code}" |
на самій <form>; там само data-sending (напис кнопки на час надсилання) і data-failed (текст на випадок збою надсилання) |
data-crm-body |
обгортка всього, що ховається після успішного надсилання |
data-crm-field="{$f.key}" |
обгортка поля, отримує клас is-invalid; у згоди ключ consent |
data-crm-error="{$f.key}" |
місце для тексту помилки поля |
data-crm-message |
місце для загальної помилки форми |
data-crm-success |
повідомлення «дякуємо» |
data-crm-submit |
кнопка надсилання |
data-crm-again |
«надіслати ще одне», необов’язково |
data-crm-success-text, data-crm-submit-label |
необов’язково: куди всередині «дякуємо» й кнопки ставити текст, якщо там є значок |
data-crm-grid |
необов’язково, лише для стилів: сітка полів, де поле з data-width="half" займає пів рядка |
data-crm-choice |
необов’язково, лише для стилів: підпис навколо прапорця чи перемикача |
Поки форма надсилається, на ній стоїть клас is-sending, а кнопку вимкнено.
Підказки браузера про помилки скрипт вимикає: помилки приходять від магазину
мовою вітрини й стають біля своїх полів.
Стилі магазину в crm-form.css відповідають за розкладку й запасний вигляд
полів, кнопки й повідомлень. Вони написані через :where(), і правило теми їх
перебиває. Винятків два: елемент з атрибутом hidden усередині форми
прихований завжди, а пастку для роботів .crm-form__trap тема не
перевизначає. Колір акценту (рамка поля у фокусі, кнопка, прапорці) тема
задає змінною --crm-form-accent.
Без скрипту форма йде звичайним надсиланням і повертає покупця на ту саму
сторінку, до форми. Результат приходить у той самий $form: sent, error,
помилки полів і введені значення. Тому шаблон має показувати їх сам, як у
стандартній темі, де data-crm-success приховано атрибутом hidden, поки
$form.sent хибне. Така сторінка в загальний кеш не потрапляє.
Подія після надсилання. Коли магазин прийняв надсилання, на формі
спрацьовує подія cartix:form-sent. Вона спливає до document, у
detail.code — код форми. У разі помилки події немає. Надсилання робота
магазин теж «приймає», щоб не підказувати йому, тому подія рахує надсилання,
що дійшли до магазину, а не справжні заявки. Її зручно передавати лічильнику
цілей:
document.addEventListener('cartix:form-sent', (event) => {
window.dataLayer?.push({ event: 'form_sent', form: event.detail.code });
});
Якщо форма з’являється на сторінці пізніше, наприклад у вікні, яке тема
підвантажує за кліком, оживіть її викликом CartiX.initForms(контейнер), де
контейнер — елемент, усередині якого лежить форма (не сама <form>). Форму,
яка вже працює, виклик не чіпає. Функція є, лише коли на сторінці вже
завантажено /storefront/crm-form.js, тобто коли на ній від самого початку
стоїть хоча б одна форма: скрипти всередині підвантаженого шматка браузер під
час вставлення не виконує.
Свій шаблон найпростіше почати з копії
themes/default/templates/partials/crm-form.html: перенесіть її у свою тему й
змінюйте оформлення, не чіпаючи опор.
Форми
<form method="post" action="/reviews/submit">
{csrf_field}
…
{captcha form="reviews"}
<button type="submit">Надіслати</button>
</form>
{csrf_field} ставить приховане поле — без нього форма не пройде. {csrf_token}
віддає той самий ключ рядком; у каркас його вставляти не треба — мета-тег
csrf-token друкує магазин, а скриптам ключ приходить у CartiX.csrf.
{captcha form="…"} сам питає, чи ввімкнена перевірка «я не робот» для цієї
форми, і малює потрібний віджет. Перевірка вимкнена — тег не виводить нічого, і
гілку {if} навколо нього писати не треба.
Докладніше — на сторінці Форми вітрини.
Друковані форми замовлення
{assign var="invoice" value={order_document order=$order code="order.invoice"}}
{if $invoice}
<a href="{$invoice}" target="_blank" rel="noopener">Рахунок на оплату</a>
{/if}
Тег віддає посилання на друковану форму замовлення: рахунок, накладну, гарантійний талон — будь-який бланк із «Налаштування» → «Друковані форми». За посиланням відкривається готова до друку сторінка, та сама, що друкує менеджер: номер і дата документа видаються один раз, тому покупець і бухгалтерія бачать один і той самий папір.
order — замовлення кабінету ($order на сторінці account/order.html), його
номер або замовлення, що прийшло в шаблон. code — код бланка: він написаний у
самій формі, в «Налаштування» → «Друковані форми», рядком «Код бланка» поруч із
назвою, і копіюється кнопкою. pdf=true — той самий папір файлом:
{assign var="pdf" value={order_document order=$order code="order.delivery_note" pdf=true}}
{if $pdf}<a href="{$pdf}">Завантажити накладну</a>{/if}
Друк одразу після натискання
Звичайне посилання відкриває папір і залишає покупця сам на сам із питанням, чим
його надрукувати: на телефоні друк захований у меню «Поділитися». Скажіть
print=true — сторінка відкриє вікно друку сама, щойно завантажиться:
{assign var="doc" value={order_document order=$order code="order.invoice" print=true}}
{if $doc}
<a href="{$doc}" target="_blank" rel="noopener">Роздрукувати рахунок</a>
{/if}
Друк викликається після повного завантаження, а не одразу: інакше аркуш іде в
принтер без логотипа й печатки, які не встигли завантажитися. З pdf=true
параметр не потрібен і не діє — там вікно друку відкриває програма перегляду.
Якщо не хочеться вести покупця на іншу вкладку, друкуйте в прихованій рамці — це ті самі кілька рядків, бібліотека для цього не потрібна:
<a class="js-print" href="{order_document order=$order code="order.invoice"}">Роздрукувати рахунок</a>
{literal}
<script>
document.addEventListener('click', function (e) {
var link = e.target.closest('.js-print');
if (!link) return;
e.preventDefault();
var frame = document.createElement('iframe');
frame.style.cssText = 'position:fixed;right:0;bottom:0;width:0;height:0;border:0';
frame.src = link.getAttribute('href');
frame.onload = function () { frame.contentWindow.focus(); frame.contentWindow.print(); };
document.body.appendChild(frame);
});
</script>
{/literal}
Рамку не прибирайте одразу після print() — доки відкрито вікно друку, документ
має залишатися на сторінці. Той самий прийом працює для будь-якого бланка:
досить змінити код форми в посиланні.
Документ іншою мовою
Типово папір виходить мовою замовлення: покупець оформлював українською — і рахунок буде український, навіть якщо зараз він дивиться магазин російською. Потрібна інша мова — скажіть про це прямо:
{assign var="en" value={order_document order=$order code="order.invoice" locale="en"}}
{if $en}<a href="{$en}">Invoice in English</a>{/if}
Мовою можна розпорядитися й по-своєму — наприклад, дати папір тією мовою, якою покупець зараз дивиться магазин:
{assign var="doc" value={order_document order=$order code="order.invoice" locale=$storefront.locale}}
{if $doc}<a href="{$doc}">Рахунок на оплату</a>{/if}
Разом із pdf це теж працює:
{order_document order=$order code="order.invoice" locale="en" pdf=true}
Просити можна лише мову, якою працює сам магазин («Основні налаштування» → «Регіональні налаштування»). Незнайомий код тег мовчки пропускає повз вуха — папір вийде мовою замовлення, а не порожній. Якщо бланк потрібною мовою не написаний, магазин візьме його своєю основною: рахунок чужою мовою кращий за порожній аркуш.
Де показувати документи і які — вирішує тема: у кабінеті, на сторінці «Замовлення оформлено», у листі. Магазин відповідає за дві речі, в яких тема помилитися не може:
- посилання отримує тільки той, чиє це замовлення — покупець, який увійшов, зі своїм замовленням або гість, який оформив його в цій самій сесії;
- вимкнений або порожній бланк посилання не дає взагалі, інакше кнопка вела б на «сторінку не знайдено».
В обох випадках тег повертає порожній рядок — тому посилання ховають звичайним
{if}, як у прикладах вище.
Адресу побудовано на особистому ключі замовлення — тому самому, за яким покупець потрапляє на оплату. Власного секрету документ не має, і таке посилання можна покласти в лист. Памʼятайте: хто посилання отримав, той документ і відкриє.
Стилі і скрипти
{block name="page_assets"}
{asset_css file="css/product.css"}
{asset_js file="js/product.js" defer=true}
{/block}
| Параметр | Що робить |
|---|---|
file |
шлях усередині assets/ вашої теми |
inline |
вставити вміст прямо в сторінку, а не посиланням |
key |
ім'я, за яким файл не підключиться двічі |
head / footer |
куди виводити |
| решта | їдуть атрибутами в <link> або <script> |
{assets_head} і {assets_footer} виводять зібраний список — останніми, коли
сторінку зібрано повністю, тому файл можна оголосити в будь-якому місці шаблону.
Сусідні файли магазин склеює в один стиснений. Що потрапляє в склейку і як це
перевірити — на сторінці Стилі, скрипти і картинки теми.
Місце для доповнень
{hook name="product.buybox.end" product=$product}
Сюди доповнення виводять свою розмітку. Підписників немає — тег не виводить нічого. Кожне доповнення викликається окремо і в захисті від помилок: те, що впало, не виводить нічого, сторінка залишається живою.
Усе, що передано параметрами, їде доповненню — тому корисно передавати контекст: товар, позицію кошика. Повний список місць — на сторінці Точки для доповнень.
Подивитися, що прийшло в шаблон
{dump} усе, що доступне цьому шаблону
{dump var=$product} одне значення
{dumpc var=$product} те саме в консоль браузера
{$product|dump nofilter} звичним записом
Бачить тільки співробітник магазину. Покупцеві блок не покажеться, навіть якщо тег забули в темі. Паролі, ключі і підписи у виводі замінюються зірочками, глибина обмежена шістьма рівнями, довгі списки і рядки обрізаються.
Теги доповнень
Доповнення може завести власний тег — він з'явиться в усіх темах одразу. Встановлені зараз:
| Тег | Доповнення |
|---|---|
{slider id="home-hero"} |
Слайдери і банери |
{slider_data id="home-hero" assign=hero} |
те саме, віддає дані в змінну |
{oneclick_button product=$product} |
Купити в один клік |
{back_in_stock product=$product} |
Повідомити про надходження |
{callback_button} |
Зворотний дзвінок |
{shorturl url=$product.url} |
Короткі посилання |
Тег вимкненого доповнення, залишений у шаблоні, не ламає сторінку — він просто нічого не виводить. Тому тема може посилатися на теги доповнень, не перевіряючи, чи стоять вони.
Що приносить встановлене доповнення, видно двома способами: у його описі в
розділі «Доповнення» і за текою templates/plugins/<доповнення>/ у вашій темі —
доповнення з власною розміткою кладе шаблони прямо туди.
Обробники значень
У Smarty 5 в шаблон не можна писати будь-яку функцію мови. Магазин повертає безпечний набір:
Текст. ucfirst, lcfirst, ucwords, trim, ltrim, rtrim, str_pad,
str_replace, str_contains, str_starts_with, str_ends_with, sprintf,
strrev, mb_strlen, mb_substr, mb_strtolower, mb_strtoupper, urlencode,
rawurlencode, htmlspecialchars_decode.
Числа. abs, ceil, floor, intval, floatval, max, min, intdiv.
Списки. array_keys, array_values, array_slice, array_reverse,
array_sum, array_unique, array_flip.
Оформлення. tel перетворює телефон із налаштувань на посилання:
<a href="tel:{$phone|tel}">. color_tone відповідає light або dark для
кольору з налаштувань — за ним тема вибирає світлий чи темний логотип
(докладніше).
Налагодження. dump, dumpc.
Крім того, працює все, що Smarty вміє сам: count, round, implode,
substr, json_encode, date_format, escape, default, upper, nl2br,
truncate, strip_tags.
{$product.name|mb_strtoupper}
{$product.summary|truncate:120:'…'}
{$product.price|default:0}
{$order.created_at|date_format:'%d.%m.%Y'}
Незнайомий обробник сторінку не ламає: значення проходить наскрізь, а ім'я йде в журнал. Так друкарська помилка не перетворюється на білий екран у покупця — але й не дає про себе знати, тому перед викладкою в журнал варто зазирнути.
Екранування
Усе, що виводиться, екранується саме. {$product.name} безпечний завжди, навіть
якщо в назві товару є < або лапки.
Там, де дані містять готову розмітку, потрібен nofilter:
{$page.content nofilter} вміст інформаційної сторінки
{$product.description nofilter} опис товару
{$product.json nofilter} карта варіантів купівлі для браузера
Забули nofilter — покупець побачить текст із <p> замість абзаців.
Чого в темі немає
Теги {sum}, {in_words}, {number} і {profile} стосуються друкованих
бланків — рахунків, накладних, чеків, — а не вітрини. У шаблоні теми їх немає.
Про них написано в розділі
Друковані бланки.