logo

Extra Block Types (EBT) - Nueva experiencia con Layout Builder❗

Extra Block Types (EBT): tipos de bloques con estilo y personalizables: Presentaciones de diapositivas, Pestañas, Tarjetas, Acordeones y muchos más. Configuraciones integradas para fondo, DOM Box y plugins de JavaScript. Experimenta hoy el futuro de la construcción de diseños.

Módulos de demostración EBT Descargar módulos EBT

❗Extra Paragraph Types (EPT) - Nueva experiencia con Paragraphs

Extra Paragraph Types (EPT): conjunto de módulos basado en párrafos de forma análoga.

Módulos de demostración EPT Descargar módulos EPT

Scroll
20/06/2025, by Ivan

Menu

Este tutorial fue publicado originalmente en Web Wash. Sin embargo, Berdir preguntó si podía publicarlo aquí, así que aquí está.

Un módulo en Drupal 7 permite almacenar ejemplos de código o fragmentos en un campo. Viene con un campo personalizado llamado "Campo de fragmentos" y muestra tres elementos de formulario: descripción, código fuente y modo de resaltado de sintaxis (qué lenguaje de programación).

Pero ahora es momento de actualizar el módulo para Drupal 8.

En esta lección te mostraré cómo creé un campo personalizado "básico" en Drupal 8. No entraré en detalles sobre PSR-4, anotaciones o plugins, para que esta lección no sea enorme.

En su lugar, incluiré enlaces a otros sitios que explican más a fondo estos conceptos.

Dicho esto, si buscas documentación detallada sobre Field API en Drupal 8, revisa estas series:

En Drupal 8, los campos no se implementan usando hooks como en Drupal 7. En su lugar, se crean usando la nueva API de plugins de Drupal 8. Esto significa que en lugar de implementar hooks, definimos clases para el widget, formateador y elemento del campo. Muchos hooks de Drupal 7, como hook_field_schema, hook_field_is_empty y otros, ahora son métodos en clases.

Paso 1: Implementar el elemento del campo

La primera parte es definir una clase del elemento del campo llamada SnippetsItem que extiende FieldItemBase.

1. En Drupal 8 las clases se cargan usando PSR-4.

Así que para definir la clase SnippetsItem, necesitamos crear el archivo SnippetsItem.php y colocarlo en módulo/src/Plugin/Field/FieldType/SnippetsItem.php

/**
 * @file
 * Contiene \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;

Luego agregamos el namespace Drupal\snippets\Plugin\Field\FieldType y tres declaraciones use:

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

2. Ahora definimos los detalles reales del campo, como ID, etiqueta, widget por defecto, formateador, etc. Esto es equivalente a implementar hook_field_info en Drupal 7.

En Drupal 8 muchos, si no todos, los hooks de información han sido reemplazados por anotaciones.

/**
 * Implementación del plugin tipo de campo 'snippets'.
 *
 * @FieldType(
 *   id = "snippets_code",
 *   label = @Translation("Campo de fragmentos"),
 *   description = @Translation("Este campo almacena fragmentos de código en la base de datos."),
 *   default_widget = "snippets_default",
 *   default_formatter = "snippets_default"
 * )
 */
class SnippetsItem extends FieldItemBase { }

Así que en lugar de implementar hook_field_info, definimos el campo como una anotación sobre la clase.

Los atributos en la anotación son autoexplicativos. Solo asegúrate que default_widget y default_formatter referencien el ID de anotación del widget y formateador, no las clases.

Para aprender más sobre anotaciones, visita la documentación de plugins basados en anotaciones en drupal.org.

3. Ahora que tenemos la clase del elemento del campo, necesitamos definir algunos métodos. Primero veremos schema().

En Drupal 7, para crear un campo personalizado definías su esquema usando hook_field_schema. En Drupal 8 definimos el esquema agregando el método schema() en la clase SnippetsItem.

La documentación del API de esquema describe la estructura del array esquema y sus valores.

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

4. Ahora agregamos el método isEmpty() para definir cuándo un elemento está vacío. Este método es similar a hook_field_is_empty en Drupal 7.

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

5. El último método que agregaremos es propertyDefinitions().

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

/**
 * {@inheritdoc}
 */
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
    $properties['source_description'] = DataDefinition::create('string')
      ->setLabel(t('Descripción del fragmento'));

    $properties['source_code'] = DataDefinition::create('string')
      ->setLabel(t('Código del fragmento'));

    $properties['source_lang'] = DataDefinition::create('string')
      ->setLabel(t('Lenguaje de programación'))
      ->setDescription(t('Lenguaje del código del fragmento'));

    return $properties;
  }

Este método define el tipo de datos presentes en los valores del campo. El "campo de fragmentos" tiene solo tres valores: descripción, código y lenguaje. Por eso simplemente los añadí como cadenas.

Para aprender más, ve la sección Cómo Entity API implementa la documentación de Typed Data API en drupal.org.

Haz clic aquí para ver el archivo completo. Nota: necesita actualización a especificación PSR-4, para más detalles ve https://www.drupal.org/node/2128865.

Paso 2: Implementar el widget de campo

Ahora que definimos el elemento de campo, vamos a crear el widget del campo. Necesitamos crear una clase llamada SnippetsDefaultWidget que extienda WidgetBase.

1. Crea el archivo SnippetsDefaultWidget.php y colócalo en módulo/src/Plugin/Field/FieldWidget/SnippetsDefaultWidget.php.

/**
 * @file
 * Contiene \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;

Asegúrate de que el namespace del archivo sea Drupal\snippets\Plugin\Field\FieldWidget y añade las siguientes declaraciones use:

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

2. Luego, definimos el widget mediante una anotación. Esto equivale a usar hook_field_widget_info en Drupal 7.

/**
 * Implementación del plugin widget 'snippets_default'.
 *
 * @FieldWidget(
 *   id = "snippets_default",
 *   label = @Translation("Snippets por defecto"),
 *   field_types = {
 *     "snippets_code"
 *   }
 * )
 */
class SnippetsDefaultWidget extends WidgetBase { }

Solo asegúrate de que el atributo field_types en la anotación refiera a los tipos de campo por su ID. En este módulo es snippets_code, porque agregamos id = "snippets_code" en la anotación @FieldType.

3. Finalmente definimos el formulario real del widget agregando el método formElement() a la clase SnippetsDefaultWidget. Este método es equivalente al hook_field_widget_form en Drupal 7.

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

  $element['source_description'] = array(
        '#title' => $this->t('Descripción'),
        '#type' => 'textfield',
        '#default_value' => isset($items[$delta]->source_description) ? $items[$delta]->source_description : NULL,
      );
  $element['source_code'] = array(
        '#title' => $this->t('Código'),
        '#type' => 'textarea',
        '#default_value' => isset($items[$delta]->source_code) ? $items[$delta]->source_code : NULL,
      );
  $element['source_lang'] = array(
        '#title' => $this->t('Lenguaje fuente'),
        '#type' => 'textfield',
        '#default_value' => isset($items[$delta]->source_lang) ? $items[$delta]->source_lang : NULL,
      );
  return $element;
}

Haz clic aquí para ver el archivo completo. Nota: necesita actualización a especificación PSR-4, para más detalles ve https://www.drupal.org/node/2128865.

Paso 3: Implementar el formateador de campo

El último elemento del rompecabezas es el formateador de campo, que creamos definiendo una clase llamada SnippetsDefaultFormatter que extiende FormatterBase.

1. Crea el archivo SnippetsDefaultFormatter.php y colócalo en módulo/src/Plugin/Field/FieldFormatter/SnippetsDefaultFormatter.php.

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

namespace Drupal\snippets\Plugin\Field\FieldFormatter;

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

Asegúrate que el namespace sea Drupal\snippets\Plugin\Field\FieldFormatter y añade las declaraciones use necesarias: Drupal\Core\Field\FieldItemListInterface y Drupal\Core\Field\FormatterBase.

2. Luego definimos el formateador con una anotación. Igual que hicimos para el widget y el tipo de campo, esto equivale a usar hook_field_formatter_info.

/**
 * Implementación del plugin formateador 'snippets_default'.
 *
 * @FieldFormatter(
 *   id = "snippets_default",
 *   label = @Translation("Snippets por defecto"),
 *   field_types = {
 *     "snippets_code"
 *   }
 * )
 */
class SnippetsDefaultFormatter extends FormatterBase { }

3. Finalmente, agregamos el método viewElements() para definir el módulo de renderizado del formateador. Este método es equivalente a hook_field_formatter_view en Drupal 7.

/**
 * {@inheritdoc}
 */
public function viewElements(FieldItemListInterface $items, $langcode) {
  $elements = array();
  foreach ($items as $delta => $item) {
    // Renderiza usando el tema snippets_default.
    $source = array(
      '#theme' => 'snippets_default',
      '#source_description' => $item->source_description,
      '#source_code' => $item->source_code,
    );
    
    $elements[$delta] = array('#markup' => drupal_render($source));
  }

  return $elements;
}

Nota que uso un template personalizado snippets_default para renderizar los fragmentos antes de que el formateador los muestre.

Esto es porque no quería poner mucha lógica o HTML en el método viewElements().

Haz clic aquí para ver el archivo completo. Nota: necesita actualización a especificación PSR-4, para más detalles ve https://www.drupal.org/node/2128865.

Conclusión

Como se indicó antes, el mayor cambio en Drupal 8 es que los campos se crean usando la API de plugins en lugar de hooks. Una vez que entiendas esto, la creación de un campo es muy similar a Drupal 7. Muchos métodos en Drupal 8 corresponden a hooks en Drupal 7.

Si quieres probar el módulo Snippets, descarga la versión 8.x-dev y pruébalo.

Drupal’s online documentation is © 2000-2020 by the individual contributors and can be used in accordance with the Creative Commons License, Attribution-ShareAlike 2.0. PHP code is distributed under the GNU General Public License.