Aller au contenu principal

Magento 2 & Magento 1 : les layouts

Magento 2 & Magento 1 : les layouts

Dans le cadre de notre expertise sur Magento 2, nous allons vous partager une série de tutoriaux et de comparatifs entre les deux versions du CMS réalisée par notre Directeur Technique Hervé Guétin. Le premier article de cette série concerne un aspect très important de la construction des pages : les layouts. 

Que ce soit dans le cadre d’une migration de M1 vers M2 ou de la création d’un nouveau module pour M2, la façon de travailler sur l’agencement des pages (le layout) peut différer selon la version.

Voici un comparatif de quelques connaissances et opérations essentielles utiles lors du travail sur les layouts.

Déclaration et nommage des fichiers

M2

Dans Magento 2, l’ajout d’un fichier de layout est automatique car déclaratif. Chaque layout handle correspond à un fichier XML qui porte son nom. Ainsi, le layout handle catalog_product_view reçoit ses modifications de layout via les fichiers catalog_product_view.xml présents dans les modules et les thèmes.

Les instructions du layout handle default sont donc dans default.xml.

Tous les fichiers XML de layout étant chargés, il est facile d’utiliser plusieurs fichiers de layout pour le même module puis d’utiliser l’action <update handle="..."/> pour ajouter le contenu des uns aux autres.

La magie opérant le chargement des fichiers XML au sein de l’objet de layout \Magento\Framework\View\Layout se trouve dans \Magento\Framework\View\Model\Layout\Merge::_loadFileLayoutUpdatesXml.

M1

L’ajout d’un fichier de layout est basé sur de la configuration via le fichier config.xml du module ou le theme.xml.

Le nommage des fichiers de layout est arbitraire mais les standards veulent que le fichier XML soit nommé comme le module (namespace_module.xml).

Utiliser plusieurs fichiers de layout (et profiter de <update handle="..."/>) pour le même module (sans rapport avec le thème) nécessite de faire une déclaration de ce type dans le config.xml du module :

    <frontend>
        <layout>
            <updates>
                <my_module_common module="My_Module">
                    <file>my_module_common_blocks.xml</file>
                </my_module_common>
                <my_module_layout module="My_Module">
                    <file>my_module.xml</file>
                </my_module_layout>
            </updates>
        </layout>
    </frontend>

Définir un block avec template

M2

Par l’ajout d’un block dans un container.

<referenceContainer name="content">  
    <block  
  class="Magento\Framework\View\Element\Template"  
  name="my_module.some_block_name"  
  template="My_Module::some/template.phtml"  
  />

Le template étant placé dans le dossier view/[area]/templates/some/ du module My_Module (potentiellement surchargé dans un thème).

Tout block placé dans un container est automatiquement intégré au layout et son HTML est généré.

En un sens, le container M2 est comparable au core/text_list de M1.

M1

Par l’ajout d’un block dans un autre block.

<reference name="content">   
    <block  
  type="core/template"  
  name="my_module.some_block_name"  
  template="my_module/some/template.phtml"  
  />  
</reference>

Le template étant placé dans le dossier app/design/[area]/[package]/[theme]/my_module/some/ de Magento.

Le block ainsi ajouté n’est pas automatiquement rendu en HTML. Il l’est uniquement si son block en reference le permet ; par exemple :

  • En étant une instance de Mage_Core_Block_Text_List
  • En utilisant explicitement la méthode getChildHtml() dans le template utilisé par le bloc reference

Modifier le fichier phtml utilisé pour le template

M2

Exemple pour modifier le template utilisé par le block navigation.sections :

<referenceBlock name="navigation.sections" template="My_Module::some/template.phtml"/>

Le template étant placé dans le dossier view/[area]/templates/some/ du module My_Module(potentiellement surchargé dans un thème).

M1

En utilisant <action method="setTemplate"/> :

<reference name="catalog.topnav">  
    <action method="setTemplate">  
        <template>my_module/some/template.phtml</template>  
    </action>  
</reference>

Le template étant placé dans le dossier app/design/[area]/[package]/[theme]/my_module/some/ de Magento.

Définir et utiliser des conteneurs

Les conteneurs sont utilisés pour définir des espaces dans le layout. Ces espaces peuvent alors être alimentés par plusieurs modules dans leurs fichiers de layout respectifs en appelant comme “reference” le nom du conteneur.

M2

M2 offre la possibilité de déclarer des conteneurs grâce au noeud container du layout XML. Le système “poupées russes” fait que seuls les block sont interprétés en HTML.

<container name="my_module.blocks_container">  
    <block  
  class="Magento\Framework\View\Element\Template"  
  name="my_module.nested_block_1"  
  template="My_Module::some/template.phtml"  
  />  
    <block  
  class="Magento\Framework\View\Element\Template"  
  name="my_module.nested_block_2"  
  template="My_Module::some/template.phtml"  
  />  
    <container name="my_module.blocks_container_child">  
        <block  
  class="Magento\Framework\View\Element\Template"  
  name="my_module.nested_block_3"  
  template="My_Module::some/template.phtml"  
  />  
    </container>  
</container>

M1

On utilise des block de type core/text_list ou page/html_wrapper par exemple.

<block type="core/text_list" name="my_module.blocks_container">  
    <block  
  type="core/template"  
  name="my_module.nested_block_1"  
  template="my_module/some/template.phtml"  
  />  
    <block  
  type="core/template"  
  name="my_module.nested_block_2"  
  template="my_module/some/template.phtml"  
  />  
    <block type="core/text_list" name="my_module.blocks_container_child">  
        <block  
  type="core/template"  
  name="my_module.nested_block_3"  
  template="my_module/some/template.phtml"  
  />  
    </block>  
</block>

Déplacer un élément

M2

Le layout de M2 propose un noeud move. Dans lequel on passe :

  • le nom du block à déplacer dans l’argument element
  • le nom du nouveau conteneur dans l’argument destination

Exemple pour déplacer la navigation dans le footer (totalement déconseillé !) :

<move element="navigation.sections" destination="footer" />

M1

En couplant l’utilisation de unsetChild et insert.

D’autres implémentations sont possibles mais voici un exemple pour, à nouveau, déplacer la navigation dans le footer !

<reference name="top.menu">  
    <action method="unsetChild">  
        <name>catalog.topnav</name>  
    </action>  
</reference>  


<reference name="bottom.container">  
    <action method="insert">  
        <name>catalog.topnav</name>  
    </action>  
</reference>

Supprimer un bloc

Dans M 2 comme dans M1, supprimer un bloc le rend indisponible dans l’ensemble du layout. Appeler remove une fois dans un fichier XML supprime le bloc sur l’ensemble des pages affectées par le layout handle dans lequel le remove est déclaré.

M2

Pour supprimer la navigation :

<body>
	...
    <referenceBlock name="navigation.sections" remove="true" />
    ...

M1

<default>  
    ...
    <remove name="catalog.topnav"/>
    ...

Changer la classe utilisée par un bloc

M 2 et M1 ne proposent pas de solution pour affecter facilement une nouvelle classe à un bloc déjà déclaré.

Même s’il est possible de déclarer un bloc avec le même nom que celui dont on veut changer la classe (et donc le remplacer), il reste nécessaire de reconstruire le layout avec les blocs enfants à afficher.

Voici une implémentation comparative pour ajouter de la logique dans la gestion de la balise alt du logo.

M2

Dans [my_module]/view/frontend/layout/default.xml :

<referenceContainer name="header-wrapper">  
    <block class="My\Module\Block\Html\Header\Logo" name="logo">  
        <arguments>  
            <argument name="logo_width" xsi:type="number">189</argument>  
            <argument name="logo_height" xsi:type="number">64</argument>  
        </arguments>  
    </block>  
</referenceContainer>

Dans \My\Module\Block\Html\Header\Logo :

namespace My\Module\Block\Html\Header;  
  
class Logo extends \Magento\Theme\Block\Html\Header\Logo  
{  
protected $_template = 'Magento_Theme::html/header/logo.phtml';  
  
public function getLogoAlt()  
 {  
	 return __('My custom logo alt');  
 }
}

M1

Dans app/design/[area]/[package]/[theme|/layout/my_module.xml:

<reference name="root">  
    <block type="my_module/page_html_header" name="header" as="header">   
        <block type="page/template_links" name="top.links" as="topLinks"/>  
        <block type="page/switch" name="store_language" as="store_language" 
        template="page/switch/languages.phtml"/>  
        <block type="core/text_list" name="top.menu" as="topMenu" translate="label">  
            <label>Navigation Bar</label>  
            <block type="page/html_topmenu" name="catalog.topnav" 
        template="page/html/topmenu.phtml"/>  
        </block>  
        <block type="page/html_wrapper" name="top.container" as="topContainer" translate="label">  
            <label>Page Header</label>  
            <action method="setElementClass"><value>top-container</value></action>  
        </block>  
        <block type="page/html_welcome" name="welcome" as="welcome"/>  
    </block>  
</reference>

Dans My_Module_Block_Page_Html_Header :

class My_Module_Block_Page_Html_Header extends Mage_Page_Block_Html_Header  
{  
  public function getLogoAlt()  
 { 
	 return $this->__('My custom logo alt');   
 }
}

Passer des données du layout vers le bloc

M2

Grâce à l’implémentation de la dependency injection, chaque bloc peut recevoir des données via le layout. Les données passées depuis le layout sont parsées puis peuplent la propriété $_data (clé =>valeur) du bloc.

Passer un argument à un bloc dans le layout :

<block class="My\Module\Block\Html\Header\Logo" name="logo">  
    <arguments>  
        ... 
        <argument name="my_module_custom_logo_alt" xsi:type="string">My layout logo alt</argument>  
        ... 
    </arguments>  
</block>

Puis récupérer la valeur de my_module_custom_logo_alt dans My\Module\Block\Html\Header\Logo (ou son template associé) :

$this->getData('my_module_custom_logo_alt')
// ou $block->getData('my_module_custom_logo_alt')` dans un template

M1

Il est nécessaire d’appeler <action method="setData" /> afin d’ajouter une valeur dans la propriété $_data du bloc.

Ainsi, dans le layout :

<block type="my_module/page_html_header" name="header" as="header">  
	...
    <action method="setData">  
        <key>my_module_custom_logo_alt</key>  
        <value>My layout logo alt</value>  
    </action>
    ...

Puis récupérer la valeur de my_module_custom_logo_alt dans My_Module_Block_Page_Html_Header (ou son template associé) :

$this->__($this->getData('my_module_custom_logo_alt'));
// ou 
// $this->__($this->getMyModuleCustomLogoAlt());

Appeler les méthodes publiques des blocs depuis le layout

M2

Bien que ceci reste possible, appeler explicitement des méthodes depuis le layout est déprécié.

On préférera utiliser les arguments comme précisé plus haut.

M1

En utilisant <action method="laMethode" sur un block.

Exemple pour appeler la méthode public function laMethode() du block My_Module_Block_Page_Html_Header :

<block type="my_module/page_html_header" name="header" as="header">  
    <action method="laMethode" />
    ...

Utiliser des helpers

L’usage des helpers est intéressant quand :

  • on cherche à injecter dans le layout les valeurs issues de logiques plus complexes
  • on est en phase de refactorisation, dans l’objectif de renforcer le respect du principe DRY

M2

Passer un argument à un bloc dans le layout en renseignant la méthode du helper à appeler :

<block class="My\Module\Block\Html\Header\Logo" name="logo">  
    <arguments>  
        ... 
        <argument name="my_module_custom_logo_alt_advanced" xsi:type="helper" 
        helper="My\Module\Helper\Logo::getAlt"/>  
        ...
    </arguments>  
</block>

Puis pour \My\Module\Helper\Logo :

namespace My\Module\Helper;  
  
class Logo  
{  
  /**  
 * @return \Magento\Framework\Phrase  
 */  public function getAlt()  
 {  
	 return __('Alt from Helper');  
 }
}

M1

Utiliser setData et déclarer un helper pour la valeur à affecter au format [module_helper_factory_name]/[helper_class_path]/[method_in_helper]:

<block type="my_module/page_html_header" name="header" as="header">  
    ...  
    <action method="setData">  
        <key>my_module_custom_logo_alt_advanced</key>  
        <value helper="my_module/logo/getAlt" />  
    </action>
    ...

Puis pour My_Module_Helper_Logo :

class My_Module_Helper_Logo extends Mage_Core_Helper_Abstract  
{  
  public function getAlt()  
 {  
	 return $this->__('Alt from Helper');  
 }
}

Combiner les layouts avec update handle

On utilisant update handle on peut inclure des instructions présentes dans d’autres layout handles. On peut ainsi :

  • Déclarer, dans un fichier à part, des éléments communs prêts à être utilisés dans d’autres layout handles (les fichiers JS, PHTML, … d’un carousel par exemple)
  • Mettre à jour des déclarations présentes dans d’autres layout handles

En somme, on combine plusieurs layout handles avec toutes les instructions d’ajout, modifications, déplacements, suppressions qu’elles contiennent.

M2

Dans la mesure où M2 charge automatiquement tous les fichiers XML, il suffit de créer un nouveau fichier dont le nom correspond à l’argument handle utilisé dans l’instruction update handle.

Ainsi, pour une layout handle nommée my_module_common_blocks, il faut créer le fichier view/[area]/layout/my_module_common_blocks.xml.

On pourra alors réutiliser les instructions présentes dans my_module_common_blocks.xml dans les autres layouts en appelant update handle directement sous la déclaration page du layout :

<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">  
    <update handle="my_module_common_blocks"/>  
    <body>  
        ...
    </body>  
</page>

M1

Il est d’abord nécessaire de demander à Magento de charger un fichier de layout complémentaire.

L’exemple ci-dessous permet de définir 2 fichiers de layout pour le même module ; en modifiant la déclaration dans config.xml :

<frontend>  
    <layout>  
        <updates>  
            <my_module_common module="My_Module">  
                <file>my_module_common_blocks.xml</file>  
            </my_module_common>  
            <my_module_layout module="My_Module">  
                <file>my_module.xml</file>  
            </my_module_layout>  
        </updates>  
    </layout>  
</frontend>

Il est aussi nécessaire de correctement déclarer la layout handle my_module_common_blocks dans le fichier my_module_common_blocks.xml :

<layout version="0.1.0">  
    <my_module_common_blocks>  
        <reference name="...">  
        ...
    </my_module_common_blocks>  
</layout>

On pourra alors réutiliser les instructions présentes dans my_module_common_blocks.xml dans les autres layouts en appelant update handle en enfant de la layout handle à combiner.

Exemple pour combiner default et my_module_common_blocks :

<layout version="0.1.0">  
    <default>  
        <update handle="my_module_common_blocks"/>  
        ... 
    </default>  
</layout>

Pour aller plus loin

Nous espérons que le concept des layout appliqués à M2 est plus clair pour vous, et vous donnons rendez-vous prochainement pour le prochain tutorial technique !

Pour en savoir plus, découvrez notre agence ecommerce spécialisée Magento.

×

Au-revoir Soon,
bienvenue Kaliop Digital Commerce !

L’agence Soon a changé de nom, mais pas d’ADN.

Porté par la même équipe d’experts amoureux du e-commerce,
Kaliop Digital Commerce, c’est toujours plus d’innovations
et de valeur-ajoutée pour vos projets de commerce connecté.

En savoir plus

Sophie Bertrand - Directrice commerciale Kaliop Digital Commerce