Catégorie : Magento 2

[Magento 2] Un widget pour améliorer les performances d’affichage des produits dans les modules du pagebuilder dans l’admin

  • app/code/Vendor/PageBuilder/view/adminhtml/web/js/content-type/preview-mixin.js
  • app/code/Vendor/PageBuilder/view/adminhtml/web/js/utils/prod-all.js
  • app/code/Vendor/PageBuilder/view/adminhtml/web/js/utils/prod-delayed-loading.js
  • app/code/Vendor/PageBuilder/view/adminhtml/web/js/content-type/module-13/preview.js
  • app/code/Vendor/PageBuilder/view/adminhtml/web/js/content-type/module-13-item/preview.js
  • app/code/Vendor/PageBuilder/view/adminhtml/web/template/content-type/module-13-item/default/preview.html
  • app/code/Vendor/PageBuilder/view/frontend/templates/widget/product/simple.phtml
  • app/code/Vendor/PageBuilder/view/adminhtml/web/js/resource/vendor/sly.js (Librairie 3-rd party Sly v1.6.1)

app/code/Vendor/PageBuilder/view/adminhtml/web/template/content-type/module-13-item/default/preview.html

app/code/Vendor/PageBuilder/view/adminhtml/web/js/content-type/preview-mixin.js

Pour l’ajout de notre widget Vendor_PageBuilder/js/utils/prod-delayed-loading en tant que composant:

…et en toute fin de fichier, l’initialisation du composant:

app/code/Vendor/PageBuilder/view/adminhtml/web/js/utils/prod-all.js

Un module façon requireJS pour stocker des fonctions amenées à être utilisées dans plusieurs fichiers:

app/code/Vendor/PageBuilder/view/adminhtml/web/js/utils/prod-delayed-loading.js

Dans ce module, de nombreuses utilisations de la variable widget qui contient, sous forme de string, les données nécessaires à l’affichage du produit:

  • La valeur de l’attribut entity correspond l’ID du produit dans le catalogue. Je la récupère via une regex dans la fonction getProductId consignée dans le fichier app/code/Vendor/PageBuilder/view/adminhtml/web/js/utils/prod-all.js.
  • La valeur de l’attribut template correspond à la vue dans laquelle les données vont être affichées, en front comme en admin.

app/code/Vendor/PageBuilder/view/adminhtml/web/js/content-type/module-13/preview.js

Ajout d’une fonctionnalité « Show All » dans les options du module:

Plus bas, définition de la fonction showAll (noter comment l’habituel $ de jQuery est remplacé par (0, _jquery)):

app/code/Vendor/PageBuilder/view/adminhtml/web/js/content-type/module-13-item/preview.js

Points d’intérêt pour explorer et étendre le code:

_events.on("<em><nom_du_module></em>:renderAfter", function (args) { correspond à différents événements (:renderAfter, :updateAfter, …) qui se déclenchent au chargement du module en BO.

Les objets contentType (cf. bout de code suivant) et args contiennent également pas mal d’infos exploitables.

afterObservablesUpdated: le code qui s’exécute après que l’utilisateur ait cliqué sur le bouton « Save » (j’ai par exemple ajouté l’exécution d’une fonction setProductId qui ajoute un data-attribute dans le DOM, avec pour valeur un ID de produit):

this.contentType.id me donne l’ID du produit sur lequel j’ai itéré en sauvegardant.

app/code/Vendor/PageBuilder/view/frontend/templates/widget/product/simple.phtml

[Magento 2] S’orienter dans les sources du Page Builder

La demande était d’ajouter, en front et en back, un widget existant sur plusieurs modules spécifiques du PageBuilder de Magento 2/Adobe Commerce.

Checkliste des fichiers sur lesquels passer:

Relatif au global, modifié:

  • app/design/frontend/MyVendor/default/Magento_Theme/web/js/widgets/prod-popover.js
  • app/design/frontend/MyVendor/default/MyVendor_PageBuilder/web/css/source/module/_layout.less
  • app/design/adminhtml/MyVendor/backend/MyVendor_PageBuilder/web/css/source/content-type/_layout-bo.less
  • app/design/frontend/MyVendor/default/Magento_Theme/web/css/source/widgets/_prod-popover.less
  • app/design/adminhtml/MyVendor/backend/MyVendor_PageBuilder/web/css/source/content-type/_prod-popover-bo.less

Les fichiers importants en cas de modification:

  • app/design/frontend/MyVendor/default/MyVendor_PageBuilder/web/js/content-type/module_41.js
  • app/design/frontend/MyVendor/default/MyVendor_PageBuilder/web/js/modules/ow-mod_41.js
  • app/code/MyVendor/PageBuilder/view/adminhtml/pagebuilder/content_type/module_41.xml
  • app/code/MyVendor/PageBuilder/view/adminhtml/ui_component/pagebuilder_module_41_form.xml
  • app/code/MyVendor/PageBuilder/view/adminhtml/web/template/content-type/module-41/theme-01/master.html
  • app/code/MyVendor/PageBuilder/view/adminhtml/web/template/content-type/module-41/theme-01/preview.html

Relatif au module, pas modifié:

  • app/design/frontend/MyVendor/default/MyVendor_PageBuilder/web/css/source/module/modules/_ow-mod_41.less

app/design/frontend/MyVendor/default/MyVendor_PageBuilder/web/js/content-type/module_41.js

ATTENTION: lorsque le module comporte plusieurs thèmes, il peut arriver qu’il existe des déclinaisons du fichier module_xx.js, comme par exemple module_xx–01.js, module_xx–02.js, etc… (où 01, 02, sont les numéros des thèmes). Il faut les éditer également comme indiqué ci-dessous pour initialiser notre nouveau module.

app/code/MyVendor/PageBuilder/view/adminhtml/pagebuilder/content_type/module_41.xml

ATTENTION: veiller à bien remplacer toutes les occurences de MyVendor dans MyVendor_PageBuilder/... dans tous les fichiers XML de cet exemple (module_41.xml et pagebuilder_module_41_form.xml) par le vrai nom de votre module (vous devriez le retrouver déjà utilisé dans le fichier XML en question). Sinon, la console vous remontera une erreur de compilation sur un fichier widget-directive.js:


Refused to execute script from 'http://245.mydomain.com.local/static/version1681995189/adminhtml/SomeVendor/Backend/en_US/MyVendor_PageBuilder/js/mass-converter/product/widget-directive.js' because its MIME type ('text/plain') is not executable, and strict MIME type checking is enabled.
require.js:168 

Uncaught Error: Script error for "MyVendor_PageBuilder/js/mass-converter/product/widget-directive"
https://requirejs.org/docs/errors.html#scripterror
    at makeError (require.js:168:17)
    at HTMLScriptElement.onScriptError (require.js:1738:36)

ATTENTION #2: lorsque le module fonctionne avec une base qu’on remplit ensuite avec des (sous-)items, les ajouts des bouts de code ci-dessous se font plutôt dans un fichier module_xx_item.xml que dans un fichier module_xx.xml.

app/code/MyVendor/PageBuilder/view/adminhtml/ui_component/pagebuilder_module_41_form.xml

ATTENTION: lorsque le module fonctionne avec une base qu’on remplit ensuite avec des (sous-)items, les ajouts des bouts de code ci-dessous se font plutôt dans un fichier pagebuilder_module_xx_item_form.xml que dans un fichier pagebuilder_module_xx_form.xml.

Cacher un fieldset complet pour un thème donné

ATTENTION: à la valeur de l’attribut name dans <item name="1" xsi:type="string">theme_03</item>! Il est possible qu’elle ait déjà été défini pour theme_03 plus haut dans le fichier et qu’elle soit différente de 1. Il faut que, pour un thème donné, la valeur de l’attribut name soit identique à chaque déclaration.

Exemple complet:

app/code/MyVendor/PageBuilder/view/adminhtml/web/template/content-type/module-41/theme-01/master.html

ATTENTION: un mauvais positionnement dans le template HTML peut bloquer l’enregistrement de la page en BO!

Remarque à propos de la vue pour le front

Bien que les DIVs soient déclarées dans l’ordre suivant au sein de ow-mod__img dans le template HTML:

  1. ow-prod-popover
  2. pm-responsive-image-wrapper

…il arrive que le DOM soit rendu dans l’ordre inverse en front:

app/code/MyVendor/PageBuilder/view/adminhtml/web/template/content-type/module-41/theme-01/preview.html

Remarques pour les vues front (master.html) ET admin (preview.html)

Si présence d’un CAPTION sur les images

Certains modules permettent d’afficher un bouton caption (révélant un court texte) en bas à gauche ou à droite de l’image. Lorsque cette fonctionnalité est présente, il faut essayer de placer notre code HTML relatif au popover immédiatement APRèS celui qui affiche le bouton caption. De cette manière, le bouton du popover s’affichera automatiquement en bas à gauche si le caption est paramètré pour s’afficher en bas à droite (via le code CSS ci-dessous, déjà présent dans les fichiers source app/design/frontend/MyVendor/default/Magento_Theme/web/css/source/widgets/_prod-popover.less et app/design/adminhtml/MyVendor/backend/MyVendor_PageBuilder/web/css/source/content-type/_prod-popover-bo.less).

Dans un même thème, plusieurs déclinaisons possibles du même emplacement en image, vidéo ou image/vidéo (3 déclinaisons)

Le bout de code:

…doit se répéter autant de fois qu’il y a de déclinaisons.

Exemple complet ci-dessous uniquement pour la partie front, mais la preview.html admin devra logiquement comporter autant de répétitions que le master.html front:

[Magento 2] Utiliser les arguments dans les layouts XML pour afficher/masquer sous condition certaines zones d’un template PHTML

Objectif

  • Utiliser les arguments pour afficher ou masquer une partie de template PHTML dans le DOM qui va être généré (il peut arriver qu’on cherche à afficher certaines parties d’un PHTML et à en masquer d’autres sous condition)
  • Utiliser $block->hasData() et $block->getData() dans un template PHTML pour interroger les arguments placés dans le layout XML
  • Utiliser la fonctionnalité de Custom Layout Update pour surcharger un layout XML de page

Ressources en ligne:

Layout XML

ATTENTION: j’ai écrit ça un peu vite; se référer plutôt à la doc officielle (lien ci-dessus).

On crée un Custom Layout Update. Le nom du fichier est important:

  • cms_page_view_selectable_socialmedia_NoSocialFooter.xml: la surcharge sera à appliquer à la main, via le BO, pour n’importe quelle page CMS.
  • cms_page_view_id_socialmedia.xml: la surcharge sera appliquée automatiquement à la page CMS dont l’URL Key est « socialmedia ».

Fichier: app/design/frontend/Vendor/default/Magento_Cms/layout/cms_page_view_selectable_socialmedia_NoSocialFooter.xml

Template PHTML

Fichier: app/design/frontend/Vendor/default/Magento_Theme/templates/html/footer/main.phtml

  • [1] On interroge le $block pour savoir si l’argument display_social_footer est redéfini dans le layout XML. Mais par défaut, on set la variable $display_social_footer à true.
  • [2] Si l’argument display_social_footer est redéfini à false dans mon exemple dans le layout XML (pour cela, il faut que le Custom Layout Update « NoSocialFooter » soit sélectionné en BO dans la config de la page CMS « socialmedia »), le <p>Lorem ipsum dolor sit amet</p> n’apparaîtra pas dans le DOM généré. Sinon, il apparaîtra.

En BO:

cache flush de rigueur!

[Magento 2] Désactiver l’extension Amasty Google Invisible Captcha qui bloque le click de certains boutons

Il peut arriver que des boutons ne soient pas cliquables dans certaines pages si il y a un problème avec Google reCAPTCHA. Vous devriez voir un encart reCAPTCHA en bas à droite de votre écran, avec le message suivant:

L’hôte local ne figure pas dans la liste des domaines acceptés pour la clé de ce site.

Pour le supprimer, il faut désactiver Google Invisible Captcha depuis l’interface d’admin:

  • STORES > Settings > Configuration > AMASTY EXTENSIONS > Google Invisible Captcha
  • General Settings > Enable Invisible Captcha: « No »
  • Bouton Save Config

[Magento 2] Etendre un Block pour en hériter et lui ajouter de nouvelles fonctionnalités

Note: dans un but d’anonymisation, le véritable libellé du vendor a été remplacé par MyVendor.

Ressource en ligne: Créer un bloc sous magento 2.

Les fichiers obligatoires pour la création d’un nouveau module dans Magento 2 (ou Adobe Commerce)

app/code/MyVendor/KlarnaOnsitemessaging/registration.php

app/code/MyVendor/KlarnaOnsitemessaging/etc/module.xml

app/code/MyVendor/KlarnaOnsitemessaging/etc/di.xml

Etendre un Block dans Magento 2

Le Block (fichier) initial

vendor/klarna/module-onsitemessaging/Block/Product.php

Le Block étendu

app/code/MyVendor/KlarnaOnsitemessaging/Block/Product.php

Raison de l’extension: dans la fiche produit, nous avons besoin de cacher la bannière Klarna pour les produits > 10 000 euros ainsi que les produits not salable ou qui ont un stock = 0.

Nous allons pour ce faire créer une fonction publique getProduct() qui va nous permettre de récupérer tout un tas d’informations sur le produit affiché et notamment celles que nous allons exploiter dans la fonction publique conditionalShowOnProduct() qui teste que toutes les conditions sont remplies pour afficher la bannière.

app/code/MyVendor/KlarnaOnsitemessaging/etc/config.xml

Dans la class Product que nous venons d’étendre (cf. extension de /Block/Product.php), nous utilisons une config const CHECKOUT_PRICES_ABOVE = 'checkout/prices_above'; que nous définissons dans ce fichier (plutôt qu’en dur).

app/design/frontend/MyTheme/default/Klarna_Onsitemessaging/templates/html/placement/product.phtml

On interroge la fonction conditionalShowOnProduct() présente dans notre Block étendu pour afficher ou non la bannière Klarna.

[Magento 2] matchMedia mediaCheck boucler dans un tableau de media-queries avec la méthode jQuery each()

Ressource: How to loop through array in jQuery?. très bien expliqué avec des exemples spécifiques pour du javasqcript vanilla (ES5, ES6, …) et l’utilisation avec this.

Dans mon exemple, je cherche simplement à ré-initialiser un carousel Slick Slider dès qu’un nouveau breakpoint est rencontré. Il est important de comprendre que c’est le même code qui est exécuté à chaque fois.

Si vous souhaitez utiliser des paramètres différents en fonction de vos vues (Mobile, Tablet, Desktop) avec Slick Slider, il faut utiliser l’option responsive.

…revient à déclarer ceci:

Attention à ne pas oublier le .bind(this) si vous voulez accéder à l’objet ‘this’

[Magento 2] Récupérer la config JSON d’un produit configurable en front

Depuis la fiche produit d’un produit configurable, afficher la source (crtl + u) et effectuer une recherche sur Magento_Swatches/js/swatch-renderer": {.

ATTENTION: c’est la première occurence qui sera retournée qui est la bonne, à moins que vous ne cherchiez un product ID spécifique. A ce moment là, votre recherche peut se faire sur le modèle "[data-role=swatch-option-XXXX]"XXXX correspond au product ID.

Le JSON récupéré peut ensuite être affiché joliment.

[Magento 2] Récupérer la placeholder image de votre store dans un block PHP

Objectif:

Dans la fonction suivante, je souhaite rajouter en else la possibilité d’afficher la placeholder image rendue disponible par Magento 2 si l’utilisateur admin backoffice n’a pas uploadé d’image spécifique.

Solution:

Tout d’abord, juste après la déclaration class myClassName extends…, déclarer le Helper Image et sa variable $imageHelper:

NOTE: dans mon cas (un peu particulier), une protected function _construct(): void existe déjà dans le block. Il ne faut pas chercher à la modifier!

Au lieu de ça, dans notre code et juste en-dessous ce premier contructeur, on crée une nouvelle extension de public function __construct sur le modèle de la fonction _construct tout droit issue de vendor/magento/framework/View/Element/Template.php et on y ajoute le image helper:

NOTE: \Template\Context $context devient \Magento\Framework\View\Element\Template\Context $context pour l’occasion.

Puis compléter la fonction de départ avec un else qui retourne l’image placeholder:

Cette manip’ donnera le même résultat que si on mettait en place le code suivant la le fichier template PHTML chargé d’afficher les images ou, à défaut, le placeholder. On est cependant plus efficace en renvoyant directement l’image adéquate via le block PHP. Donc, le code ci-dessous est présent juste à titre informatif mais éviter de l’utiliser dans la mesure du possible (on voit qu’on utilise, comme argument de la condition, la même méthode empty() que dans le block PHP):

[Magento 2] i18n, bonnes pratiques

Insérer un ou plusieurs liens dans une même chaîne de caractères:

(Exemple pour dans un fichier PHTML)

Insérer un mail:

(Exemple pour dans un fichier PHTML)

Une locale contenant des balises HTML dans un fichier PHP:

Fichier PHP

Fichier PHTML

Ne pas utiliser $escaper->escapeHtml();:

Voici ce qu’il faut mettre:

Fichier CSV