Layout XML do Magento 2: guia prático para desenvolvedores
O layout XML é a parte do Magento 2 que decide quais blocks renderizam numa página, em qual ordem, dentro de quais containers e com quais templates. Quase toda tarefa de frontend passa por ele, de adicionar um block na página de produto a remover algo que um módulo de terceiros injetou no header. É também a parte onde mais vejo copiar e colar sem entender, o que gera páginas que quebram no próximo upgrade.
Este guia cobre como o layout XML funciona no Magento 2.4.8, as instruções que você vai usar toda semana e os hábitos que mantêm as mudanças de layout pequenas e seguras.
Onde ficam os arquivos de layout
Um módulo traz seus arquivos de layout em view/frontend/layout/ ou view/adminhtml/layout/. Um tema os traz dentro da pasta do módulo que ele visa, por exemplo app/design/frontend/Vendor/theme/Magento_Catalog/layout/. O nome do arquivo é o layout handle. Quando o Magento monta uma página, ele coleta todo arquivo com um handle correspondente de todos os módulos ativos e da cadeia de temas, e os mescla numa árvore só.
Alguns handles que você vai usar muito:
defaultcarrega em toda página.catalog_product_viewcarrega em toda página de produto. O Magento também adiciona handles mais específicos comocatalog_product_view_type_configurableecatalog_product_view_id_42.catalog_category_viewcarrega em páginas de categoria, comcatalog_category_view_type_layeredpara anchor categories.cms_index_indexé a home,cms_page_viewé qualquer outra página CMS.checkout_cart_indexecheckout_index_indexsão o cart e o checkout.
O handle de qualquer controller segue o padrão routeid_controller_action. Se você não tem certeza de quais handles uma página carrega, registre em log o resultado de $this->getLayout()->getUpdate()->getHandles() a partir de um block durante o desenvolvimento.
Page layouts e page configuration
Existem três tipos de arquivo de layout, e misturá-los é uma fonte comum de confusão.
- Page layouts ficam em
page_layout/e definem o esqueleto:empty,1column,2columns-left,2columns-righte3columns. Só contêm containers. - Os arquivos de page configuration ficam em
layout/e são os que você mais edita. Podem definir o page layout e mudar oheade obody. - Generic layouts são usados em respostas AJAX e em algumas partes do admin. Têm um elemento raiz
layoutem vez depage.
Um arquivo de page configuration típico é assim:
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
layout="1column"
xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<head>
<css src="Vendor_Module::css/product-extra.css"/>
<remove src="Some_Module::js/legacy-tracker.js"/>
</head>
<body>
<referenceContainer name="product.info.main">
<block class="Magento\Framework\View\Element\Template"
name="vendor.product.delivery"
template="Vendor_Module::product/delivery.phtml"
after="product.info.price"/>
</referenceContainer>
</body>
</page>
O atributo layout em page define o page layout daquele handle. O admin também pode definir um layout por página CMS, categoria ou produto, e essa escolha vence o XML para aquela entidade.
Containers e blocks
Um container é um elemento estrutural sem classe PHP e sem template. Ele renderiza seus filhos em ordem e pode envolvê-los numa tag HTML. Um block é um objeto PHP, geralmente com um template, que produz HTML.
<container name="vendor.usp.wrapper" htmlTag="div" htmlClass="usp-bar" label="USP Bar">
<block class="Magento\Cms\Block\Block" name="vendor.usp.cms">
<arguments>
<argument name="block_id" xsi:type="string">usp-bar</argument>
</arguments>
</block>
</container>
Um container sem filhos e sem htmlTag não renderiza nada, o que é útil. Você pode adicionar containers vazios como pontos de extensão e eles não custam nada nas páginas onde ninguém os usa.
Para blocks novos, use Magento\Framework\View\Element\Template como classe e ponha a lógica num view model. Escrever uma classe de block customizada só para expor alguns métodos a um template é um padrão mais antigo que dificulta testes e reuso.
View models
Um view model é uma classe simples que implementa Magento\Framework\View\Element\Block\ArgumentInterface. Você o passa como argument e o busca no template.
<referenceBlock name="vendor.product.delivery">
<arguments>
<argument name="view_model" xsi:type="object">Vendor\Module\ViewModel\Delivery</argument>
</arguments>
</referenceBlock>
<?php
/** @var \Magento\Framework\View\Element\Template $block */
/** @var \Magento\Framework\Escaper $escaper */
/** @var \Vendor\Module\ViewModel\Delivery $viewModel */
$viewModel = $block->getData('view_model');
?>
<p class="delivery-estimate"><?= $escaper->escapeHtml($viewModel->getEstimateText()) ?></p>
O view model recebe suas dependências pelo construtor como qualquer outra classe, então pode ser testado em unit test sem renderizar nada.
As instruções que você usa toda semana
referenceBlock e referenceContainer
Eles apontam para um elemento declarado em outro lugar, para você adicionar filhos, mudar arguments ou mudar atributos. Se o nome referenciado não existe naquela página, o Magento ignora a instrução sem erro. É conveniente, mas também significa que um erro de digitação falha em silêncio, então confira o resultado no navegador.
remove e display
<referenceBlock name="product.info.mailto" remove="true"/>
<referenceBlock name="catalog.compare.sidebar" display="false"/>
O remove="true" tira o elemento e seus filhos da árvore. O display="false" mantém na árvore mas não renderiza, então outros arquivos de layout ainda podem referenciá-lo ou tirar os filhos dele de dentro. Quando outro módulo move algo para dentro de um block que você removeu, o elemento movido some junto. Se isso te pegar, troque por display="false".
move
<move element="product.info.stock.sku" destination="product.info.main" before="product.info.price"/>
O move muda o pai e a posição de um elemento. Use before e after com o nome de um elemento, ou - para colocá-lo primeiro ou último. Não remova um block num arquivo e o redeclare em outro só para mudar a posição. Isso quebra todo outro módulo que referencia o original.
update
<update handle="customer_account"/> traz todas as instruções de outro handle. As páginas da conta do cliente usam isso para compartilhar a navegação lateral. É uma forma limpa de compartilhar um layout entre os seus próprios controllers.
Arguments e ifconfig
Arguments suportam vários valores de xsi:type: string, boolean, number, array, object, url, helper e options. Strings podem ser traduzidas com translate="true".
Blocks podem ser exibidos de forma condicional com ifconfig, que lê um caminho de config booleano:
<block class="Magento\Framework\View\Element\Template"
name="vendor.banner"
template="Vendor_Module::banner.phtml"
ifconfig="vendor_module/general/enabled"/>
Sobrescrever vs estender arquivos de layout
Um arquivo de tema com o mesmo nome de um arquivo de módulo o estende. Os dois são mesclados, e as instruções do tema são aplicadas depois. É o que você quer quase sempre.
O Magento também suporta overrides completos. Um arquivo em Vendor/theme/Magento_Catalog/layout/override/base/catalog_product_view.xml substitui o arquivo do módulo por inteiro, e override/theme/Magento/luma/ substitui um arquivo do tema pai. Eu evito. Você perde toda correção que o módulo trouxer naquele arquivo no próximo upgrade, e nada avisa.
Blocks cacheable e full page cache
Um block pode ser declarado com cacheable="false". Isso não deixa só aquele block sem cache. Deixa a página inteira não cacheável para o full page cache, em toda página onde aquele handle carrega. Um único block assim no default.xml desliga o full page cache da loja inteira. É uma das primeiras coisas que procuro numa auditoria de performance:
grep -rn 'cacheable="false"' app/code app/design vendor --include=*.xml | grep -v '/Test/'
Conteúdo específico do customer deve vir de customer data sections carregadas com JavaScript, para o HTML ser igual para todo visitante e o Varnish poder servi-lo.
Depurando o layout
- Limpe o cache de layout e de página depois das mudanças:
bin/magento cache:clean layout full_page. - Ative os template hints com
bin/magento dev:template-hints:enablepara ver qual template renderiza cada parte da página. - Valide o XML contra o schema na sua IDE. O PhpStorm resolve os caminhos
urn:magentodepois que você rodabin/magento dev:urn-catalog:generate .idea/misc.xml. - Quando um block não aparece, confira se o container pai existe naquele handle e se nenhum outro arquivo o remove.
Se você está montando um tema do zero, o guia de child theme mostra onde esses arquivos ficam numa pasta de tema real.
FAQ
Qual a diferença entre um block e um container no Magento 2?
Um container não tem classe nem template. Ele só agrupa filhos e pode envolvê-los numa tag HTML. Um block é um objeto PHP, geralmente renderizando um template, que produz o HTML de fato.
Por que minha mudança com referenceBlock não tem efeito?
O nome está errado, o block não faz parte daquele handle, ou outro arquivo de layout aplicado depois o remove ou muda. O Magento ignora referências a elementos inexistentes sem erro, então confira o nome e os handles que carregam naquela página.
Devo usar remove="true" ou display="false"?
Use display="false" quando outros módulos podem referenciar o block ou mover filhos para dentro dele. Use remove="true" quando você quer o elemento e todos os filhos fora e sabe que nada depende dele.
O cacheable="false" afeta só um block?
Não. Ele marca a página inteira como não cacheável para o full page cache. Carregue dados específicos do customer com customer data sections.
Atendo como freelancer em Magento e Adobe Commerce, incluindo correções de tema e frontend. Se você precisa de ajuda com isso, fale comigo.
Artigos relacionados
Como criar um child theme no Magento 2 passo a passo
Crie um child theme do Luma ou do Blank no Magento 2: theme.xml, registration.php, composer.json, view.xml, logo, override de templates, aplicação e deploy.
Customização de LESS no Magento 2: _theme.less, _extend.less
Como personalizar os estilos do Luma com LESS no Magento 2: variáveis no _theme.less, adições no _extend.less, overrides de módulo, media queries e compilação com Grunt.
Core Web Vitals no Magento 2: como corrigir LCP, INP e CLS
Onde lojas Magento 2 com Luma perdem nos Core Web Vitals e como corrigir LCP, INP e CLS: resposta do servidor, CSS, fontes, imagens, galeria e scripts de terceiros.
Static content deploy no Magento 2: estratégias e opções
Acelere o setup:static-content:deploy no Magento 2: estratégias standard, quick e compact, jobs em paralelo, escopo de temas e locales e builds sem banco.