9.10.3. Lavorare con i campi in Drupal. Creare un proprio tipo di campo, widget e formatter per inserire video da YouTube.
Negli articoli precedenti abbiamo visto come funziona il tipo di campo Link: Storage, Widget, Formatter. In questo articolo creeremo un nostro tipo di campo personalizzato per visualizzare video di YouTube su una pagina, con due formati e impostazioni differenti.
Questo articolo è dedicato alla Fields API e, se hai bisogno di aggiungere un campo video di YouTube al tuo sito, è meglio utilizzare il modulo già pronto:
https://www.drupal.org/project/video_embed_field
Ho aggiunto tutto il codice su GitHub nel modulo drupalbook_youtube
, puoi scaricare il modulo e aggiungerlo al tuo sito:
https://github.com/levmyshkin/drupalbook8
Vediamo ora la struttura di questo modulo e cercherò di descrivere come funziona questo tipo di campo:
modules/custom/drupalbook_youtube/drupalbook_youtube.info.yml
name: DrupalBook Youtube
type: module
description: Youtube embed field
core: 8.x
package: Custom
Definiamo i metadati del modulo.
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;
/**
* Implementazione del plugin per il tipo di campo 'drupalbook_youtube'.
*
* @FieldType(
* id = "drupalbook_youtube",
* label = @Translation("Embed Youtube video"),
* module = "drupalbook_youtube",
* description = @Translation("Output video from Youtube."),
* default_widget = "drupalbook_youtube",
* default_formatter = "drupalbook_youtube_thumbnail"
* )
*/
class DrupalbookYoutubeItem extends FieldItemBase {
/**
* {@inheritdoc}
*/
public static function schema(FieldStorageDefinitionInterface $field_definition) {
return array(
'columns' => array(
'value' => array(
'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('Youtube video URL'));
return $properties;
}
}
Creiamo un tipo di campo in modo che Drupal sappia cosa verrà memorizzato nella tabella per questo campo.
<?php
namespace Drupal\drupalbook_youtube\Plugin\Field\FieldType;
use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\TypedData\DataDefinition;
Definisci gli spazi dei nomi per il nostro tipo di campo.
/**
* Implementazione del plugin per il tipo di campo 'drupalbook_youtube'.
*
* @FieldType(
* id = "drupalbook_youtube",
* label = @Translation("Embed Youtube video"),
* module = "drupalbook_youtube",
* description = @Translation("Output video from Youtube."),
* default_widget = "drupalbook_youtube",
* default_formatter = "drupalbook_youtube_thumbnail"
* )
*/
Scriviamo un'annotazione per la nostra classe: da questa annotazione Drupal prenderà il nome del nostro tipo di campo e il suo machine name.
class DrupalbookYoutubeItem extends FieldItemBase {
Il nome della classe è meglio scriverlo con "Item" alla fine.
/**
* {@inheritdoc}
*/
public static function schema(FieldStorageDefinitionInterface $field_definition) {
return array(
'columns' => array(
'value' => array(
'type' => 'text',
'size' => 'tiny',
'not null' => FALSE,
),
),
);
}
Definiamo che memorizzeremo nel database un campo value
di tipo testo.
/**
* {@inheritdoc}
*/
public function isEmpty() {
$value = $this->get('value')->getValue();
return $value === NULL || $value === '';
}
Nel caso in cui il campo venga richiamato da codice esterno, restituiamo un valore vuoto come fallback se il campo non contiene dati.
/**
* {@inheritdoc}
*/
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
$properties['value'] = DataDefinition::create('string')
->setLabel(t('Youtube video URL'));
return $properties;
}
Descriviamo le colonne per la tabella MySQL e l’oggetto entità. Di conseguenza, verrà memorizzato l’intero link:
Ora che abbiamo aggiunto un tipo di campo, creiamo un Widget per l’inserimento dei dati:
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;
/**
* Implementazione del plugin per il widget 'drupalbook_youtube'.
*
* @FieldWidget(
* id = "drupalbook_youtube",
* module = "drupalbook_youtube",
* label = @Translation("Youtube video URL"),
* 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 += array(
'#type' => 'textfield',
'#default_value' => $value,
'#size' => 32,
'#maxlength' => 256,
'#element_validate' => array(
array($this, 'validate'),
),
);
return array('value' => $element);
}
/**
* Validazione del campo di testo.
*/
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("Youtube video URL is not correct."));
}
}
}
Il widget ci permette di inserire i dati nel form di modifica dell’entità.
Abbiamo specificato nella annotazione della classe il field_type
che abbiamo creato prima, cioè drupalbook_youtube
.
Alla fine del nome della classe aggiungiamo Widget per indicare che la classe serve per un Field Widget.
Creiamo un semplice campo di testo tramite la Form API in cui inseriremo il link al video YouTube.
La callback di validazione che abbiamo indicato sopra in #element_validate
serve per assicurarci che l’utente inserisca un link valido di YouTube. L’espressione regolare è presa da StackOverflow; puoi sostituirla se non funziona correttamente.
Ora possiamo inserire i dati nel nostro campo; manca solo aggiungere un Field Formatter per l’output dei dati.