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

Каркас сторінки: блоки та включення

Як сторінка успадковує спільний каркас, де перевизначати заголовок і вміст та як виносити повторювані шматки.

На цій сторінці

Сторінка вітрини не повторює шапку і підвал — вона успадковує загальний каркас і заповнює в ньому блоки. Каркас лежить у layout.html, сторінки успадковують його тегом {extends}.

{extends file="layout.html"}

{block name="title"}{$product.name} — {$storefront.name}{/block}

{block name="content"}
    <h1>{$product.name}</h1>
{/block}

Успадкування — ваше рішення, а не вимога магазину: сторінка може бути і самостійним файлом із власним <html>. Але повторювати шапку у двадцяти файлах ніхто не стане, тому каркас є в усіх тем.

Що зобов'язане бути в каркасі

<!DOCTYPE html>
<html lang="{$storefront.locale}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>{block name="title"}{if $seo.title}{$seo.title}{else}{$storefront.name}{/if}{/block}</title>
    {block name="meta"}
        {if $seo.description}<meta name="description" content="{$seo.description|escape}">{/if}
        {if $seo.canonical}<link rel="canonical" href="{$seo.canonical}">{/if}
        {foreach $seo.hreflang as $code => $href}
            <link rel="alternate" hreflang="{$code}" href="{$href}">
        {/foreach}
    {/block}

    {block name="page_assets"}{/block}
    {assets_head}
    {hook name="storefront.head"}
</head>
<body class="page-{$action}" data-action="{$action}">
    {hook name="storefront.body.start"}

    {include file="partials/header.html"}
    <main>{block name="content"}{/block}</main>
    {include file="partials/footer.html"}

    {assets_footer}
    {hook name="storefront.body.end"}
</body>
</html>

Розберемо те, без чого магазин працювати не буде.

<meta name="csrf-token"> — без нього не пройде жодна форма і жоден запит із браузера: додавання в кошик, перерахунок оформлення, надсилання відгуку. Рядок виглядає необов'язковим, а без нього покупець не зможе нічого купити.

Адреси дій на <body> — за ними магазин знаходить, куди звертатися при додаванні в кошик, зміні кількості, додаванні в обране і порівняння, надсиланні відгуку і запитання. Повний список і що станеться без кожного з них — на сторінці Атрибути data-*.

{assets_head} і {assets_footer} — сюди виводяться стилі і скрипти: і ваші, і ті, що магазин підключає сам, і ті, що додають доповнення. Без них вітрина залишиться без оформлення і без поведінки.

{block name="page_assets"} стоїть до {assets_head}. Порядок тут важливий: усе, що зареєстроване після {assets_head}, у шапку вже не потрапить. Сторінка оголошує свої файли в блоці page_assets, а вивід зібраного списку йде слідом.

$action — ім'я поточного розділу вітрини: default на головній, далі category, product, search, checkout/cart. Зручно і для класу на <body>, і для підсвічування активного пункту меню.

Блоки

Блок — це іменоване місце в каркасі, яке сторінка може заповнити своїм вмістом. У базовій темі їх чотири:

Блок Для чого
title заголовок вкладки
meta мета-теги: опис, canonical, Open Graph
page_assets стилі і скрипти, потрібні тільки цій сторінці
content сам вміст сторінки

Заводьте свої, якщо вони вам потрібні, — наприклад окремий блок під клас на <body> або під хлібні крихти. Блок, який сторінка не перевизначила, виводить те, що написано в каркасі.

{* у layout.html *}
{block name="breadcrumbs"}{include file="partials/breadcrumbs.html"}{/block}

{* у home.html — на головній крихти не потрібні *}
{block name="breadcrumbs"}{/block}

Включення

Шматок розмітки, який потрібен на кількох сторінках, виноситься в partials/ і включається:

{include file="partials/product-card.html" product=$item}

Усе, що передано параметрами, доступне всередині включеного файлу як звичайні змінні: {$product.name}. Дані сторінки там теж видно — передавати $storefront або $shop не потрібно.

Включення особливо окупається на лістингу: категорія, пошук, бренд і мітка отримують однаковий набір даних, тому список товарів, фільтри і посторінкова навігація пишуться один раз.

{* category.html, search.html, brand.html, tag.html — одне й те саме *}
{include file="partials/filters.html"}
{foreach $products as $product}
    {include file="partials/product-card.html" product=$product}
{/foreach}
{include file="partials/pagination.html"}

Умови і цикли

{if $product.preorder}
    Під замовлення
{elseif $product.is_available}
    В наявності
{else}
    Немає в наявності
{/if}

{foreach $products as $product}
    {$product@index}    порядковий номер з нуля
    {$product@iteration} він же з одиниці
    {$product@first}    перший у списку
    {$product@last}     останній
{foreachelse}
    Товарів немає
{/foreach}

{foreachelse} спрацьовує, коли список порожній, — зручно, щоб не писати окрему перевірку.

Коментарі

{* цей текст не потрапить у розмітку сторінки *}

Звичайний HTML-коментар <!-- … --> покупець побачить у вихідному коді; {* … *} вирізається при збиранні шаблону.

Чого в шаблоні не можна

Шаблон працює в пісочниці. Заборонені {php}, {eval}, {fetch}, {include_php}, звернення до класів магазину і читання файлів із диска. Написали {App\Models\User::count()} — отримаєте помилку і білий екран, а не дані.

Це не недоробка, а захист: тема правиться з панелі, і файл теми не повинен перетворюватися на спосіб виконати на сервері що завгодно. Усе, що темі потрібно, вона бере у помічника $shop і з даних сторінки.

Що буває, коли файлу немає

Що сталося Що побачить покупець
немає шаблону сторінки помилка на цій сторінці, решта працюють
{extends} посилається на неіснуючий файл те саме
{include} посилається на неіснуючий файл те саме
немає 404.html стандартна сторінка «не знайдено», без вашої шапки і підвалу
незнайомий одиночний тег {unknown} нічого, сторінка живе
незнайомий парний тег {foo}…{/foo} помилка збирання шаблону
незнайомий обробник значення значення виводиться як є, у журнал іде попередження

Друкарська помилка в імені файлу — єдина з цих помилок, через яку покупець бачить помилку замість сторінки. Тому після перейменування шаблону варто пройтися по вітрині очима.

Оновлено 25 серпня 2026