logo

Types de blocs supplémentaires (EBT) – Nouvelle expérience de Layout Builder❗

Types de blocs supplémentaires (EBT) – types de blocs stylisés et personnalisables : diaporamas, onglets, cartes, accordéons et bien d’autres. Paramètres intégrés pour l’arrière-plan, la boîte DOM, les plugins JavaScript. Découvrez dès aujourd’hui le futur de la création de mises en page.

Démo des modules EBT Télécharger les modules EBT

❗Types de paragraphes supplémentaires (EPT) – Nouvelle expérience Paragraphes

Types de paragraphes supplémentaires (EPT) – ensemble de modules basé sur les paragraphes analogiques.

Démo des modules EPT Télécharger les modules EPT

Défilement
05/07/2025, by Ivan

Menu

Ce tutoriel a été initialement publié sur Web Wash. Cependant, Berdir a demandé si je pouvais le publier ici, donc le voilà.

Un module Drupal 7 permettait de stocker des exemples de code/extraits dans un champ. Il fournissait un champ personnalisé appelé « Champ d’extraits » affichant trois éléments de formulaire : description, code source, et mode de coloration syntaxique (langage de programmation).

Mais il est temps de mettre à jour ce module pour Drupal 8.

Dans ce tutoriel, je vais vous montrer comment j’ai créé un champ personnalisé « basique » dans Drupal 8. Je ne rentrerai pas dans les détails de PSR-4, annotations ou plugins, ce serait trop long.

À la place, je fournirai des liens vers d’autres ressources expliquant ces concepts plus en détail.

Cependant, si vous cherchez une documentation approfondie sur l’API Field dans Drupal 8, consultez ces séries :

Dans Drupal 8, les champs ne sont plus implémentés avec des hooks comme dans Drupal 7. Ils sont créés via la nouvelle API de plugins Drupal 8. Cela signifie qu’au lieu de hooks, on définit une classe pour le widget, le formatteur et le type de champ. La plupart des hooks Drupal 7 (hook_field_schema, hook_field_is_empty, etc.) deviennent des méthodes dans ces classes.

Étape 1 : implémenter l’élément de champ

La première étape consiste à définir une classe d’élément de champ nommée SnippetsItem, qui étend FieldItemBase.

1. En Drupal 8, les classes sont chargées via PSR-4.

Il faut donc créer un fichier SnippetsItem.php dans « module »/src/Plugin/Field/FieldType/SnippetsItem.php

/**
 * @file
 * Contient \Drupal\snippets\Plugin\Field\FieldType\SnippetsItem.
 */

namespace Drupal\snippets\Plugin\Field\FieldType;

use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\TypedData\DataDefinition;

Ensuite, dans le fichier, on ajoute l’espace de noms Drupal\snippets\Plugin\Field\FieldType et trois use :

  • Drupal\Core\Field\FieldItemBase,
  • Drupal\Core\Field\FieldStorageDefinitionInterface
  • Drupal\Core\TypedData\DataDefinition.

2. Maintenant, il faut définir les détails du champ : identifiant, label, widget par défaut, formatteur, etc. Cela remplace hook_field_info de Drupal 7.

Dans Drupal 8, beaucoup (sinon tous) les hooks d’information sont remplacés par des annotations.

/**
 * Implémentation du plugin pour le type de champ 'snippets'.
 *
 * @FieldType(
 *   id = "snippets_code",
 *   label = @Translation("Champ d’extraits"),
 *   description = @Translation("Ce champ stocke des extraits de code dans la base de données."),
 *   default_widget = "snippets_default",
 *   default_formatter = "snippets_default"
 * )
 */
class SnippetsItem extends FieldItemBase { }

Au lieu de hook_field_info, on définit le champ via une annotation dans le commentaire au-dessus de la classe.

Les attributs de l’annotation sont explicites. Veillez à ce que default_widget et default_formatter réfèrent aux identifiants des annotations widget et formatteur, pas aux classes.

Pour en savoir plus sur les annotations, consultez la page documentation des plugins basés sur annotations sur drupal.org.

3. Avec la classe d’élément prête, définissons plusieurs méthodes. Premièrement, schema().

Dans Drupal 7, la définition du schéma de champ se faisait via hook_field_schema. En Drupal 8, on définit cette méthode dans SnippetsItem.

La documentation de l’API schema décrit la structure et les valeurs possibles du tableau schema.

/**
 * {@inheritdoc}
 */
public static function schema(FieldStorageDefinitionInterface $field) {
  return [
    'columns' => [
      'source_description' => [
        'type' => 'varchar',
        'length' => 256,
        'not null' => FALSE,
      ],
      'source_code' => [
        'type' => 'text',
        'size' => 'big',
        'not null' => FALSE,
      ],
      'source_lang' => [
        'type' => 'varchar',
        'length' => 256,
        'not null' => FALSE,
      ],
    ],
  ];
}

4. Ajoutons la méthode isEmpty() pour définir quand un élément de champ est vide. Cette méthode remplace hook_field_is_empty en Drupal 7.

/**
 * {@inheritdoc}
 */
public function isEmpty() {
  $value = $this->get('source_code')->getValue();
  return $value === NULL || $value === '';
}

5. Enfin, ajoutons la méthode propertyDefinitions().

/**
 * {@inheritdoc}
 */
static $propertyDefinitions;

/**
 * {@inheritdoc}
 */
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
  $properties['source_description'] = DataDefinition::create('string')
    ->setLabel(t('Description de l’extrait'));

  $properties['source_code'] = DataDefinition::create('string')
    ->setLabel(t('Code de l’extrait'));

  $properties['source_lang'] = DataDefinition::create('string')
    ->setLabel(t('Langage de programmation'))
    ->setDescription(t('Langage du code de l’extrait'));

  return $properties;
}

Cette méthode définit les types de données stockés dans les valeurs du champ. Notre champ d’extraits contient trois valeurs : description, code et langage, que l’on déclare ici comme des chaînes.

Pour en savoir plus, consultez comment l’Entity API implémente la documentation Typed Data API sur drupal.org.

Cliquez ici pour voir le fichier complet. Note : il doit être mis à jour selon la spécification PSR-4, voir https://www.drupal.org/node/2128865 pour plus d’infos.

Étape 2 : implémenter le widget de champ

Après avoir défini l’élément de champ, créons le widget. Il faut créer une classe SnippetsDefaultWidget qui étend WidgetBase.

1. Créez le fichier SnippetsDefaultWidget.php dans « module »/src/Plugin/Field/FieldWidget/SnippetsDefaultWidget.php.

/**
 * @file
 * Contient \Drupal\snippets\Plugin\Field\FieldWidget\SnippetsDefaultWidget.
 */

namespace Drupal\snippets\Plugin\Field\FieldWidget;

use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Form\FormStateInterface;

Assurez-vous que le namespace est Drupal\snippets\Plugin\Field\FieldWidget et ajoutez les use suivants :

  • Drupal\Core\Field\FieldItemListInterface
  • Drupal\Core\Field\WidgetBase
  • Drupal\Core\Form\FormStateInterface.

2. Définissez le widget avec une annotation, équivalent à hook_field_widget_info en Drupal 7 :

/**
 * Implémentation du plugin pour le widget 'snippets_default'.
 *
 * @FieldWidget(
 *   id = "snippets_default",
 *   label = @Translation("Snippets default"),
 *   field_types = {
 *     "snippets_code"
 *   }
 * )
 */
class SnippetsDefaultWidget extends WidgetBase { }

Veillez à ce que l’attribut field_types dans l’annotation réfère aux IDs des types de champ. Ici, c’est snippets_code car défini dans l’annotation @FieldType.

3. Ajoutez la méthode formElement() qui définit le formulaire du widget, équivalent à hook_field_widget_form en Drupal 7 :

/**
 * {@inheritdoc}
 */
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {

  $element['source_description'] = [
    '#title' => $this->t('Description'),
    '#type' => 'textfield',
    '#default_value' => isset($items[$delta]->source_description) ? $items[$delta]->source_description : NULL,
  ];
  $element['source_code'] = [
    '#title' => $this->t('Code'),
    '#type' => 'textarea',
    '#default_value' => isset($items[$delta]->source_code) ? $items[$delta]->source_code : NULL,
  ];
  $element['source_lang'] = [
    '#title' => $this->t('Langage source'),
    '#type' => 'textfield',
    '#default_value' => isset($items[$delta]->source_lang) ? $items[$delta]->source_lang : NULL,
  ];
  return $element;
}

Cliquez ici pour voir le fichier complet. Note : il doit être mis à jour selon la spécification PSR-4, voir https://www.drupal.org/node/2128865.

Étape 3 : implémenter le formatteur de champ

Dernier élément : le formatteur. Créez la classe SnippetsDefaultFormatter qui étend FormatterBase.

1. Créez le fichier SnippetsDefaultFormatter.php dans « module »/src/Plugin/Field/FieldFormatter/SnippetsDefaultFormatter.php.

/**
 * @file
 * Contient \Drupal\snippets\Plugin\field\formatter\SnippetsDefaultFormatter.
 */

namespace Drupal\snippets\Plugin\Field\FieldFormatter;

use Drupal\Core\Field\FormatterBase;
use Drupal\Core\Field\FieldItemListInterface;

Assurez-vous que le namespace est Drupal\snippets\Plugin\Field\FieldFormatter et ajoutez les use :

  • Drupal\Core\Field\FieldItemListInterface
  • Drupal\Core\Field\FormatterBase

2. Définissez le formatteur avec une annotation, comme pour le widget et le type, équivalent à hook_field_formatter_info :

/**
 * Implémentation du plugin pour le formatteur 'snippets_default'.
 *
 * @FieldFormatter(
 *   id = "snippets_default",
 *   label = @Translation("Snippets default"),
 *   field_types = {
 *     "snippets_code"
 *   }
 * )
 */
class SnippetsDefaultFormatter extends FormatterBase { }

3. Ajoutez la méthode viewElements() qui définit le rendu du champ, équivalent à hook_field_formatter_view :

/**
 * {@inheritdoc}
 */
public function viewElements(FieldItemListInterface $items, $langcode) {
  $elements = [];
  foreach ($items as $delta => $item) {
    // Rendu utilisant le thème snippets_default.
    $source = [
      '#theme' => 'snippets_default',
      '#source_description' => $item->source_description,
      '#source_code' => $item->source_code,
    ];
    
    $elements[$delta] = ['#markup' => drupal_render($source)];
  }

  return $elements;
}

Notez que j’utilise un template personnalisé snippets_default pour rendre les extraits avant l’affichage par le formatteur.

Cela évite de placer trop de logique ou HTML dans viewElements().

Cliquez ici pour voir le fichier complet. Note : à mettre à jour selon PSR-4, voir https://www.drupal.org/node/2128865.

Conclusion

Comme indiqué, la grande nouveauté de Drupal 8 est que les champs sont créés via l’API des plugins plutôt que par hooks. Une fois compris, le concept de création de champ est très similaire à Drupal 7. Beaucoup de méthodes correspondent aux anciens hooks.

Si vous souhaitez tester les extraits de code, téléchargez la version 8.x-dev et essayez.