Comment avoir plusieurs uploader d'images pour un même composant PageBuilder ?

Comment avoir plusieurs uploader d'images pour un même composant PageBuilder ?

Nous allons voir au cours de cet article comment avoir plusieurs uploader d'images pour un même composant PageBuilder.

Dans un souci de clarté, nous partirons du postulat que vous savez déjà créer un composant.
Si ce n'est pas le cas, je vous invite à consulter cet article (ajouter un lien vers l'article sur la création de composants) au préalable.

Imaginons que vous ayez développé un composant constitué d'un conteneur qui contient trois sous-conteneurs.
Chacun de ces sous-conteneurs est composé d'une image, ainsi que d'un titre et d'un bouton superposé sur l'image.

Vous souhaitez maintenant disposer d'un uploader pour chacun de vos sous-conteneurs afin que chacun d'eux ait sa propre image.

Création de votre élément image dans le conteneur.

Dans un premier temps, il va falloir définir votre élément image. Pour cela, rendons-nous dans le fichier de configuration.

Dans ce fichier de configuration, nous allons déclarer le nouvel élément (pour plus de clarté, je vais me concentrer sur le conteneur gauche, mais les deux autres sont identiques sauf les noms :

app/code/VotreProjet/VotreModule/view/adminhtml/pagebuilder/content_type/nom_du_composant.xml
            <element name="leftimage">
                        <style name="border" source="border_style" converter="Magento_PageBuilder/js/converter/style/border-style"/>
                        <style name="border_color" source="border_color"/>
                        <style name="border_width" source="border_width" converter="Magento_PageBuilder/js/converter/style/border-width"/>
                        <style name="border_radius" source="border_radius" converter="Magento_PageBuilder/js/converter/style/remove-px"/>
                        <static_style source="max-width" value="100%"/>
                        <static_style source="height" value="auto"/>
                        <attribute name="leftimage" source="src" converter="Magento_PageBuilder/js/converter/attribute/src" preview_converter="Magento_PageBuilder/js/converter/attribute/preview/src"/>
                        <attribute name="leftalt" source="alt" converter="Magento_PageBuilder/js/converter/html/tag-escaper"/>
                        <attribute name="lefttitle_attribute" source="title"/>
                    </element>

Voyons ensemble comment est composé l'élément :

Le nom unique de notre composant :
<element name="">
Styles injectés directement sur l'image. Ils prennent le nom du style ainsi que sa valeur :
<static_style>
app/code/VotreProjet/VotreModule/view/adminhtml/pagebuilder/content_type/nom_du_composant.xml
                    <element name="leftimage">
                        <style name="border" source="border_style" converter="Magento_PageBuilder/js/converter/style/border-style"/>
                        <style name="border_color" source="border_color"/>
                        <style name="border_width" source="border_width" converter="Magento_PageBuilder/js/converter/style/border-width"/>
                        <style name="border_radius" source="border_radius" converter="Magento_PageBuilder/js/converter/style/remove-px"/>
                        <static_style source="max-width" value="100%"/>
                        <static_style source="height" value="auto"/>
                        <attribute name="leftimage" source="src" converter="Magento_PageBuilder/js/converter/attribute/src" preview_converter="Magento_PageBuilder/js/converter/attribute/preview/src"/>
                        <attribute name="leftalt" source="alt" converter="Magento_PageBuilder/js/converter/html/tag-escaper"/>
                        <attribute name="lefttitle_attribute" source="title"/>
                    </element>

Une fois l'élément déclaré, nous devons lier nos attributs à notre formulaire.

Nous allons donc maintenant lier les attributs dans notre formulaire comme ceci :

app/code/VotreProjet/VotreModule/view/adminhtml/ui_component/pagebuilder_votre_composant_form.xml
<field name="lefttitle_attribute" sortOrder="100" formElement="input">
    <argument name="data" xsi:type="array">
        <item name="config" xsi:type="array">
            <item name="source" xsi:type="string">page</item>
        </item>
    </argument>
    <settings>
        <dataScope>lefttitle_attribute</dataScope>
        <dataType>text</dataType>
        <label translate="true">Image title</label>
    </settings>
</field>
<field name="leftalt" sortOrder="110" formElement="input">
    <argument name="data" xsi:type="array">
        <item name="config" xsi:type="array">
            <item name="source" xsi:type="string">page</item>
        </item>
    </argument>
    <settings>
        <dataScope>leftalt</dataScope>
        <dataType>text</dataType>
        <label translate="true">Image alt</label>
    </settings>
</field>

Nous avons donc lié notre attribut title et alt à notre formulaire.

Ajout des Uploader pour notre composant

Configuration des Uploaders

Dans un premier temps, nous allons déclarer la configuration de notre uploader dans le fichier de configuration du composant.

Voici la configuration de l'uploader à rajouter :

app/code/VotreProjet/VotreModule/view/adminhtml/pagebuilder/content_type/nom_du_composant.xml
<additional_data>
  <item name="leftUploaderConfig" xsi:type="array">
      <item name="maxFileSize" xsi:type="object">ImageMaxFileSizeDesktop</item>
      <item name="allowedExtensions" xsi:type="string">jpg jpeg gif png</item>
      <item name="component" xsi:type="string">Magento_PageBuilder/js/form/element/image-uploader</item>
      <item name="componentType" xsi:type="string">imageUploader</item>
      <item name="dataScope" xsi:type="string">leftimage</item>
      <item name="formElement" xsi:type="string">imageUploader</item>
      <item name="uploaderConfig" xsi:type="array">
          <item name="url" xsi:type="object">Magento\PageBuilder\Model\Config\ContentType\AdditionalData\Provider\Uploader\SaveUrl</item>
      </item>
      <item name="previewTmpl" xsi:type="string">Magento_PageBuilder/form/element/uploader/preview</item>
      <item name="template" xsi:type="string">Magento_PageBuilder/form/element/uploader/preview/image</item>
      <item name="mediaGallery" xsi:type="array">
          <item name="openDialogUrl" xsi:type="object">Magento\PageBuilder\Model\Config\ContentType\AdditionalData\Provider\Uploader\OpenDialogUrl</item>
          <item name="openDialogTitle" xsi:type="string" translate="true">Insert Images...</item>
          <item name="initialOpenSubpath" xsi:type="string">wysiwyg</item>
          <item name="storeId" xsi:type="object">Magento\PageBuilder\Model\Config\ContentType\AdditionalData\Provider\StoreId</item>
      </item>
      <item name="validation" xsi:type="array">
          <item name="required-entry" xsi:type="boolean">true</item>
      </item>
  </item>            
</additional_data>

Ici, dans un souci de clarté, je ne détaillerai pas la totalité des options de configuration de l'uploader. Ce qu'il faut bien retenir dans le cas d'un uploader multiple, c'est l'item global (ligne 2 : qui s'appelle "leftUploaderConfig") et l'item qui se nomme dataScope (ligne 7).

En effet, si vous souhaitez avoir un uploader pour votre bloc gauche, bloc du centre et le bloc droit, par exemple, il faudra dupliquer la configuration ci-dessus afin que chaque uploader ait un nom distinct et qu'il cible bien un attribut src à l'aide du dataScope de chacun de vos éléments.

Ajout de l'uploader dans le form

Nous devons maintenant lier notre attribut leftimage au formulaire afin de pouvoir bénéficier de l'uploader graphique dans notre back-office Magento. Pour ce faire, retournons dans le fichier et plaçons ce morceau de code dans notre formulaire avant la déclaration de nos précédents attributs :

app/code/VotreProjet/VotreModule/view/adminhtml/ui_component/pagebuilder_votre_composant_form.xml
<field name="leftimage" sortOrder="90" formElement="imageUploader">
    <settings>
        <label translate="true">Image</label>
        <componentType>imageUploader</componentType>
    </settings>
    <formElements>
        <imageUploader>
            <settings>
                <allowedExtensions>jpg jpeg gif png</allowedExtensions>
                <maxFileSize>4194304</maxFileSize>
                <uploaderConfig>
                    <param xsi:type="string" name="url">pagebuilder/contenttype/image_upload</param>
                </uploaderConfig>
                <previewTmpl>Magento_PageBuilder/form/element/uploader/preview</previewTmpl>
            </settings>
        </imageUploader>
    </formElements>
</field>

Vous devriez maintenant disposer de ce rendu dans votre back-office Magento.

En revanche, ce dernier n'est pas encore fonctionnel. C'est pourquoi nous allons maintenant dans notre fichier preview.js.

C'est donc dans ce fichier que nous allons gérer chacun de nos téléchargeurs comme ceci :

app/code/VotreProjet/VotreModule/view/adminhtml/web/js/content-type/votre-nom-de-composant/preview.js
 Preview.prototype.getLeftUploader = function() \{
        var initialImageValue = this.contentType.dataStore
            .get(this.config.additional_data.leftUploaderConfig.dataScope, "");

        return new Uploader(
            "imageleftuploader_" + this.contentType.id,
            this.config.additional_data.leftUploaderConfig,
            this.contentType.id + 'left',
            this.contentType.dataStore,
            initialImageValue,
        );
    };

Dans notre fichier preview.js, nous déclarons donc une fonction qui porte le nom que nous avons déclaré à la ligne 2 de notre fichier de configuration auquel on rajoute en préfixe "get" (attention, votre fonction doit être écrite en CamelCase). Cette fonction a pour vocation d'être appelée par votre template preview.html que nous verrons ensuite.

Cette fonction va donc nous permettre de récupérer les données passées dans notre fichier de configuration. Et elle va retourner une nouvelle instance de l'uploader.

  • Le premier paramètre sera son nom afin de permettre à Magento de le retrouver dans le registre UI.
  • Ensuite, nous lui donnons la configuration définie dans le fichier de configuration de notre composant.
  • Le troisième paramètre est le contentType Id.

Attention : dans le cadre d'un uploader multiple, toutes nos instances auront le même contentType id, ce qui va générer des conflits et vous créer des bugs du type "l'upload d'une image se fera aussi sur vos autres éléments". Pour remédier à cela, l'astuce est de lui concaténer une valeur différente pour chaque uploader.

  • Le quatrième élément est le dataStore dans lequel l'image uploadée doit être stockée.
  • Et pour terminer, la valeur de l'image à définir pour l'état initial du composant uploader.

Ces cinq paramètres sont obligatoires dans la création d'une nouvelle instance, il existe deux autres paramètres que nous ne détaillerons pas ici mais que vous pouvez retrouver sur la documentation officielle de Magento ICI

Ensuite, il vous reste à reproduire cette logique pour vos autres uploader, en créant par exemple un uploader pour votre conteneur droit :

   Preview.prototype.getRightUploader = function() \{
        var initialImageValue = this.contentType.dataStore
            .get(this.config.additional_data.rightUploaderConfig.dataScope, "");

        return new Uploader(
            "imagerightuploader_" + this.contentType.id,
            this.config.additional_data.rightUploaderConfig,
            this.contentType.id + 'right',
            this.contentType.dataStore,
            initialImageValue,
        );
    };

Les multiples uploader dans le html

Voyons maintenant comment mettre en place nos téléchargeurs dans notre template preview.html.

C'est donc ce fichier qui va s'occuper du rendu dans votre back-office, et c'est ici que nous allons rajouter nos différents téléchargeurs comme ceci :

app/code/VotreProjet/VotreModule/view/adminhtml/web/template/content-type/votre-nom-de-composant/default/preview.html
<div
    ko-style="data.main.style"
    class="pagebuilder-content-type pagebuilder-content-type-affordance type-container"
    attr="data.main.attributes"
    event="\{ mouseover: onMouseOver, mouseout: onMouseOut }, mouseoverBubble: false"
    css="data.main.css"
>
    <render args="getOptions().template"></render>
    <div class="pagebuilder-photo-block-container">
        <div class="pagebuilder-photo-block-container-left">
            <figure attr="data.main.attributes" ko-style="Object.assign(\{}, data.main.style(), !data.leftimage.attributes().src ? \{} : \{borderStyle: 'none'})" class="photo-block-image">
                <scope args="getLeftUploader().getUiComponent()">
                    <render></render>
                </scope>
                <img if="data.leftimage.attributes().src"
                     attr="data.leftimage.attributes"
                     css="data.leftimage.css"
                     ko-style="data.leftimage.style">
            </figure>
        </div>
        <div class="pagebuilder-photo-block-container-center">
            <figure attr="data.main.attributes" ko-style="Object.assign(\{}, data.main.style(), !data.centerimage.attributes().src ? \{} : \{borderStyle: 'none'})" class="photo-block-image">
                <scope args="getCenterUploader().getUiComponent()">
                    <render></render>
                </scope>
                <img if="data.centerimage.attributes().src"
                     attr="data.centerimage.attributes"
                     css="data.centerimage.css"
                     ko-style="data.centerimage.style">
            </figure>
        </div>
        <div class="pagebuilder-photo-block-container-right">
            <figure attr="data.main.attributes" ko-style="Object.assign(\{}, data.main.style(), !data.rightimage.attributes().src ? \{} : \{borderStyle: 'none'})" class="photo-block-image">
                <scope args="getRightUploader().getUiComponent()">
                    <render></render>
                </scope>
                <img if="data.rightimage.attributes().src"
                     attr="data.rightimage.attributes"
                     css="data.rightimage.css"
                     ko-style="data.rightimage.style">
            </figure>
        </div>
    </div>
</div>

Comme nous pouvons le voir ci-dessus, je dispose d'un conteneur. Il contient aussi un conteneur qui vient abriter mes trois conteneurs servant de colonne et dans lesquels nous retrouvons nos uploader.


Détail du conteneur ayant la classe CSS 'pagebuilder-photo-block-container-left' :


Ce conteneur est composé d'une balise <figure>, elle-même composée d'une balise <scope> et d'une balise <img>. Ce qui est le plus important ici, c'est la balise <scope> à qui nous allons passer en argument l'appel de notre fonction afin de retourner une instance de notre uploader. Une fois ceci fait, la balise <img> vient vérifier si l'attribut src est rempli. Si tel est le cas, alors comme pour d'autres éléments, nos attributs et notre CSS seront remplis en fonction des données entrées dans notre formulaire et notre configuration.

Vous devriez donc maintenant avoir vos différents téléchargeurs fonctionnels vous permettant soit de glisser/déposer vos images dans votre composant PageBuilder, ou alors de les télécharger depuis le formulaire de configuration.

En conclusion, cet article nous a guidés à travers la mise en place de plusieurs uploader d'images pour un même composant PageBuilder dans Magento. Nous avons détaillé chaque étape, de la création de l'élément image dans le conteneur à l'ajout des uploader et leur configuration.


Nous avons souligné l'importance de la clarté dans le fichier de configuration, en insistant sur des points cruciaux tels que l'attribution de noms uniques et la gestion des dataScopes pour chaque uploader. De plus, la liaison des attributs au formulaire et la mise en place des uploader dans le code JavaScript ont été détaillées de manière approfondie.

En suivant ces étapes, vous devriez désormais être en mesure d'intégrer avec succès plusieurs uploader d'images dans votre composant PageBuilder, offrant ainsi une expérience plus riche et personnalisée.

En résumé, cette approche fournit une solution pratique pour répondre aux besoins spécifiques des développeurs Magento, en étendant les fonctionnalités de PageBuilder de manière intuitive et efficace.