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

9.10.3. Travailler avec les champs dans Drupal. Créez votre propre type de champ, widget, et formatteur pour insérer des vidéos depuis Youtube.

07/07/2025, by Ivan

Menu

Dans les articles précédents, nous avons vu comment fonctionne le type de champ Lien : Stockage, Widget, Formatteur. Dans cet article, nous allons créer notre propre type de champ personnalisé pour afficher une vidéo Youtube sur une page avec deux formats et réglages différents.

Cet article se concentre sur l’API des Champs (Fields API), et si vous avez besoin d’ajouter un champ vidéo Youtube à votre site, il est préférable d’utiliser un module déjà prêt :

https://www.drupal.org/project/video_embed_field

J’ai ajouté tout le code sur GitHub dans le module drupalbook_youtube, vous pouvez télécharger ce module et l’ajouter à votre site :

https://github.com/levmyshkin/drupalbook8

Regardons le listing de ce module et je vais essayer de décrire comment ce type de champ fonctionne :

modules/custom/drupalbook_youtube/drupalbook_youtube.info.yml

name: DrupalBook Youtube
type: module
description: Champ d’intégration Youtube
core: 8.x
package: Custom

Définition des métadonnées pour le module.

modules/custom/drupalbook_youtube/src/Plugin/Field/FieldType/DrupalbookYoutubeItem.php

<?php

namespace Drupal\drupalbook_youtube\Plugin\Field\FieldType;

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

/**
 * Implémentation du plugin pour le type de champ 'drupalbook_youtube'.
 *
 * @FieldType(
 *   id = "drupalbook_youtube",
 *   label = @Translation("Intégrer une vidéo Youtube"),
 *   module = "drupalbook_youtube",
 *   description = @Translation("Affiche une vidéo depuis Youtube."),
 *   default_widget = "drupalbook_youtube",
 *   default_formatter = "drupalbook_youtube_thumbnail"
 * )
 */
class DrupalbookYoutubeItem extends FieldItemBase {
  /**
   * {@inheritdoc}
   */
  public static function schema(FieldStorageDefinitionInterface $field_definition) {
    return [
      'columns' => [
        'value' => [
          'type' => 'text',
          'size' => 'tiny',
          'not null' => FALSE,
        ],
      ],
    ];
  }

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

  /**
   * {@inheritdoc}
   */
  public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
    $properties['value'] = DataDefinition::create('string')
      ->setLabel(t('URL de la vidéo Youtube'));

    return $properties;
  }
}

Création d’un type de champ pour que Drupal sache quoi stocker dans la table pour ce champ.

Définition des namespaces pour notre type de champ et annotation du plugin avec l’identifiant, le label, le module, la description, le widget et le formatteur par défaut.

Le nom de la classe se termine par Item pour indiquer qu’il s’agit d’un type de champ.

Nous définissons que nous stockerons un champ « value » de type texte.

La méthode isEmpty() sert à retourner vrai si le champ est vide, ce qui permettra à Drupal de gérer proprement les champs vides.

La méthode propertyDefinitions() décrit les propriétés du champ, ici juste une chaîne pour l’URL de la vidéo Youtube.

Voici un aperçu de ce que l’on stockera : le lien complet vers la vidéo.

select

Maintenant que le type de champ est ajouté, créons un Widget pour la saisie des données :

modules/custom/drupalbook_youtube/src/Plugin/Field/FieldWidget/DrupalbookYoutubeWidget.php

<?php

namespace Drupal\drupalbook_youtube\Plugin\Field\FieldWidget;

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

/**
 * Implémentation du plugin pour le widget 'drupalbook_youtube'.
 *
 * @FieldWidget(
 *   id = "drupalbook_youtube",
 *   module = "drupalbook_youtube",
 *   label = @Translation("URL de la vidéo Youtube"),
 *   field_types = {
 *     "drupalbook_youtube"
 *   }
 * )
 */
class DrupalbookYoutubeWidget extends WidgetBase {

  /**
   * {@inheritdoc}
   */
  public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
    $value = isset($items[$delta]->value) ? $items[$delta]->value : '';
    $element += [
      '#type' => 'textfield',
      '#default_value' => $value,
      '#size' => 32,
      '#maxlength' => 256,
      '#element_validate' => [
        [$this, 'validate'],
      ],
    ];
    return ['value' => $element];
  }

  /**
   * Validation du champ URL Youtube.
   */
  public function validate($element, FormStateInterface $form_state) {
    $value = $element['#value'];
    if (strlen($value) == 0) {
      $form_state->setValueForElement($element, '');
      return;
    }
    if (!preg_match("#(?=v=)[a-zA-Z0-9-]+(?=&)|(?=v\/)[^&\n]+(?=\?)|(?=v=)[^&\n]+|(?=youtu.be/)[^&\n]+#", $value, $matches)) {
      $form_state->setError($element, t("L’URL de la vidéo Youtube n’est pas correcte."));
    }
  }
}

Le widget permet la saisie du lien Youtube dans le formulaire d’édition de l’entité.

Nous avons précisé dans l’annotation que ce widget correspond au type de champ « drupalbook_youtube » créé plus haut.

La méthode formElement() crée un simple champ texte avec validation personnalisée.

La validation vérifie que le lien correspond bien à un format d’URL Youtube valide via une expression régulière. Cette expression peut être adaptée si nécessaire.

Passons maintenant au Field Formatter qui affichera les données.

modules/custom/drupalbook_youtube/src/Plugin/Field/FieldFormatter/DrupalbookYoutubeThumbnailFormatter.php

Nous commencerons par un formatteur simple qui affiche une image cliquable vers la vidéo Youtube :

<?php

namespace Drupal\drupalbook_youtube\Plugin\Field\FieldFormatter;

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

/**
 * Implémentation du plugin pour le formatteur 'drupalbook_youtube_thumbnail'.
 *
 * @FieldFormatter(
 *   id = "drupalbook_youtube_thumbnail",
 *   module = "drupalbook_youtube",
 *   label = @Translation("Affiche la vignette de la vidéo"),
 *   field_types = {
 *     "drupalbook_youtube"
 *   }
 * )
 */
class DrupalbookYoutubeThumbnailFormatter extends FormatterBase {

  /**
   * {@inheritdoc}
   */
  public function viewElements(FieldItemListInterface $items, $langcode) {
    $elements = [];

    foreach ($items as $delta => $item) {
      preg_match("#(?=v=)[a-zA-Z0-9-]+(?=&)|(?=v\/)[^&\n]+(?=\?)|(?=v=)[^&\n]+|(?=youtu.be/)[^&\n]+#", $item->value, $matches);

      if (!empty($matches)) {
        $content = '';
        $elements[$delta] = [
          '#type' => 'html_tag',
          '#tag' => 'p',
          '#value' => $content,
        ];
      }
    }

    return $elements;
  }

}

Nous définissons le formatteur avec son annotation et indiquons le type de champ.

Le rendu utilise #type html_tag pour retourner une balise <p> contenant une image cliquable vers la vidéo.

Les vignettes sont générées directement par Youtube, accessibles via l’ID vidéo extrait.

Le paramètre $delta permet de gérer plusieurs valeurs dans le champ.

Passons maintenant à un formatteur plus avancé avec template et paramètres :

modules/custom/drupalbook_youtube/src/Plugin/Field/FieldFormatter/DrupalbookYoutubeVideoFormatter.php

<?php

namespace Drupal\drupalbook_youtube\Plugin\Field\FieldFormatter;

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

/**
 * Implémentation du plugin pour le formatteur 'drupalbook_youtube_video'.
 *
 * @FieldFormatter(
 *   id = "drupalbook_youtube_video",
 *   module = "drupalbook_youtube",
 *   label = @Translation("Affiche la vidéo Youtube"),
 *   field_types = {
 *     "drupalbook_youtube"
 *   }
 * )
 */
class DrupalbookYoutubeVideoFormatter extends FormatterBase {

  /**
   * {@inheritdoc}
   */
  public static function defaultSettings() {
    return [
      'width' => '600',
      'height' => '450',
    ] + parent::defaultSettings();
  }

  /**
   * {@inheritdoc}
   */
  public function settingsForm(array $form, FormStateInterface $form_state) {
    $elements['width'] = [
      '#type' => 'textfield',
      '#title' => t('Largeur de la vidéo Youtube'),
      '#default_value' => $this->getSetting('width'),
    ];
    $elements['height'] = [
      '#type' => 'textfield',
      '#title' => t('Hauteur de la vidéo Youtube'),
      '#default_value' => $this->getSetting('height'),
    ];

    return $elements;
  }

  /**
   * {@inheritdoc}
   */
  public function viewElements(FieldItemListInterface $items, $langcode) {
    $elements = [];
    $width = $this->getSetting('width');
    $height = $this->getSetting('height');

    foreach ($items as $delta => $item) {
      preg_match("#(?=v=)[a-zA-Z0-9-]+(?=&)|(?=v\/)[^&\n]+(?=\?)|(?=v=)[^&\n]+|(?=youtu.be/)[^&\n]+#", $item->value, $matches);

      if (!empty($matches)) {
        $elements[$delta] = [
          '#theme' => 'drupalbook_youtube_video_formatter',
          '#width' => $width,
          '#height' => $height,
          '#video_id' => $matches[0],
        ];
      }
    }

    return $elements;
  }

  /**
   * {@inheritdoc}
   */
  public function settingsSummary() {
    $summary = [];

    $settings = $this->getSettings();

    if (!empty($settings['width']) && !empty($settings['height'])) {
      $summary[] = t('Taille de la vidéo : @width x @height', ['@width' => $settings['width'], '@height' => $settings['height']]);
    }
    else {
      $summary[] = t('Définir la taille de la vidéo');
    }

    return $summary;
  }

}

Au début du fichier, les namespaces standard et l’annotation du plugin sont présents.

Le nom de la classe se termine par Formatter pour indiquer qu’il s’agit d’un Field Formatter.

Nous définissons des réglages par défaut pour la taille de la vidéo, puis un formulaire de configuration pour permettre de modifier ces dimensions dans l’interface d’administration (page Gérer l’affichage).

La méthode settingsSummary() retourne un résumé affiché sur la page Gérer l’affichage.

La méthode viewElements() est la plus importante pour le rendu. Elle récupère les paramètres de largeur et hauteur, puis boucle sur les valeurs du champ.

Pour chaque valeur, elle extrait l’ID de la vidéo Youtube via une expression régulière et construit un rendu en spécifiant la clé #theme pour utiliser un template twig.

Nous définissons ce template dans le fichier drupalbook_youtube.module :

modules/custom/drupalbook_youtube/drupalbook_youtube.module

/**
 * Implémente hook_theme().
 */
function drupalbook_youtube_theme() {
  return [
    'drupalbook_youtube_video_formatter' => [
      'variables' => ['width' => 600, 'height' => 450, 'video_id' => NULL],
    ],
  ];
}

Nous définissons les valeurs par défaut de largeur, hauteur et la variable video_id qui sera récupérée depuis #video_id dans le tableau des éléments.

Enfin, voici le fichier template twig :

modules/custom/drupalbook_youtube/templates/drupalbook-youtube-video-formatter.html.twig

{#
/**
 * @file
 * Implémentation du thème par défaut d’une vidéo Youtube simple.
 *
 * Variables disponibles :
 * - width : largeur de la vidéo Youtube.
 * - height : hauteur de la vidéo Youtube.
 * - video_id : ID de la vidéo Youtube.
 *
 * @see template_preprocess()
 * @see template_drupalbook_youtube_video_formatter()
 *
 * @ingroup themeable
 */
#}
{% spaceless %}
  <iframe width="{{ width }}" height="{{ height }}" src="https://www.youtube.com/embed/{{ video_id }}" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>
{% endspaceless %}

Vous pouvez maintenant créer un champ vidéo Youtube :

link

Voilà, nous terminons ici la création de champs personnalisés et passerons à l’Entity API où nous créerons des types d’entités personnalisés.