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

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

Как страница наследует общий каркас, где переопределять заголовок и содержимое и как выносить повторяющиеся куски.

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

Страница витрины не повторяет шапку и подвал — она наследует общий каркас и заполняет в нём блоки. Каркас лежит в 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