Resumen: creación de un campo personalizado
Este tutorial fue publicado originalmente en Web Wash. Sin embargo, Berdir preguntó si podía publicar el tutorial aquí, así que aquí está.
El módulo en Drupal 7 permite almacenar ejemplos de código / fragmentos en un campo. Viene con un campo personalizado llamado "Campo de fragmentos" y muestra tres elementos del formulario: descripción, código fuente y modo de resaltado de sintaxis (qué lenguaje de programación).
Pero ahora es tiempo de actualizar el módulo a 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, o esta lección sería enorme.
En lugar de eso, agregaré enlaces a otros sitios que explican estos conceptos con mayor detalle.
Dicho esto, si buscas documentación detallada sobre la API de Campos (Field API) en Drupal 8, revisa las siguientes series:
En Drupal 8, los campos no se implementan usando hooks como en Drupal 7. En lugar de eso, se crean usando la nueva API de plugins de Drupal 8. Esto significa que en lugar de implementar hooks, definimos una clase para el widget, el formateador y el elemento de campo. La mayoría de los 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 de campo
La primera parte del trabajo que necesitamos hacer es definir una clase de elemento de campo llamada SnippetsItem que extienda la clase FieldItemBase.
1. En Drupal 8, las clases se cargan usando PSR-4.
Por lo tanto, para definir la clase SnippetsItem, necesitamos crear un 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, en el archivo agregamos el espacio de nombres Drupal\snippets\Plugin\Field\FieldType y tres declaraciones use: Drupal\Core\Field\FieldItemBase, Drupal\Core\Field\FieldStorageDefinitionInterface y Drupal\Core\TypedData\DataDefinition.
2. Ahora necesitamos definir los detalles reales del campo, como el identificador del campo, etiqueta, widget por defecto, formateador, etc. Esto equivale 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 para el 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 { }
Por lo tanto, en lugar de implementar hook_field_info, definimos el campo como una anotación dentro del comentario sobre la clase.
Los atributos de anotación son autoexplicativos. Solo asegúrate de que default_widget y default_formatter hagan referencia al identificador de la anotación del widget y formateador, no a la clase.
Si quieres aprender más sobre anotaciones, visita la página de documentación de plugins basados en anotaciones en drupal.org.
3. Ahora que tenemos la clase del elemento de campo, necesitamos definir algunos métodos. Primero, veremos schema().
En Drupal 7, al crear un campo personalizado definías el esquema usando hook_field_schema. En Drupal 8 definimos el esquema agregando el método schema() en la clase SnippetsItem.
La documentación de la API Schema contiene una descripción de la estructura del array esquema y los posibles 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 necesitamos agregar el método isEmpty() y definir qué constituye un elemento de campo vacío. Este método es equivalente a la implementación de 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 a la clase es el método 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 se utiliza para definir el tipo de datos que existen en los valores del campo. El "Campo de fragmentos" solo tiene tres valores: descripción, código y lenguaje. Por eso simplemente agregué esos valores como cadenas en el método.
Para aprender más sobre esto, consulta la sección Cómo la Entity API implementa la documentación de la API Typed Data en drupal.org.
Haz clic aquí para ver el archivo completo. Nota: necesita actualizarse a la especificación PSR-4, para más detalles consulta https://www.drupal.org/node/2128865
Paso 2: Implementar el widget de campo
Ahora que hemos definido el elemento de campo, vamos a crear el widget de campo. Necesitamos crear una clase llamada SnippetsDefaultWidget que extienda la clase WidgetBase.
1. Así que 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 que el espacio de nombres del archivo sea Drupal\snippets\Plugin\Field\FieldWidget y agrega las siguientes tres declaraciones use: Drupal\Core\Field\FieldItemListInterface, Drupal\Core\Field\WidgetBase y Drupal\Core\Form\FormStateInterface.
2. Luego necesitamos definir el widget mediante una anotación. Esto equivale al uso de hook_field_widget_info en Drupal 7.
/** * Implementación del plugin para el widget 'snippets_default'. * * @FieldWidget( * id = "snippets_default", * label = @Translation("Predeterminado de fragmentos"), * field_types = { * "snippets_code" * } * ) */ class SnippetsDefaultWidget extends WidgetBase { }
Para adelantarte, asegúrate que el atributo field_types en la anotación haga referencia a los tipos de campo usando sus IDs. Para este módulo es snippets_code, porque agregamos id = "snippets_code" en la anotación @FieldType.
3. Finalmente, necesitamos definir el formulario real del widget. Lo hacemos agregando el método formElement() en la clase SnippetsDefaultWidget. Este método es equivalente al uso de 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 actualizarse a la especificación PSR-4, para más detalles consulta https://www.drupal.org/node/2128865.
Paso 3: Implementar el formateador de campo
El último elemento del rompecabezas es el formateador de campo, y lo creamos definiendo una clase llamada SnippetsDefaultFormatter que extienda la clase 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\FieldFormatter\SnippetsDefaultFormatter. */ namespace Drupal\snippets\Plugin\Field\FieldFormatter; use Drupal\Core\Field\FormatterBase; use Drupal\Core\Field\FieldItemListInterface;
Asegúrate que el espacio de nombres del archivo sea Drupal\snippets\Plugin\Field\FieldFormatter y agrega las siguientes declaraciones use: Drupal\Core\Field\FieldItemListInterface y Drupal\Core\Field\FormatterBase.
2. Luego necesitamos definir el formateador como una anotación. Al igual que hicimos para el widget y el tipo de campo, esto equivale al uso de hook_field_formatter_info.
/** * Implementación del plugin para el formateador 'snippets_default'. * * @FieldFormatter( * id = "snippets_default", * label = @Translation("Predeterminado de fragmentos"), * field_types = { * "snippets_code" * } * ) */ class SnippetsDefaultFormatter extends FormatterBase { }
3. Solo queda agregar el método viewElements() y definir el módulo real de formateo de campo. Nuevamente, este método es equivalente al uso de hook_field_formatter_view en Drupal 7.
/** * {@inheritdoc} */ public function viewElements(FieldItemListInterface $items, $langcode) { $elements = array(); foreach ($items as $delta => $item) { // Renderizar salida 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; }
Es importante notar que uso una plantilla personalizada snippets_default para renderizar los fragmentos antes de que sean mostrados por el formateador.
La razón es que no quería poner mucha lógica o código HTML en el método viewElements().
Haz clic aquí para ver el archivo completo. Nota: necesita actualizarse a la especificación PSR-4, para más detalles consulta https://www.drupal.org/node/2128865
Conclusión
Como se mencionó antes, el cambio más grande en Drupal 8 es que los campos se crean usando la API de plugins, no hooks. Una vez que entiendes esto, el concepto de crear un campo es muy parecido a Drupal 7. Muchos métodos en Drupal 8 corresponden a hooks en Drupal 7.
Si quieres probar fragmentos de código, 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.