Categorieën
Magento Nieuws

Layout customization in Magento 2

Net als in Magento 1 kun je in Magento 2 je layout aanpassen via XML. In Magento 2 is dit echter prominenter gemaakt, zodat je de phtml-bestanden zelf minder hoeft aan te passen en dit allemaal overzichtelijk kan via XML. In deze blog leg ik uit wat je allemaal kan via XML en wat best practices kunnen zijn.

Structuur van een XML-bestand

<?xml version=”1.0”>
<page layout=”1column” xmlns_xsi=”http://www.w3.org/2001/XMLSchema-instance” xsi_noNamespaceSchemaLocation=”urn:magento:framework:View/Layout/etc/page_configuration.xsd”>
<head>
<css src=”stores.css”/>
<script src=”stores.js”/>
</head>
<body>
<container name=”customer.stores.country” label=”Customer Stores Country” htmlTag=”div” htmlClass=”customer-stores-country”>
<block class=”GuapaCustomerStoresBlockCountry” name=”customer.stores.country” ifconfig=”guapa/customerstores/is_enabled”>
<arguments>
<argument name=”label” xsi_type=”string”>Stores per Country</argument>
</arguments>
</block>
</container>
</body>
</page>

In bovenstaand voorbeeld zie je dat een layout XML begint en eindigt met <page>. In de page kun je nog de layout aangeven, zoals “1column”. Wanneer je hetzelfde XML-bestand extend (zie later in deze blog) en je hebt een layout aangegeven, dan zal deze leidend zijn.

Vervolgens kun je een <head> plaatsen waarin je externe CSS of javascripts kan laden. We adviseren sterk om zoveel mogelijk javascripts, die bijvoorbeeld zelf geschreven zijn, in te laden via requirejs-config.js in plaats van via XML! Dit omdat requireJs helpt met het inladen van de javascripts met de juiste dependencies en op het juiste moment, door de modulaire opbouw van Magento.

In de <body> kun je blokken en containers aanmaken, aanpassen of verwijderen. In dit voorbeeld is er een container aangemaakt. Wat het aanmaken van een container zo nuttig maakt is dat je geen onnodige elementen hoeft aan te maken in de phtml-bestanden. Dit zorgt voor overzichtelijkheid en nettere code in je phtml-bestanden. Je kan zelf bepalen welk element je wilt hebben via htmlTag, dit hoeft dus niet per se <div> te zijn, maar kan ook een ander geldig element zijn. Daarnaast kun je ook nog een of meerdere classes aangeven via htmlClass.

Vervolgens wordt er in de container een blok aangemaakt, in dit geval een CMS blok met identificatiecode “about”, waarvan de inhoud dan getoond wordt op de frontend. De “ifconfig” kan handig zijn als je het blok of de container alleen wilt tonen op basis van een instelling in de backend.

De “name” die wordt getoond in zowel container als blok moet altijd uniek zijn. Wanneer je in een ander XML-bestand de “name” gebruikt van een container of blok, dan zul je desbetreffende container of blok overschrijven (overriden). Dat betekent dat de oude container of het blok niet meer geldig is en er alleen wordt geluisterd naar wat je gedaan hebt in je nieuwe blok of container.

Layout uitbreiden (extend)

Aller eerst dien je een XML-bestand aan te maken. Als je een bestaand XML-bestand wilt aanpassen / uitbreiden dan dien je deze aan te maken in:

app/design/frontend/Vendor_naam/Guapa_naam/Guapa_Naam/layout/

Gebruik hierbij dezelfde bestandsnaam als het origineel. Let wel op dat je niet de complete inhoud van de XML hoeft te kopiëren. Sterker nog, dit raden we je ten strengste af! Het kopiëren van dezelfde blokken en containers leidt tot het daadwerkelijk overschrijven van de blokken of containers die al worden aangemaakt via het originele XML-bestand, zoals ik eerder heb vermeld. Ook kan dit voor problemen zorgen wanneer je van een bepaald blok of container standaard acties verwacht. Overschrijf dus nooit blokken of containers als dit niet nodig is! Wanneer je in het geval een bepaalde blok wel nodig hebt waarvan je de class wilt overschrijven, dan kun je dit het beste doen door het blok “opnieuw” aan te maken waarin je een andere class kan meegeven, maar wel de naam (name) van het blok behoudt. Let op; vanaf Magento 2.2 is het overigens niet meer mogelijk om een class te overriden via een referenceBlock.

Refereren naar een blok of container

Als je iets wilt doen met blokken of containers die al zijn aangemaakt via het originele XML-bestand, refereer dan naar het desbetreffende blok of de container, oftewel “reference”.

Door middel van refereren haal je het blok of de container op die al is aangemaakt via het originele XML-bestand. Na het refereren kun je zowel aanpassingen maken, of je kan extra containers of blokken toevoegen onder het gerefereerde blok of container.

Verwijderen van een blok of container

Als je wilt dat een blok of container niet getoond moet worden, kun je deze verwijderen. Let wel op dat alle blokken en containers die daaronder zitten ook worden verwijderd. Zie hieronder een voorbeeld hoe je een blok of container kan verwijderen:

&lt;referenceBlock name=”advanced-search-link” remove=”true”/&gt;

Verplaatsen van een blok of container

Zie het volgende voorbeeld om een blok of container te verplaatsen:

&lt;move element=”navigation.sections” destination=”header.sticky” after=”-”/&gt;

Dit is vooral handig om blokken of containers te verplaatsen zonder al te veel moeite te doen door bijvoorbeeld in de phtml-bestanden aanpassingen te maken. Op deze manier kun je ook mooi overzichtelijk houden wat je allemaal verplaatst hebt.

In het voorbeeld wordt gebruik gemaakt van een after=”-”. Dit betekent dat “navigation.sections” pas na alle andere blokken/containers wordt ingeladen binnen header.sticky. Je kunt ook before=”-” gebruiken om “navigation.sections” voor alle andere blokken/containers te plaatsen in “header.sticky”. Als je “navigation.sections” ergens wat specifieker wilt plaatsen kun je in de before of after ook een “name” gebruiken van een blok of container i.p.v. “-”, zodat deze dan voor of na dit blok of container wordt geladen.

Template instellen van een blok

Op veel blokken kun je een template instellen. Bijvoorbeeld:

&lt;block class=”MagentoFrameworkViewElementTemplate” name=”back.to.top” template=”Magento_Theme::html/page/back.to.top.phtml”/&gt;

Wanneer al ergens een blok aangemaakt wordt en je wilt alleen het pad van de template aanpassen kan je dat doen op de volgende twee manieren:

&lt;referenceBlock name=”back.to.top” template=”Magento_Theme::html/page/new.back.to.top.phtml”/&gt;

Bovenstaande is de eerste en meest voorkomende manier om een template aan te passen. Dit komt ook omdat dit wat tekens scheelt en ziet er verder ook netjes uit.

<referenceBlock name=”back.to.top”>
<arguments>
<argument name=”template” xsi_type=”string”>Magento_Theme::html/page/new.back.to.top.phtml</argument>
</arguments>
</referenceBlock>

Ook deze manier is door Magento zelf aanbevolen middels de DevDocs (http://devdocs.magento.com/). Echter kan het voorkomen dat niet alle blokken op deze manier worden aangemaakt. Dat betekent dat “<arguments>” niet kan werken.

Wanneer beide manieren niet werken kun je gebruik maken van de oude manier, maar gebruik dit als laatste redmiddel, want bij de meeste modules is dit verouderd (deprecated):

<referenceBlock name=”back.to.top”>
<action method=”setTemplate”>
<argument name=”template” xsi_type=”string”>Magento_Theme::html/page/new.back.to.top.phtml</argument>
</action>
</referenceBlock>

In eerdere Magento 2.1.* versies zit nog wel een bug. Veel van de Magento modules bevatten de module naam niet voor het template pad. Bijvoorbeeld:

&lt;block class=”MagentoCatalogBlockProductView” name=”product.info.addtocart” as=”addtocart” template=”product/view/addtocart.phtml”/&gt;

Dit wordt een probleem wanneer je besluit de class te “overriden” in je eigen module. De class kan namelijk de template niet meer vinden. Er wordt namelijk letterlijk gezocht naar “product/view/addtocart.phtml” en zolang je in dezelfde module zit, dan gaat dit goed. Echter bij een override zit de class in een andere module, waardoor de template niet gevonden kan worden, omdat er rekening wordt gehouden met de module naam. Dit gedrag wijkt af van Magento 1, waar er nooit om een module naam gevraagd wordt. Dit is op te lossen door in je eigen XML-bestand de module naam toe te voegen, zoals hieronder is uitgewerkt:

<referenceBlock name=”product.info.addtocart”>
<arguments>
<argument name=”template” xsi_type=”string”>Magento_Catalog::product/view/addtocart.phtml</argument>
</arguments>
</referenceBlock>

Wanneer je te maken hebt met een blok zonder “name” dan is dit niet te doen via XML. Je kan dan via de blok class de “setTemplate()” functie beïnvloeden.

Deze hotfixes zijn allang van belang in oudere Magento 2 versies, want in latere versies van 2.1.* en vanaf 2.2.* heeft Magento in al zijn modules wel correct de module naam toegevoegd aan de template paden. Overigens is het verstandig om wel altijd de module naam toe te voegen bij de template paden of je nu een nieuw blok aanmaakt of een bestaande aanpast!

Layout handles

Je kunt op verschillende manieren een eigen layout handle aanmaken en deze toepassen. Zo kun je bijvoorbeeld je eigen layout XML-bestanden aanmaken op basis van controller acties in je eigen module.

Een ander handig voorbeeld is om via een observer extra layout handles toe te voegen aan controller acties die al één of meerdere layout handles heeft. Op deze manier kun je namelijk je layout beïnvloeden op specifieke condities. Bijvoorbeeld: op de categoriepagina wil je voor ingelogde gebruikers iets extra’s tonen op de frontend dat niet te zien is (en dus niet ingeladen wordt) wanneer je uitgelogd bent.

Er zijn tal van mogelijkheden om je frontend er anders uit te laten zien met behulp van layout XML’s zonder veel te hoeven aanpassen in je phtml-bestanden. Dit maakt het aanpassen van de frontend in Magento een stuk makkelijker en vooral overzichtelijker.

“Broken reference”

In je var folder kun je de logs inschakelen. Vaak wordt er dan een support_report.log en/of system.log aangemaakt. In deze logbestanden kun je “Broken reference” tegenkomen. Dit kan betekenen dat je een bepaalde module niet goed hebt geïnstalleerd waardoor je bepaalde layout XML’s mist of er zijn aanpassingen geweest die niet correct geïmplementeerd zijn.

Je kunt dit altijd controleren op de elementnaam die de log weergeeft. Vaak zijn de berichten voor de hand liggend en de meest voorkomende fout is dat er een element wordt verplaatst die nog niet is aangeroepen.

Enkele voorbeelden:

report.CRITICAL: Broken reference: the ‘header.inner.right’ element cannot be added as child to ‘header’, because the latter doesn’t exist [] []

report.CRITICAL: Broken reference: the ‘global.search’ tries to reorder itself towards ‘notification.messages’, but their parents are different: ‘header.inner.right’ and ‘header’ respectively. [] []

Probeer hier altijd op te controleren, want anders lopen je logs vol met dit soort berichten en dat zou zonde zijn.

Heb je naar aanleiding van deze blog vragen over layout XML’s, neem dan contact met ons op, we staan je graag te woord!