Mois : janvier 2020

[Magento 2] Créer un Helper pour récupérer sous forme d’objet dans un template PHTML des informations (ID, nom…) sur la catégorie produit en cours

Testé fonctionnel Magento 2.3!

ATTENTION: bien que cette méthode soit fonctionnelle, ce billet consigne mes notes sur la façon de créer un Helper pour récupérer sous forme d’objet dans un template PHTML des informations (ID, nom…) sur catégorie produit en cours. Les fichiers ne sont pas forcément placés aux bons endroits par rapport aux bonnes pratiques Magento 2.

Créer un module

Sodifrance_GetCurrentCategory

app/code/Sodifrance/GetCurrentCategory/etc/di.xml

app/code/Sodifrance/GetCurrentCategory/Model/Layer/Resolver.php

Hérité de vendor/magento/module-catalog/Model/Layer/Resolver.php.

Créer un module

Pdv_Catalog

app/code/Pdv/Catalog/Helper/Data.php

Hérité de vendor/magento/module-catalog/Helper/Data.php.

app/design/frontend/Sodifrance/pdv/Magento_Catalog/templates/product/list_abonnements.phtml

Hérité de vendor/magento/module-catalog/view/frontend/templates/product/list.phtml.

Dans le bloc de code suivant:

…mettre à la suite:

Pour récupérer le nom ou l’ID de la catégorie en cours

La majeure partie des informations récupérables est définie ici: vendor/magento/module-catalog/Model/Category.php. Mais précisément pour le nom et l’ID de la catégorie en cours:

Vous pouvez également utiliser les fonctionnalités d’auto-complétion de votre IDE pour avoir accès à toute la liste des informations contenues dans votre objet PHP $currentCategory en commençant à taper <?php echo $currentCategory->get.

[Magento 2] PHP dans les fichiers PHTML

Les conventions utilisées dans les templates PHP (PHTML) de Magento 2

  • $this no longer applies to the rendering block. Use $block or $block>getData('view_model') to obtain access to the instigating object or its data.
  • Always type hint variables that are automatically imported ($block, $viewModel).
  • Never use squiggly braces: this is a code smell that indicates your block or view model should be doing more work.
  • If you need to use a loop, use the foreach > endforeach constructs (de cette manière: <?php foreach () : ?> puis <?php endforeach ?>).
  • Keep templates to a reasonable minimum. Massive 500 line files are a code smell.
  • Magento now uses <?= instead of <?php echo.
  • Always translate strings in templates that will be displayed to user.
  • Magento templates do not use the if(){ /* … */ } constructs because squiggly braces are harder to discern amongst HTML. A la place, utiliser <?php if (): ?> suivi de <?php else: ?> ou directement de <?php endif; ?>.

Les méthodes communément disponibles pour la variable $block

  • getRootDirectory()
  • getMediaDirectory()
  • getUrl()
  • getBaseUrl()
  • getChildBlock($alias)
  • getChildHtml($alias, $useCache = true)
  • getChildChildHtml($alias, $childChildAlias = '', $useCache = true): this method returns the HTML from a grandchild block.
  • getChildData($alias, $key = ''): calls getData on a child block.
  • formatDate($date = null, $format = \ IntlDateFormatter::SHORT, $showTime = false, $timezone = null)
  • formatTime($time = null, $format = \ IntlDateFormatter::SHORT, $showDate = false)
  • getModuleName()
  • escapeHtml($data, $allowedTags = null)
  • escapeJs($string)
  • escapeHtmlAttr($string, $escapeSingleQuote = true)
  • escapeCss($string)
  • stripTags($data, $allowableTags = null, $allowHtmlEntries = false)
  • escapeUrl($string)
  • getVar($name, $module = null): locates a value from the theme’s etc/view.xml

Comment afficher un child block?

Comment afficher tous les child block?

Afficher un argument défini via un layout XML

Source: Magento 2: Adding Frontend Assets via Layout XML (Alan Storm)

La valeur de test_value sera disponible via $block->getData('test_value') ou $block->getTestValue().

Rendu sécurisé des templates, contenus HTML / JS et Urls

  • HTML: $block->escapeHtml(‘value’, $allowedTags);
  • HTML attributes: $block->escapeHtmlAttr(‘value’, $escapeSingleQuote);
  • JavaScript: $block->escapeJs(‘value’);
  • URLs: $block->escapeUrl($url);

Si la condition d’un booléen est true, afficher quelque chose…

Nous avons créé un attribut produit custom que nous avons nommé best_offer. Il s’agit d’un booléen. Nous allons commencer par stocker sa valeur dans une variable $bestOffer:

Puis, lorsque nous avons besoin d’afficher quelque chose de spécifique si la valeur de la variable $bestOffer est true (attribut configuré à oui en back-office):

Fonction PHP de gestion des variables empty()

Source: Fonctions de gestion des variables: empty().

Nous avons créé un attribut produit custom que nous avons nommé offer_content. Il s’agit d’un champ texte. Nous allons commencer par stocker sa valeur dans une variable $offerContent:

Puis, si la variable ne vaut pas 0, n’est pas vide, est définie, afficher sa valeur:

Utitiser la fonction PHP strst() pour supprimer une partie d’une chaîne de caractères jusqu’à un caractère spécifié

Ici, pour la chaîne de caractère toto-titi, je veux conserver -titi uniquement:

Utitiser la fonction PHP str_replace() pour supprimer un caractère spécifié

Ici, pour la chaîne de caractère -titi, je veux retirer le caractère - pour conserver titi uniquement:

[Magento 2] Surcharger override un layout ou un page layout XML

Ressources utiles:

Pour surcharger app/design/frontend/Mgs/supro/Magento_Catalog/layout/catalog_category_view.xml (qui est déjà une surcharge) dans un thème, il faut créer l’arborescence et le fichier suivants: app/design/frontend/Sodifrance/pdv/Magento_Catalog/layout/override/theme/Mgs/supro/catalog_category_view.xml.

Surcharger un page_layout

Un page_layout est un gabarit de mise en page. Exemple: 2columns-left, 2columns-right, .... On trouve ces gabarits par défaut dans vendor/magento/module-theme/view/frontend/page_layout/.

Si je veux surcharger le page_layout 2columns-left.xml

Je dois créer le fichier suivant: app/design/frontent/MyVendor/mytheme/Magento_Theme/page_layout/override/base/2columns-left.xml (attention à la partie override/base/).

[Magento 2] Manipuler le rendu des prix

Ressources utiles:

Astuces de ce billet:

Mise en garde!

Dans Magento 2, l’affichage des différents types de prix dans les différentes vues (liste produit, résultats de recherche, comparatif, fiche produit, …) est toujours accompagné d’un markup HTML assez complexe (ci-dessous un exemple récupéré dans le DOM d’une fiche produit, mais qui est identique dans une liste produit) juste pour afficher la valeur de 8,90 €:

Dans les cas où vous devez changer le markup HTML, les classes et autres attributs pour personnaliser votre Magento SOYEZ EXTREMEMENT PRUDENTS car la présence de chacun des éléments précités a des répercussions sur le bon fonctionnement de votre site.

Modifier l’élément HTML <div /> et sa classe price-box qui englobent l’affichage du prix dans les vues produit de Magento 2

Edit 05/02/2020: en faisant un grep -rn --exclude=\*.{less,css} "price-box", j’ai obtenu des résultats de type dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Product/Compare/ListCompare.php:52:    protected $priceSelector = './/div[contains(@class,"price-box")]';.

Ceci sous-entend que des fonctionnalités sont attachées à la price-box uniquement lorsque la classe price-box est affectée à l’élément HTML div. Changer cet élément en span peut avoir des effets de bord sour les fonctionnalités rattachées à la price-box de votre projet Magento 2.

Ainsi, je déconseille fortement la mise en place de cette astuce dans vos projets. Conservez ici l’élément div voulu par les développeurs de Magento 2 et utilisez flexbox ou display: inline-block; pour aligner vos prix.

Testé fonctionnel Magento 2.3!. Code source du module: magento2_customPriceBox

Figurez vous que l’élément <div class="price-box" /> qui embarque tout ce qu’il faut pour afficher un prix en bonne et due forme dans Magento 2 est généré via le fichier vendor/magento/module-catalog/Pricing/Render/FinalPriceBox.php! Dans notre exemple, nous allons simplement chercher à remplacer l’élément HTML div par un span. Pour ce faire, pas le choix: il faut créer un module pour étendre la classe PHP FinalPriceBox et la fonction wrapResult().

La méthode est la même que pour surcharger un Block natif dans Magento 2. Je ne vais donc pas la décrire en détail ici. Vous pouvez vous référer au billet pré-cité pour plus de détails.

Convention: Dans notre exemple, nous travaillerons avec le vendor Sodifrance et le module CustomPriceBox.

Il faudra commencer par créer un nouveau module Sodifrance/CustomPriceBox.

app/code/Sodifrance/CustomPriceBox/etc/di.xml

app/code/Sodifrance/CustomPriceBox/Plugin/FinalPriceBox.php

Ce fichier vient étendre la classe définie dans vendor/magento/module-catalog/Pricing/Render/FinalPriceBox.php.

Puis taper les commandes habituelles:


Modifier le markup autour du prix

Dans Magento 2, le code qui englobe l’affichage du prix dans les vues produit est éclaté en une multitude de petits fichiers PHTML. A l’exception de l’élément <div /> et sa classe price-box, ces derniers sont tous déclarés dans le layout XML vendor/magento/module-catalog/view/base/layout/catalog_product_prices.xml.

Il faudra donc surcharger ce fichier en priorité pour modifier l’affichage de vos prix. Vous pourrez ensuite modifier les cibles des <item name="render_template" xsi:type="string" /> définis ici pour les faire pointer vers vos templates PHTML custom.

[Magento 2] Afficher les descriptions courte et complète des produits dans la page de liste produits

Testé fonctionnel Magento 2.3! Source: How to get products description and short description in products list page?

[Magento 2] Afficher la description courte d’un produit dans la liste produits

Dans un template PHTML hérité de vendor/magento/module-catalog/view/frontend/templates/product/list.phtml mettre le paramètre $showDescription à true pour le mode d’affichage désiré (grid ou list – j’ai mis les deux à true dans l’exemple ci-dessous):

[Magento 2] Afficher la description détaillée d’un produit dans la liste produits

Pas testé! Toujours dans un template PHTML hérité de vendor/magento/module-catalog/view/frontend/templates/product/list.phtml, ajouter le code suivant:

[Magento 2] Afficher un layout différent selon une catégorie spécifique

Astuce testée fonctionnelle Magento 2.3!

Afficher un page layout différent selon une catégorie de produits spécifique

Ce que je veux: j’ai plusieurs catégories de produits dans ma boutique Magento 2. L’affichage des listes produits de l’une de ces catégories (la catégorie Abonnements dans ce tutoriel) doit bénéficier d’un affichage complètement différent par rapport aux listes produits des autres catégories de mon site Magento 2, dont l’affichage restera « standard ».

Nativement, c’est l’articulation du layout vendor/magento/module-catalog/view/frontend/layout/catalog_category_view.xml et du template vendor/magento/module-catalog/view/frontend/templates/product/list.phtml qui génèrent l’affichage de l’ensemble des listes produits dans les pages du catalogue d’une boutique Magento 2.

Nous allons créer un nouveau page layout XML qui servira à rediriger vers un template PHTML de liste spécifique. Une action en backoffice sera nécessaire pour affecter ce nouveau page layout aux catégories concernées.

Conventions pour ce tuto: je pars du principe qu’il existe un Vendor/theme Sodifrance/pdv dans votre arborescence de fichiers et que la catégorie pour laquelle nous cherchons à surcharger l’affichage s’appelle Abonnements.

Commencer par déclarer un nouveau page layout XML dans votre thème

Chemin vers ce fichier: app/design/frontend/Sodifrance/pdv/Magento_Theme/layouts.xml

Créer ensuite le nouveau page layout XML abonnements_subcategory.xml correspondant

Source: Magento2: Add a new page layout.

Nous allons créer un page layout XML spécifique app/design/frontend/Sodifrance/pdv/Magento_Theme/page_layout/abonnements_subcategory.xml.

L'<update handle="empty"/> et le contenu du <referenceContainer name="page.wrapper"> sont copiés/collés du page layout vendor/magento/module-theme/view/frontend/page_layout/1column.xml dont je veux reprendre les bases. Aucun merge n’étant prévu par Magento 2 à ce niveau (jusqu’à ce qu’on me contredise, je ne peux déclarer nulle part que mon nouveau layout abonnements_subcategory.xml doit hériter d’un parent existant column1.xml), je suis obligé de faire du copier/coller, sinon mon front-end n’affiche plus rien…

Créer un template PHTML custom list_abonnements.pthml dans votre thème

Ce template sera une surcharge du fichier vendor/magento/module-catalog/view/frontend/templates/product/list.phtml, située dans app/design/frontend/Sodifrance/pdv/Magento_Catalog/templates/product/list_abonnements.phtml et dans laquelle vous consignerez les spécificités d’affichage liées à la catégorie Abonnements de votre boutique.

Je ne vous mets pas d’exemple de code ici. Votre projet n’aura pas les mêmes spécificités que le mien.

Enfin, rajouter un referenceContainer à notre list_abonnements.phtml spécifique

Source: Different list.phtml for categorys in the backend of Magento 2.1.7.

Sur les bases du fichier app/design/frontend/Sodifrance/pdv/Magento_Theme/page_layout/abonnements_subcategory.xml crée précédemment, nous allons ajouter un referenceContainer à notre template PHTML de liste spécifique app/design/frontend/Sodifrance/pdv/Magento_Catalog/templates/product/list_abonnements.phtml.

Affecter le nouveau layout à vos catégories via l’interface d’admin

En backoffice: Catalogue > Catégories > choisir une catégorie ou une sous-catégorie dans l’arborescence de votre catalogue > volet Design > champ Agencement > sélectionner votre page layout ([PDV] Abonnements (sous-catégorie) dans mon exemple).


Autre méthode

Source: Different template for different categories in magento 2 [category_id layout handle in M2].

Il ne s’agit pas à proprement parler d’un override. Il faut créer un fichier XML catalog_category_view_id_{{id}}.xml sous app/design/frontend/<vendorName>/<themeName>/Magento_Catalog/layout/, où {{id}} est l’ID de votre catégorie (visible en admin sous Catalogue > Catégories > choisir la catégorie/sous-catégorie).

Commande à exécuter pour voir les modifs en front: $ n98-magerun2 cache:clean.

[Magento 2] Exploiter la fonctionnalité CSS critical path avec un fichier LESS (via Grunt)

Versions de Magento 2.3.3+: Petit topo sur le CSS critical path dans la doc front-end officielle.

La fonctionnalité est limitée, nativement, à l’utilisation d’un fichier au format CSS (pas de LESS).

Pour contourner cette limitation, créez un fichier critical.less dans app/design/frontend/Vendor/theme/web/css/critical.less et déclarez ce dernier dans votre config Grunt (dev/tools/grunt/configs/local-themes.js):

Puis lancez les commandes:

[Magento 2] Rendre accessibles des styles custom dans l’éditeur WYSIWYG TinyMCE

Solution testée fonctionnelle Magento 2.3!

Votre charte graphique prévoit sans doutes des styles spécifiques pour vos titres. Ces styles sont affectés via des classes CSS aux éléments Hx et ne font pas l’objet d’une prise en compte automatique par l’éditeur WYSIWYG TinyMCE 4 fourni dans Magento 2.

Convention: dans nos examples, le <vendorNamespace> sera Sodifrance et le <moduleName> sera Customtinymce. Le code source de ce module Tiny MCE 4 custom est disponible ici au téléchargement.

Créer un nouveau module

La commande ci-dessous va créer un nouveau module et initier tous les fichiers de base nécessaires à la configuration de ce dernier.

Editer le fichier app/code/Sodifrance/Customtinymce/registration.php

Editer le fichier app/code/Sodifrance/Customtinymce/etc/di.xml

Editer le fichier app/code/Sodifrance/Customtinymce/etc/module.xml

Créer le fichier app/code/Sodifrance/Customtinymce/Plugin/Config.php

Solution partiellement trouvée ici (si l’on excepte l’option $settings['style_formats'] = [ ... ]; que j’ai rajouté moi): Magento 2.3 – TinyMCE4 Toolbar and Plugin Configuration. Attention: cette solution a ensuite été reprise, améliorée et rendue disponible par Magefan sur son repository Git.

Dans le fichier ci-dessus, vous pouvez éditer la partie suivante à votre convenance pour réorganiser votre éditeur WYSIWYG:

Mettre en place vos paramètres TinyMCE 4 custom dans Magento 2 (exemple avec l’option style_formats)

Avec Magento, exit l’initialisation de votre plugin en front via javaScript avec passage d’un objet pour paramétrer le tout. Les options natives de TinyMCE v4 pour formater le contenu sont prises en charge, mais il faut les déclarer dans le fichier app/code/Sodifrance/Customtinymce/etc/module.xml, en PHP.

Aussi, pour l’option <a href="https://www.tiny.cloud/docs-4x/configure/content-formatting/#style_formats">style_formats</a> qu’on utiliserait nativement de cette manière:

…l’heureux développeur Magento 2 se voit contraint et forcé de déclarer ses paramètres de cette manière:

Quelques outils pour convertir un tableau JSON en tableau PHP

(puisqu’on est obligé de travailler comme ça, hein…)

Tester la validité de votre tableau avec PHP Sandbox

Dans le champ « script », tapez ceci et activez le bouton « Execute code »:

Le champ « Result » doit vous retourner un JSON correct:

Tester la validité de données JSON avec JSON Formatter & Validator

Dans le champ « JSON Data/Url », collez le code JSON retourné juste au-dessus par PHP Sandbox et cliquer sur le bouton « Process ».

Passée la ligne ci-dessous, les devs sont expérimentaux et à ne pas utiliser en prod.


ATTENTION! La solution présentée, bien que fonctionnelle, est absolument dégueulasse en terme de bonnes pratiques. Elle se révélera notamment assez compliquée à maintenir.

Vous pouvez télécharger le code source du module permettant d’ajouter des styles custom (style_formats) à l’éditeur WYSIWYG TinyMCE 4 pour Magento 2 et le tester immédiatement dans votre projet en tapant les commandes suivantes (attention: le zip contient 2 modules; c’est le module Customtinymce qu’il faut utiliser):

Mes sources d’inspiration pour parvenir à mes fins:

Dans app\code\Sodifrance\Customtinymce\view\adminhtml\web\js\customtinymce.js (qui n’est autre que la version 4.9.5 native du plugin TinyMCE4 (DEV package, code non minifié)), passez les paramètres style_formats custom que vous souhaitez ajouter dans la variable getDefaultSettings:

Dans le fichier app/code/Sodifrance/Customtinymce/view/base/requirejs-config.js, il faut ensuite mapper l’alias tinymce4 (initialement défini ici: vendor/magento/module-ui/view/base/requirejs-config.js) vers notre surcharge de la source du plugin TinyMCE4:

[Magento 2] Etendre les possibilités de l’éditeur WYSIWYG TinyMCE 4

Par défaut, l’éditeur WYSIWYG TinyMCE 4 proposé par Magento 2 n’inclut pas une bonne partie des fonctionnalités embarquées par la librairie TinyMCE 4 (couleur du texte, du fond, …) qui peuvent être utiles pour un rédacteur de contenu. Je prends pour exemple les Custom Formats (Demo CodePen), très utiles lorsque vous créez des styles qui reposent sur un assemblage d’éléments HTML et de classes.

Solution #1: Utiliser le plugin Magefan module-wysiwyg-advanced

Testé fonctionnel Magento 2.3.3. Attention: le plugin ne fournissant aucune licence, il est déconseillé de l’utiliser sur des projets que vous vendez.

Vous pouvez utiliser le plugin Better Magento 2 WYSIWYG TinyMCE4 Editor par Magefan.

Solution #2: Créer votre module d’extension du WYSIWYG TinyMCE 4 pour Magento 2 from-scratch

Testé fonctionnel Magento 2.3.3. Source: Magento 2.3 – TinyMCE4 Toolbar and Plugin Configuration. Créer plusieurs fichiers:

Convention: dans notre exemple, Project et project sont à remplacer par le libellé de votre Vendor.

Create the directory [app/code/vendor/module]: app/code/Project/Customtinymce

Create app/code/Project/Customtinymce/etc/di.xml:

Create app/code/Project/Customtinymce/etc/module.xml:

Create app/code/Project/Customtinymce/registration.php

Create the after plugin app/code/Project/Customtinymce/Plugin/Config.php:

Remarque: dans ce fichier Config.php, si on désactive tous les $settings[''] = ; (comme ci-dessous), c’est les fonctionnalités prévues par défaut dans Magento 2 qui s’afficheront dans vos champs d’éditeurs WYSIWYG en admin.

Lancer les commandes:

[Magento 2] Surcharger un Block ou un Model natif

Testé fonctionnel Magento 2.3

Sources: www.magestore.com – Overriding Block, Model In Magento 2 – Magento 2.3, Surcharger une classe native magento2 (Model, Block, Helper, Action…).

(si ce n’est pas fait) Créer un nouveau Module

En ligne de commande:

…puis:

Surcharger un Block natif dans Magento 2

Convention: dans l’exemple ci-dessous, nous surchargeons le Block Magento\Catalog\Block\Product\ListProduct.php.

Créer le fichier di.xml dans app/code/[Name_Space]/[Your_Module]/etc:

Dans le fichier ci-dessus:

  • l’attribut for de la balise preference déclare le namespace + \ + le nom de la classe du Block initial avec, en dernier, le libellé du fichier sans l’extension *.php. Dans notre exemple, le chemin vers le fichier natif est vendor/magento/module-catalog/Block/Product/ListProduct.php.
  • l’attribut type de la balise preference déclare le chemin, à partir de la racine de votre projet et sans les deux premiers dossiers app/code/, vers le Block qui surcharge avec, en dernier, le libellé du fichier sans l’extension *.php

Créer le fichier Block ListProduct.php dans app/code/[Name_Space]/[Your_Module]/Block/Rewrite/Product:

Dans le fichier ci-dessus:

  • le namespace déclare le chemin vers le Block qui surcharge, cette fois-ci sans le nom du fichier. Ici, on peut reprendre une partie de la valeur déclarée pour l’attribut type de la balise preference du fichier di.xml (sans le nom du fichier à la fin, donc…)
  • le libellé de la class déclarée reprend celui du fichier qui surcharge (le libellé du fichier qu’on est justement en-train de créer ou d’éditer) sans l’extension *.php
  • le chemin déclaré après extends est celui vers le Block initial avec, en dernier, le libellé du fichier sans l’extension *.php. Ici, on peut reprendre l’intégralité de la valeur déclarée pour l’attribut for de la balise preference du fichier di.xml, mais ATTENTION: il faut impérativement rajouter un anti-slash \ devant ce dernier!

Commandes Magerun à exécuter impérativement:

A la création de votre module (la première fois):

Puis à chaque modification dans le fichier di.xml:

Surcharger un Model natif dans Magento 2

Convention: dans l’exemple ci-dessous, nous surchargeons le Model Magento\Catalog\Model\Product.php.

Créer le fichier di.xml dans app/code/[Name_Space]/[Your_Module]/etc:

Dans le fichier ci-dessus:

  • l’attribut for de la balise preference déclare le namespace + \ + le nom de la classe du Model initial avec, en dernier, le libellé du fichier sans l’extension *.php. Dans notre exemple, le chemin vers le fichier natif est vendor/magento/module-catalog/Model/Product.php.
  • l’attribut type de la balise preference déclare le chemin, à partir de la racine de votre projet et sans les deux premiers dossiers app/code/, vers le Model qui surcharge avec, en dernier, le libellé du fichier sans l’extension *.php

Créer le fichier Model Product.php dans app/code/[Name_Space]/[Your_Module]/Model/Rewrite/Catalog:

Dans le fichier ci-dessus:

  • le namespace déclare le chemin vers le Model qui surcharge, cette fois-ci sans le nom du fichier. Ici, on peut reprendre une partie de la valeur déclarée pour l’attribut type de la balise preference du fichier di.xml (sans le nom du fichier à la fin, donc…)
  • le libellé de la class déclarée reprend celui du fichier qui surcharge (le libellé du fichier qu’on est justement en-train de créer ou d’éditer) sans l’extension *.php
  • le chemin déclaré après extends est celui vers le Model initial avec, en dernier, le libellé du fichier sans l’extension *.php. Ici, on peut reprendre l’intégralité de la valeur déclarée pour l’attribut for de la balise preference du fichier di.xml, mais ATTENTION: il faut impérativement rajouter un anti-slash \ devant ce dernier!

Commandes Magerun à exécuter impérativement:

A la création de votre module (la première fois):

Puis à chaque modification dans le fichier di.xml: