Каркас страницы: блоки и включения
Как страница наследует общий каркас, где переопределять заголовок и содержимое и как выносить повторяющиеся куски.
На этой странице
Страница витрины не повторяет шапку и подвал — она наследует общий каркас и
заполняет в нём блоки. Каркас лежит в 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} |
ошибка сборки шаблона |
| незнакомый обработчик значения | значение выводится как есть, в журнал идёт предупреждение |
Опечатка в имени файла — единственная из этих ошибок, из-за которой покупатель видит ошибку вместо страницы. Поэтому после переименования шаблона стоит пройтись по витрине глазами.