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.
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.
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 :
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.