Шаблоны
Шаблон — файл с расширением .phtml, в котором HTML смешан с короткими вставками PHP. Шаблон принадлежит блоку: макет говорит, где стоит блок, а шаблон — как он выглядит.
Где лежат шаблоны
Шаблоны витрины находятся в папке view/frontend/templates модуля, шаблоны панели управления — в view/adminhtml/templates. Файл создают вручную. В макете шаблон указывают как Acme_Hello::greeting.phtml; вложенную папку пишут после двоеточий: Acme_Hello::review/list.phtml.
Простой шаблон
Замените содержимое файла из прошлого урока:
<!-- app/code/Acme/Hello/view/frontend/templates/greeting.phtml -->
<div class="hello-greeting">
<h2><?= $escaper->escapeHtml(__('Здравствуйте!')) ?></h2>
</div>На странице /hello появится заголовок «Здравствуйте!». Конструкция <?= ... ?> выводит значение, функция __() делает строку переводимой, а $escaper->escapeHtml() обезвреживает спецсимволы HTML.
Переменные шаблона
В любом шаблоне Magento уже есть две переменные:
| Переменная | Что это |
|---|---|
$block | Блок, которому принадлежит шаблон |
$escaper | Объект для безопасного вывода |
Чтобы редактор кода подсказывал методы, в начале шаблона пишут комментарии с типами:
<!-- app/code/Acme/Hello/view/frontend/templates/greeting.phtml -->
<?php
/** @var \Magento\Framework\View\Element\Template $block */
/** @var \Magento\Framework\Escaper $escaper */
?>Значения из макета
Блоку можно передать значение прямо в макете — в разделе arguments:
<!-- app/code/Acme/Hello/view/frontend/layout/hello_index_index.xml -->
<block name="hello.greeting" template="Acme_Hello::greeting.phtml">
<arguments>
<argument name="shop_name" xsi:type="string">Acme</argument>
</arguments>
</block>В шаблоне значение читают методом getData():
<!-- app/code/Acme/Hello/view/frontend/templates/greeting.phtml -->
<h2><?= $escaper->escapeHtml(__('Добро пожаловать в %1!', $block->getData('shop_name'))) ?></h2>После bin/magento cache:clean заголовок станет «Добро пожаловать в Acme!». Метки %1, %2 в __() заменяются следующими аргументами.
Условия и циклы
В шаблонах принята запись с двоеточием:
<!-- app/code/Acme/Hello/view/frontend/templates/greeting.phtml -->
<?php $benefits = ['Доставка за 2 дня', 'Возврат 30 дней']; ?>
<?php if ($benefits): ?>
<ul>
<?php foreach ($benefits as $benefit): ?>
<li><?= $escaper->escapeHtml($benefit) ?></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>Шаблон выведет список из двух пунктов. В следующем уроке массив переедет из шаблона в отдельный класс.
Экранирование
Всё, что выводится на страницу, пропускают через $escaper:
| Метод | Где применять |
|---|---|
escapeHtml() | Текст между тегами |
escapeHtmlAttr() | Значение атрибута |
escapeUrl() | Адрес в href и src |
escapeJs() | Строка внутри JavaScript |
Ссылки и вложенные блоки
<!-- app/code/Acme/Hello/view/frontend/templates/greeting.phtml -->
<a href="<?= $escaper->escapeUrl($block->getUrl('hello/index/status')) ?>">
<?= $escaper->escapeHtml(__('Статус')) ?>
</a>
<?= $block->getChildHtml('hello.extra') ?>Метод getUrl() строит адрес страницы, а getChildHtml() выводит блок, вложенный в текущий в макете. Его результат не экранируют — это уже готовый HTML.
Частая ошибка
Значение выводят без экранирования: <?= $review->getAuthor() ?>. Если покупатель ввёл в поле имени тег script, он выполнится в браузерах других посетителей. Любые данные из базы и запроса выводите только через методы $escaper.