Templates
A template is a file with the .phtml extension where HTML is mixed with short PHP inserts. A template belongs to a block: the layout says where the block stands, and the template says what it looks like.
Where templates live
Storefront templates are in the module's view/frontend/templates folder, admin templates in view/adminhtml/templates. The file is created by hand. In a layout a template is referenced as Acme_Hello::greeting.phtml; a nested folder goes after the colons: Acme_Hello::review/list.phtml.
A simple template
Replace the contents of the file from the previous lesson:
<!-- app/code/Acme/Hello/view/frontend/templates/greeting.phtml -->
<div class="hello-greeting">
<h2><?= $escaper->escapeHtml(__('Hello!')) ?></h2>
</div>The heading "Hello!" appears on the /hello page. The <?= ... ?> construct prints a value, the __() function makes a string translatable, and $escaper->escapeHtml() neutralises HTML special characters.
Template variables
Every Magento template already has two variables:
| Variable | What it is |
|---|---|
$block | The block the template belongs to |
$escaper | An object for safe output |
For the code editor to suggest methods, type comments are written at the top of a template:
<!-- app/code/Acme/Hello/view/frontend/templates/greeting.phtml -->
<?php
/** @var \Magento\Framework\View\Element\Template $block */
/** @var \Magento\Framework\Escaper $escaper */
?>Values from the layout
A block can receive a value right in the layout — in the arguments section:
<!-- 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>In the template the value is read with the getData() method:
<!-- app/code/Acme/Hello/view/frontend/templates/greeting.phtml -->
<h2><?= $escaper->escapeHtml(__('Welcome to %1!', $block->getData('shop_name'))) ?></h2>After bin/magento cache:clean the heading becomes "Welcome to Acme!". The %1, %2 placeholders in __() are replaced with the following arguments.
Conditions and loops
Templates use the colon syntax:
<!-- app/code/Acme/Hello/view/frontend/templates/greeting.phtml -->
<?php $benefits = ['Delivery in 2 days', '30-day returns']; ?>
<?php if ($benefits): ?>
<ul>
<?php foreach ($benefits as $benefit): ?>
<li><?= $escaper->escapeHtml($benefit) ?></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>The template prints a list of two items. In the next lesson the array moves from the template into a separate class.
Escaping
Everything printed to the page goes through $escaper:
| Method | Where to use it |
|---|---|
escapeHtml() | Text between tags |
escapeHtmlAttr() | An attribute value |
escapeUrl() | A URL in href and src |
escapeJs() | A string inside JavaScript |
Links and child blocks
<!-- app/code/Acme/Hello/view/frontend/templates/greeting.phtml -->
<a href="<?= $escaper->escapeUrl($block->getUrl('hello/index/status')) ?>">
<?= $escaper->escapeHtml(__('Status')) ?>
</a>
<?= $block->getChildHtml('hello.extra') ?>The getUrl() method builds a page URL, and getChildHtml() prints a block nested into the current one in the layout. Its result is not escaped — it is already finished HTML.
Common mistake
A value is printed without escaping: <?= $review->getAuthor() ?>. If a customer typed a script tag into the name field, it runs in other visitors' browsers. Print any data from the database or the request only through the $escaper methods.