.

Personnalisation du constructeur de mise en page. Créer une mise en page personnalisée dans Drupal

Layout Builder permet de créer rapidement des mises en page de site Web à partir de composants préconstruits ajoutés aux sections. Par défaut, Drupal propose quatre types de sections : une, deux, trois et quatre colonnes. Ces colonnes ont un comportement prédéfini sur lequel les éditeurs n'ont aucun contrôle. Drupal offre la possibilité de créer nos propres types de sections, afin que nous puissions les adapter à notre projet. C'est le processus que nous allons vous montrer dans notre article.

Comment créer une section personnalisée dans Layout Builder ?

La première étape, et la plus importante, est de définir les objectifs et donc la liste des fonctionnalités que la section doit fournir. Ensuite, il est utile de diviser les fonctionnalités en petites tâches réalisables dans un temps donné. Le but de notre section sera de permettre l'ajout de classes au conteneur principal de section et aux régions individuelles.

Comme base, nous utiliserons le modèle disponible dans le module Drupal Layout Builder, qui est utilisé dans les sections disponibles avec l'installation du module. Notre liste de tâches doit inclure :

  • la création d'un module personnalisé,
  • la définition d'une section dans le fichier layouts.yml,
  • la définition d'un modèle pour nos sections et le plugin dans lequel nous intégrerons la logique d'ajout de nos classes.

Créer un nouveau module dans Drupal

Nous devons créer un fichier standard *.info.yml, comme pour chaque module. Pour une description détaillée, veuillez consulter la documentation sur Drupal.org.

# nom du fichier : custom_layout_builder.info.yml

name: Sections de Custom Layout Builder
description: Fonctionnalité qui étend Layout Builder
core_version_requirement: ^9
type: module
package: custom
dependencies:
  - drupal:layout_builder

Nous savons quel est le but du module car nous avons défini la fonctionnalité requise. Par conséquent, à ce stade déjà, nous sommes certains que la liste des dépendances doit inclure au moins le module Layout Builder. Après avoir défini le fichier info.yml, il est utile de vider le cache et de vérifier si le module apparaît dans la liste. Pour cela, allons dans la vue des modules et cherchons le module par le titre ou nom machine. Nous devrions voir notre module avec une liste de dépendances requises.

Informations sur votre nouveau module Drupal contenant une liste de dépendances

 

Comme nous pouvons le voir clairement, même si nous avons fourni une dépendance au module Layout Builder uniquement, leur liste est un peu plus longue. C'est parce que le module Layout Builder a sa propre liste de dépendances et elle est héritée par notre module.

Une vue d'une liste de dépendances du module Drupal Layout Builder

 

À ce stade, il vaut la peine de considérer la santé mentale des autres développeurs (ou la vôtre, si vous revenez à ce code après quelques mois) et de commencer à construire sa documentation. Il vaut la peine de commencer par l'implémentation du hook hook_help().

Un exemple de l'implémentation du hook_help() dans le code du nouveau module Drupal

 

Il est également recommandé de créer un fichier README.md et de le mettre à jour régulièrement.

Enregistrement de la section avec *.layouts.yml

Afin d'enregistrer une nouvelle section, le moyen le plus simple est d'ajouter le fichier *.layouts.yml (où * est le nom machine de notre module). Le fichier doit être ajouté dans le dossier principal du module, où nous avons ajouté le fichier *.info.yml.

Commençons par définir une section :

# nom du fichier : custom_layout_builder.layouts.yml

layout_custom_one_column:                 # Clé principale de section
  label: '[CUSTOM] Une colonne'            # Titre de section
  category: 'Mises en page personnalisées'
  path: layouts/custom_onecol_section     # Chemin relatif à partir du modèle
  template: layout--custom-onecol-section # Nom du modèle
  default_region: first
  icon_map:
    - [first]
  regions:                                # Tableau des régions
    first:                                # Nom machine de la région
      label: Première                     # Titre de la région

Après la configuration, lors de l'ajout de sections, nous devrions être en mesure de voir notre section nouvellement définie.

Voici à quoi ressemble la section nouvellement définie dans le nouveau module Drupal

 

Définition du modèle de section

Pour pouvoir ajouter une section, nous devons encore ajouter le modèle dont nous avons spécifié le nom et le chemin. Dans notre cas, nous devons créer le dossier layouts/custom_onecol_section à l'intérieur duquel le fichier layout--custom-onecol-section.html.twig doit être placé.

Par défaut, le modèle aura accès à quatre variables : content, attributes, region_attributes et settings. Si nous ne mettons aucun bloc dans la section, la variable content retournera false après avoir été convertie en valeur booléenne. Nous pouvons exploiter ce comportement pour éviter d'afficher des balises HTML vides. À l'intérieur de la variable content, nous trouverons les clés correspondant à chaque région définie, et à l'intérieur de ces régions se trouvent les blocs que nous avons ajoutés. Dans la variable content, nous ne trouverons que la clé first, car c'est la seule que nous avons définie. Le comportement de content.first lors de la conversion en valeur booléenne est analogue au comportement de la variable content. Nous utiliserons cela pour ne pas afficher de balises vides.

# nom du fichier :  layout--custom-onecol-section.html.twig

{#
/**
 * @file
 * Implémentation par défaut pour une section de mise en page une colonne personnalisée.
 *
 * Variables disponibles :
 * - content : Le contenu pour cette mise en page.
 * - attributes : Attributs HTML pour la disposition <div>.
 * - region_attributes : Attributs HTML pour la région <div>.
 * - settings : Un tableau de paramètres configurés pour la disposition.
 *
 * @ingroup themeable
 */
#}
{% if content %}
  <div{{ attributes }}>

    {% if content.first %}
      <div {{ region_attributes.first }}>
        {{ content.first }}
      </div>
    {% endif %}

  </div>
{% endif %}

Après avoir ajouté le modèle, nous devrions être en mesure d'ajouter facilement notre section :

Définition du plugin Layout

Du point de vue de l'utilisateur final, nous n'avons rien fait jusqu'à présent, car l'éditeur de contenu ne verra que le nouveau titre de la section avec le grand préfixe [CUSTOM]. Cela est dû au fait que la section que nous avons ajoutée fonctionne de manière identique à la section par défaut, fournie avec le module Layout Builder (avec une petite exception : notre implémentation n'ajoute pas de classes). Pour changer son comportement, nous devons implémenter un nouveau plugin de mise en page.

Cadre de la classe de base

La classe doit se trouver dans le dossier src/Plugin/Layout. Elle sera suffisamment générique pour pouvoir être utilisée pour tout nombre de régions. La classe Drupal\Core\Layout\LayoutDefault contient de nombreuses méthodes de base et implémente les interfaces nécessaires. Pour ne pas réinventer la roue, nous pouvons l'étendre dans notre classe.

# nom du fichier : CustomLayoutClassBase.php

<?php

namespace Drupal\custom_layout_builder\Plugin\Layout;

use Drupal\Core\Layout\LayoutDefault;

/**
 * Classe de base de nos mises en page personnalisées avec des classes HTML configurables.
 *
 * @internal
 *   Les classes de plugins sont internes.
 */
class CustomLayoutClassBase extends LayoutDefault {

}

Ajout d'options de configuration à une classe de base

L'une des exigences est la possibilité de sélectionner une classe pour la balise enveloppant les régions de la section. Pour y parvenir, nous devons d'abord remplacer la méthode defaultConfiguration et y ajouter une nouvelle option de configuration.

/**
 * {@inheritdoc}
 */
public function defaultConfiguration() {
  $configuration = parent::defaultConfiguration();

  return $configuration + [
    'wrapper_classes' => '',
  ];
}

Ensuite, nous devrions ajouter la possibilité de spécifier une valeur pour cette option de configuration. Nous pouvons le faire en remplaçant les méthodes buildConfigurationForm et submitConfigurationForm.

/**
 * {@inheritdoc}
 */
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
  $form['wrapper_classes'] = [
    '#type' => 'textfield',
    '#title' => $this->t('Classes supplémentaires de l'enveloppe'),
    '#default_value' => $this->configuration['wrapper_classes'],
    '#description' => $this->t('Classes supplémentaires pour l'enveloppe. Tapez autant que vous le souhaitez, mais n'oubliez pas de les séparer par un espace.´),
  ];

  return parent::buildConfigurationForm($form, $form_state);
}

/**
 * {@inheritdoc}
 */
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
  parent::submitConfigurationForm($form, $form_state);

  $this->configuration['wrapper_classes'] = $form_state->getValue('wrapper_classes');
}

S'il y a besoin d'ajouter une validation de formulaire, cela peut être fait en remplaçant la méthode validateConfigurationForm. Nous recommandons d'implémenter une validation pour ce champ, car les classes doivent être conformes à la norme de la communauté Drupal. La méthode Html::getClass() pourrait être utile dans ce cas.

Utilisation de la configuration pour construire des sections

Le tableau de rendu est créé dans la méthode build, c'est ce que nous allons maintenant remplacer. Si vous vous souvenez du contenu du modèle que nous avons ajouté, vous savez probablement déjà que nous ajoutons des classes à l'objet attributes.

/**
 * {@inheritdoc}
 */
public function build(array $regions): array {
  $build = parent::build($regions);
  $wrapper_classes = explode(' ', (string) $this->configuration['wrapper_classes']);
  $build['#attributes']['class'] = [...$wrapper_classes];

  return $build;
}

Utilisation de la base pour créer un plugin Layout

Notre classe est prête, il est temps de l'utiliser dans une section. Pour ce faire, revenons au fichier *.layouts.yml pour déclarer un nouveau plugin. Cela se fait en spécifiant l'intégralité du namespace de la classe sous la clé class.

# nom du fichier : custom_layout_builder.layouts.yml

layout_custom_one_column:
  label: '[CUSTOM] Une colonne'
  category: 'Mises en page personnalisées'
  path: layouts/custom_onecol_section
  template: layout--custom-onecol-section
  class: '\Drupal\custom_layout_builder\Plugin\Layout\CustomOneColLayout'
  default_region: first
  icon_map:
    - [first]
  regions:
    first:
      label: Première

Après avoir apporté les modifications ci-dessus, vous pouvez remarquer que le formulaire de la section dispose d'un nouveau champ et que les classes saisies dans ce champ sont au bon endroit dans le HTML.

Voici une vue du nouveau champ ajouté dans le formulaire dans Drupal

L'extrait de code HTML contenant les classes, visible lors de la création d'un plugin de mise en page

Ajout de l'option pour sélectionner des classes pour les régions

Nous pouvons déjà définir une liste de classes pour l'élément enveloppeur dans notre section. Il est temps de réfléchir à la manière de créer la logique responsable de l'ajout de classes aux sections individuelles de notre mise en page. Nous devons prendre en considération l'extensibilité de notre classe de base. C'est pourquoi nous recommandons de baser la logique de détermination et d'accès aux régions sur la méthode getRegionNames() de la classe LayoutDefinition.

1. Tout d'abord, nous ajoutons un champ à notre formulaire pour chaque région :

/**
 * {@inheritdoc}
 */
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
  $form['wrapper_classes'] = [
    '#type' => 'textfield',
    '#title' => $this->t('Classes supplémentaires de l'enveloppe'),
    '#default_value' => $this->configuration['wrapper_classes'],
    '#description' => $this->t('Classes supplémentaires pour l'enveloppe. Tapez autant que vous le souhaitez, mais n'oubliez pas de les séparer par un espace.´),
  ];

  foreach ($this->getPluginDefinition()->getRegionNames() as $region_name) {
    $form['region_classes'][$region_name] = [
      '#type' => 'textfield',
      '#title' => $this->t('Classes supplémentaires pour la région @region_name', [
        '@region_name' => $region_name,
      ]),
      '#default_value' => $this->configuration['region_classes'][$region_name],
      '#description' => $this->t('Classes supplémentaires pour l'enveloppe de la région @region_name. Tapez autant que vous le souhaitez, mais n'oubliez pas de les séparer par un espace.´, [
        '@region_name' => $region_name,
      ]),
    ];
  }

  return parent::buildConfigurationForm($form, $form_state);
}

2. Nous utilisons une boucle similaire pour écrire la valeur :

/**
 * {@inheritdoc}
 */
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
  parent::submitConfigurationForm($form, $form_state);

  $this->configuration['wrapper_classes'] = $form_state->getValue('wrapper_classes');

  foreach ($this->getPluginDefinition()->getRegionNames() as $region_name) {
    $this->configuration['region_classes'][$region_name] = $form_state->getValue(['region_classes', 
$region_name], '');
      }
}

3. La dernière étape consistera à remplacer la méthode build et à intégrer nos classes dans l'objet de la classe Attributes approprié.

/**
 * {@inheritdoc}
 */
public function build(array $regions): array {
  $build = parent::build($regions);
  $wrapper_classes = explode(' ', (string) $this->configuration['wrapper_classes']);
  $build['#attributes']['class'] = [...$wrapper_classes];

  foreach (array_keys($regions) as $region_name) {
    $region_classes = explode(' ', (string) $this->configuration['region_classes'][$region_name]);
    $build[$region_name]['#attributes']['class'] = [...$region_classes];
  }

  return $build;
}

Après nos dernières modifications, nous devrions voir un nouveau champ Classes supplémentaires pour la première région où nous pouvons fournir une liste de classes que nous voulons utiliser.

Un formulaire de modification de section dans Drupal où nous pouvons ajouter des classes et notre propre étiquette administrative

Gardez à l'esprit que la région ne s'affichera que si elle n'est pas vide. C'est pourquoi nous avons ajouté un bloc test contenant le titre du nœud. Voyons si les classes sont visibles dans le HTML.

Extrait de code HTML contenant des balises de section et des attributs pour le plugin de mise en page

Création de différentes variantes de sections

Le code a été écrit de manière si générique que l'ajout d'une section avec un nombre différent de régions nécessite uniquement de définir la région et le modèle de notre part. Ajoutons alors une nouvelle section contenant deux régions.

Tout d'abord, nous ajoutons la définition :

# nom du fichier : custom_layout_builder.layouts.yml

layout_custom_one_column:
  label: '[CUSTOM] Une colonne'
  category: 'Mises en page personnalisées'
  path: layouts/custom_onecol_section
  template: layout--custom-onecol-section
  class: '\Drupal\custom_layout_builder\Plugin\Layout\CustomLayoutClassBase'
  default_region: first
  icon_map:
    - [first]
  regions:
    first:
      label: Première
layout_custom_two_columns:
  label: '[CUSTOM] Deux colonnes'
  category: 'Mises en page personnalisées'
  path: layouts/custom_twocol_section
  template: layout--custom-twocol-section
  class: '\Drupal\custom_layout_builder\Plugin\Layout\CustomLayoutClassBase'
  default_region: first
  icon_map:
    - [first, second]
  regions:
    first:
      label: Première
    second:
      label: Deuxième

Ensuite, nous préparons le modèle :

# nom du fichier : layout--custom-twocol-section.html.twig

{#
/**
 * @file
 * Implémentation par défaut pour une section de mise en page personnalisée à une colonne.
 *
 * Variables disponibles :
 * - content : Le contenu pour cette mise en page.
 * - attributes : Attributs HTML pour la disposition <div>.
 * - region_attributes : Attributs HTML pour la région <div>.
 * - settings : Un tableau de paramètres configurés pour la disposition.
 *
 * @ingroup themeable
 */
#}
{% if content %}
  <div{{ attributes }}>

    {% if content.first %}
      <div {{ region_attributes.first }}>
        {{ content.first }}
      </div>
    {% endif %}

    {% if content.second %}
      <div {{ region_attributes.second }}>
        {{ content.second }}
      </div>
    {% endif %}

  </div>
{% endif %}

La section devrait être disponible maintenant.

Une liste de sections disponibles pour une mise en page personnalisée dans le Layout Builder

 

Le formulaire de configuration devrait s'ajuster automatiquement au nombre de régions.

Vue du formulaire de configuration de la section avec les champs pour les classes et l'étiquette administrative

Après avoir configuré le formulaire et ajouté des données de test, nous pouvons voir le résultat de notre opération dans le HTML.

L'extrait de code HTML contenant les résultats des opérations liées à la section

 

Le module créé dans ce tutoriel est disponible sur notre compte GitHub.

Personnalisation de Layout Builder - résumé

Layout Builder est un excellent outil dont l'API permet une liberté totale. Comme toujours avec Drupal, si vous pouvez le rêver, vous pouvez le construire. L'exemple montré dans cet article n'est qu'une petite partie de ce qui peut être réalisé. Si vous êtes intéressé par une utilisation plus large de l'API Layout Builder, il vaut la peine de lire à propos du module Bootstrap Layout Builder.

Avez-vous besoin de paramètres personnalisés dans votre système ? Découvrez comment nous pouvons vous aider dans le cadre de nos services liés au développement Drupal.

As part of Drupal support, we maintain existing websites and expand them with new functionalities