Magento 2 Certified Professional Front End Developer Guide
Section 4: Create and Customize Template Files
4.1. Assign a customized template file using layout XML
How can a customized template file be assigned to a block using layout XML?
Let’s say you want to assign a customized wishlist template.You need to copy the standard file of the wishlist module.1 2 3 4 5 6 7 8 9 10 | xml version="1.0"?> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <referenceBlock name="customer.wishlist"> <arguments> <argument name="template" xsi:type="string">Vendor_Module::view.phtml</argument> </arguments> </referenceBlock> </body> </page> |
1 2 3 4 5 6 | xml version="1.0"?> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <referenceBlock name="customer.wishlist" template="Namespace_Module::new_template.phtml" /> </body> </page> |
1 2 3 4 5 6 7 8 9 10 | xml version="1.0"?> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <referenceBlock name="customer.wishlist"> <action method="setTemplate"> <argument name="template" xsi:type="string">Vendor_Module::view.phtml</argument> </action> </referenceBlock> </body> </page> |
1 2 3 4 5 6 7 8 | xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> <module name="Vendor_Module" setup_version="100.0.0"> <sequence> <module name="Magento_Wishlist"/> </sequence> </module> </config> |
1 2 3 4 5 6 7 8 9 10 | <div class="clickme-wrap" > <a href=""> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 129 129" xmlns:xlink="http://www.w3.org/1999/xlink" enable-background="new 0 0 129 129"> <g> <path d="m121.6,40.1c-3.3-16.6-15.1-27.3-30.3-27.3-8.5,0-17.7,3.5-26.7,10.1-9.1-6.8-18.3-10.3-26.9-10.3-15.2,0-27.1,10.8-30.3,27.6-4.8,24.9 10.6,58 55.7,76 0.5,0.2 1,0.3 1.5,0.3 0.5,0 1-0.1 1.5-0.3 45-18.4 60.3-51.4 55.5-76.1zm-57,67.9c-39.6-16.4-53.3-45-49.2-66.3 2.4-12.7 11.2-21 22.3-21 7.5,0 15.9,3.6 24.3,10.5 1.5,1.2 3.6,1.2 5.1,0 8.4-6.7 16.7-10.2 24.2-10.2 11.1,0 19.8,8.1 22.3,20.7 4.1,21.1-9.5,49.6-49,66.3z"/> </g> </svg> <span>click me</span> </a> </div> |
At the same time, we add the XML file.1 2 3 | <referenceContainer name="header-wrapper"> <block class="Magento\Framework\View\Element\Template" name="clickme-header" before="-" template="Magento_Theme::header/clickme.phtml"/> </referenceContainer> |
php bin/magento cache:flushOn the front, we get the following:
If you need to customize the standard template of the module and display it in the necessary place on the website, you should do the following:Copy the necessary template file to the folder with the theme by relative path.For example, the addtocart.phtml template is on the path:1 2 3 | <referenceContainer name="product.info.main"> <block class="Magento\Catalog\Block\Product\View" name="product.info.addtocart.second" as="addtocart-second" template="product/view/addtocart.phtml"/> </referenceContainer> |
php bin/magento cache:flushAs a result, you see two identical blocks one of which is located in the place that we need. (For example, standard block output can be removed with the help of name=" " remove="true" />):
This way, the block can be displayed anywhere.How does overriding a template affect upgradability?
When you override the template file with the help of layout XML, you actually violate the fallback path for the template. It does not allow the themes to modify the templates. While upgrading a module, it can happen that new functionality which appears in templates after the upgrade, is not available on the website as the template has been overridden.What precautions can be taken to ease future upgrades when customizing templates?
If you are a backend developer: use plugin for the block (in this case, it will not allow to use view models), which will turn on the set template only when needed. The best way is to avoid overriding when possible.4.2. Override a native template file with a customized template file, using the design fallback
How can the design fallback be used to render customized templates?
If you need to use a customized template instead of the standard one, all you need is to copy the necessary phtml file to your theme with the right path. This way you override the standard theme. For example, in order to use customized template of the “Add to the Cart” button for the product:Copy the file:
Then, move it to the path:
On the front:
How does that influence upgradability?
When upgrading, the template functionality can be interrupted. The reason is that the template which has been overridden, could be upgraded together with the other system aspects which depend on the template. If the overridden template does not upgrade, everything can break down.How can you determine which template a block renders?
In Magento 2, there are a few ways to find out which template renders.You can search in the Magento files. Usually, the template files are in the XML files of the layout. In the XML file (for example, if you look for the .box-tocart block of the addtocart.phtml template, it is likely to be set in the1 2 3 | <container name="product.info.form.content" as="product_info_form_content"> <block class="Magento\Catalog\Block\Product\View" name="product.info.addtocart" as="addtocart" template="product/view/addtocart.phtml"/> </container> |
template="product/view/addtocart.phtml"path to the template. In some places, the template file is defined in the PHP class. Then, you need to search for the word $_template in the block’s class.Defining the template1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <?php namespace Belvg\PaymentTrade\Block\Form; /** * Class PaymentTrade * @package Belvg\PaymentTrade\Block\Form */ class PaymentTrade extends \Magento\Payment\Block\Form { /** * Purchase order template * * @var string */ protected $_template = 'Belvg_PaymentTrade::form/paymenttrade.phtml'; } |
Here, on the tab ADVANCED -> Developer in the “Debug” section, you give the priority “Enabled Template Path Hints for Storefront” value “Yes”.
As a result, on the website, you can see the paths to the templates in red:
Another way to enable the path to the template is via the console command:bin/magento dev:template-hints:enable4.3. Describe conventions used in template files
What conventions are used in PHP templates?
- $this is no longer applicable to the block rendering. No matter which object you use in the template ($ block or $ this), it will always call the method from $block. If you take a look at \Magento\Framework\View\TemplateEngine\Php::__call() you will see:123public function __call($method, $args) {return call_user_func_array([$this->_currentBlock, $method], $args);}
- Here you can see that \Magento\Framework\View\TemplateEngine\Php is just a proxy between the template and $block, so the use of $block is preferable because of the direct call.
- echo command in PHP should be written using a short tag in the Magento templates.
= $block->getModuleName() ?>insteadgetModuleName() ?> - To make sure that your theme is displayed correctly with any language applicable to store view, check that unique lines of your theme are added in i18n. To get your new line added to the vocabulary and translated, use the __(‘
’) method when displaying the line in the .phtml template. If the line includes a variable, then you should use the following syntax:1echo __('Hello world') ?>1echo __('Hello %1', $yourVariable) ?> - If you need to use loop, use the foreach > endforeach constructions12345foreach ($answerData as $answerPosted):?><h4>Hello</h4><p>Answer given by: echo $answerPosted['username']; ?></p><p>Answer: echo $answerPosted['answer']; ?></p>endforeach; ?>
Why aren’t the common PHP loop and block constructs used?
Magento templates do not use constructions with { } because curly brackets are more difficult to identify in HTML.1 2 3 4 5 6 7 | if($block->value): ?> Hello elseif($block->asd): ?> Your name is: = $block->name ?> else: ?> You don't have a name. endif; ?> |
Which common methods are available on the $block variable?
All the common methods from the block context are available in template.Block context is a block class which is assigned to the template in layout XML file. It is equal to the block type in Magento 1. By default it is \Magento\Framework\View\Element\Template which is equal to Mage_Core_Block_Template in Magento 1.This block context is assigned to the template as the $block variable during rendering.Name the common methods available on the $block variable:getRootDirectory() – Instantiates filesystem directorygetMediaDirectory() – Get media directorygetUrl($route = '', $params = []) – Generate url by route and parametersgetBaseUrl() – Get base url of the applicationgetChildBlock($alias) – Retrieve child block by namegetChildHtml($alias = '', $useCache = true) – Retrieve child block HTMLgetChildChildHtml($alias, $childChildAlias = '', $useCache = true) – Render output of child child elementgetChildData($alias, $key = '') – Get a value from child block by specified keygetBlockHtml($name) – Retrieve block htmlformatDate($date = null, $format = \IntlDateFormatter::SHORT, $showTime = false, $timezone = null) – Retrieve formatting dateformatTime($time = null, $format = \IntlDateFormatter::SHORT, $showDate = false) – Retrieve formatting timegetModuleName() – Retrieve module name of blockescapeHtml($data, $allowedTags = null) – Escape HTML entitiesescapeJs($string) – Escape string for the JavaScript contextescapeHtmlAttr($string, $escapingSingleQuote = true) – Escape a string for the HTML attribute contextescapeCss($string) – Escape string for the CSS contextstripTags($data, $allowableTags = null, $allowHtmlEntries = false) – Wrapper for standard strip_tags() function with extra functionality for html entitiesescapeUrl($string) – Escape URLgetVar($name, $module = null) – Get variable value from view configurationHow can a child block be rendered?
Use the \Magento\Framework\View\Element\AbstractBlock::getChildHtml() method:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | public function getChildHtml($alias = '', $useCache = true) { $layout = $block->getLayout(); if (!$layout) { return ''; } $name = $this->getNameInLayout(); $out = ''; if ($alias) { $childName = $layout->getChildName($name, $alias); if ($childName) { $out = $layout->renderElement($childName, $useCache); } } else { foreach ($layout->getChildNames($name) as $child) { $out .= $layout->renderElement($child, $useCache); } } return $out; } |
1 2 3 4 | public function getSaveButtonHtml() { return $block->getChildHtml('save_button'); } |
1 | $block->getChildHtml('save_button'); |
How can all child blocks be rendered?
The same way as you would call a specific child block but without using the name variable:$block->getChildHtml();How can a group of child blocks be rendered?
The block group methods are located in vendor/magento/framework/View/LayoutInterface.phpThe main group example in Magento core is located on the product detail page’s tabs1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ // @codingStandardsIgnoreFile ?> if ($detailedInfoGroup = $block->getGroupChildNames('detailed_info', 'getChildHtml')):?> <div class="product info detailed"> $layout = $block->getLayout(); ?> <div class="product data items" data-mage-init='{"tabs":{"openedState":"active"}}'> foreach ($detailedInfoGroup as $name):?> $html = $layout->renderElement($name); if (!trim($html)) { continue; } $alias = $layout->getElementAlias($name); $label = $block->getChildData($alias, 'title'); ?> <div class="data item title" data-role="collapsible" id="tab-label-= /* @escapeNotVerified */ $alias ?>"> <a class="data switch" tabindex="-1" data-toggle="trigger" href="#= /* @escapeNotVerified */ $alias ?>" id="tab-label-= /* @escapeNotVerified */ $alias ?>-title"> = /* @escapeNotVerified */ $label ?> </a> </div> <div class="data item content" aria-labelledby="tab-label-= /* @escapeNotVerified */ $alias ?>-title" id="= /* @escapeNotVerified */ $alias ?>" data-role="content"> = /* @escapeNotVerified */ $html ?> </div> endforeach;?> </div> </div> endif; ?> |
4.4. Render values of arguments set via layout XML
How can values set on a block in layout XML be accessed and rendered in a template?
Let’s take a look at the example. For instance, you want to add a class to the product form block using XML.In order to do it, in the module file (for example,/app/design/frontend/{vendor_name}/{theme_name}/Magento_Catalog/layout/catalog_product_view.xml), you need do add the following:1 2 3 4 5 | <referenceBlock name="product.info" template="product/view/form.phtml" after="alert.urls"> <arguments> <argument name="custom_class" xsi:type="string">custom-class</argument> </arguments> </referenceBlock> |
$cssClass = $block->hasCustomClass() ? ' ' . $block->getCustomClass() : '';or$cssClass = $block- >getData('ustom_class');Let’s add the variables with different methods of obtaining the value and render them in the class=”” attribute.As a result, it looks like that:
The unicode symbols are encrypted, for example,⌛ turns into U+231B. This method is used for JS string literal. For inline JavaScript,(for example, onсlick) you should use escapeHtmlAttr()escapeHtml()
It should be your method of escaping data by default for any output. The main point is that the output of all the methods that do not include “Html” should be escaped.escapeHtmlAttr()
Use it for escaping the output in the HTML attribute, for example:
By default, it also escapes single quotes:
Set the value false on the second variable if you do not need it.escapeUrl()
This method is used for URL output. It applies HTML literal by default and additionally deletes javascript:, vbscript: and data:. If you want to deny URLs similar to these among the links provided by a user, you can use the method.In the releases before Magento 2.1, this function was not enabled and you had to use escapeXssInUrl().

4.5. Demonstrate ability to escape content rendered and template files
How can dynamic values be rendered securely in HTML, HTML attributes, JavaScript, and in URLs?
To render the values securely, you need to use the block’s built-in methods:For JavaScript:$block->escapeJs('value');For HTML: $block->escapeHtml('value', $allowedTags);HTML attributes: $block->escapeHtmlAttr('value', $escapeSingleQuote);URLs: $block->escapeUrl($url);All these methods are described in AbstractBlock (Magento\Framework\Escaper)escapeJs()1 2 3 4 5 6 | /** * Escape string for the JavaScript context * * @param string $string * @return string */ public function escapeJs($string) |
1 2 3 4 5 6 7 8 9 | /** * Escape string for HTML context. allowedTags will not be escaped, except the following: script, img, embed, * iframe, video, source, object, audio * * @param string|array $data * @param array|null $allowedTags * @return string|array */ public function escapeHtml($data, $allowedTags = null) |
1 2 3 4 5 6 7 8 | /** * Escape a string for the HTML attribute context * * @param string $string * @param boolean $escapeSingleQuote * @return string */ public function escapeHtmlAttr($string, $escapeSingleQuote = true) |
1 | title=" echo $block->escapeHtmlAttr($title) ?>" |
1 | onclick="console.log(' echo $block->escapeHtmlAttr($message) ?>')" |
1 2 3 4 5 6 7 | /** * Escape URL * * @param string $string * @return string */ public function escapeUrl($string) |
Copyright BelVG