Drupal 8: Desarrollo de módulos

Rafa Morales 4 Febrero 2020
15min
0
Drupal 8: Desarrollo de módulos

En este artículo quiero recoger los conceptos básicos para comenzar a desarrollar nuestros propios módulos bajo Drupal 8. Es sólo un punto de partida, y dejaré enlaces para profundizar en más conceptos.

Serán necesarios conocimientos de PHP y Symfony antes de continuar.

 

Configuración XAMP

Ver las notas en el artículo Drupal 8: Guía de referencia.

 

Drush

Drush es un script de línea de comandos que nos permite administrar un sitio Drupal.

Si hemos realizado la instalación de nuestro sitio Drupal con Composer nos lo encontraremos instalado directamente. Incluye la siguiente ruta en la variable de entorno PATH para utilizarlo cómodamente.

/proyecto_composer/vendor/bin

Debemos utilizarlo desde la carpeta "web" del proyecto en la siguiente ruta:

/proyecto_composer/web$ drush version

 

Drupal Console

Drupal Console es un script de línea de comandos que nos permite administrar un sitio Drupal y crear plantillas de diferentes componentes mientras estamos desarrollando nuestros propios módulos.

Si hemos realizado la instalación de nuestro sitio Drupal con Composer nos lo encontraremos instalado directamente. Incluye la siguiente ruta en la variable de entorno PATH para utilizarlo cómodamente.

/proyecto_composer/vendor/bin

Debemos utilizarlo desde la carpeta "web" del proyecto en la siguiente ruta:

/proyecto_composer/web$ drupal about

 

API de Drupal

La API de Drupal la podemos encontrar en el siguiente enlace:

https://api.drupal.org/api/drupal

Y mucha más información en profundidad en el siguiente enlace:

https://www.drupal.org/developing/api

 

Estándar de codificación en Drupal

Todos los estándares recomendados para desarrollar bajo Drupal se pueden encontrar en el siguiente enlace:

https://www.drupal.org/developing/api

 

En concreto, el código debe cumplir los siguientes requisitos:

https://www.drupal.org/docs/develop/standards/coding-standards

 

En concreto, la documentación y los comentarios deben cumplir los siguientes requisitos:

https://www.drupal.org/docs/develop/standards/api-documentation-and-comment-standards

 

Coder

Coder es un conjunto de "sniffs" que permiten a PHP Code Sniffer analizar el código fuente de módulo y comprobar si cumple los estándares de codificación de Drupal. Por tanto, los "sniffs" son dichos estándares de codificación.

Instalar Coder y PHP Code Sniffer:

composer require dealerdirect/phpcodesniffer-composer-installer
composer require drupal/coder

Incluye la siguiente ruta en la variable de entorno PATH para utilizarlo cómodamente.

/proyecto_composer/vendor/bin

 

Podemos analizar los estándares de codificación de un módulo especificando las extensiones a analizar y la carpeta de dicho módulo:

phpcs --standard=Drupal --extensions=php,module,inc,install,test,profile,theme,twig,css,json,info,yml,txt,md /proyecto_composer/web/modules/contrib/admin_toolbar/

 

Podemos analizar las buenas prácticas de codificación de un módulo especificando las extensiones a analizar y la carpeta de dicho módulo:

phpcs --standard=DrupalPractice --extensions=php,module,inc,install,test,profile,theme,twig,css,json,info,yml,txt,md /proyecto_composer/web/modules/contrib/admin_toolbar/

 

Podemos solucionar los errores de codificación de un módulo especificando las extensiones a analizar y la carpeta de dicho módulo:

phpcbf --standard=Drupal --extensions=php,module,inc,install,test,profile,theme,twig,css,json,info,yml,txt,md /proyecto_composer/web/modules/contrib/admin_toolbar/

 

Más información sobre Coder.

 

Devel

[Contrib] Devel: Funcionalidades para dsarrolladores

Incluye cuatro módulos muy interesantes orientados al desarrollo de módulos propios.

  • Devel. Añade varias funcionalidades para desarrolladores.
  • Devel generate. Permite crear contenidos de prueba para utilizardurante el desarrollo del sitio.
  • Devel Kint. Integra Kint en Devel para depuración.
  • Web Profiler. Añade una barra inferior con información de depuración de cada página cargada.

Podemos configurar las opciones en:

  • Configuración -> Desarrollo -> Opciones de desarrollo.

 

En el Diseño de Bloques podemos incluir el bloque Development en el pie de página que incluirá una serie de enlaces interesantes durante el desarrollo.

Si tenemos instalado Admin Toolbar nos creará un menú Desarrollo también con todas las opciones disponibles.

El módulo nos permite utilizar una serie de funciones de depuración nuevas durante el desarrollo del módulo (más información).

Posee funciones de generación de contenido aleatorio.

E incluye una poderosa barra de herramientas en la parte inferior de la página con toda muchísima información de depuración.

 

Registro y errores

Por defecto, Drupal oculta los errores PHP. Para activarlos en nuestro sitio de desarrollo debemos activarlos en:

  • Configuración -> Desarrollo -> Registro y errores.

 

Examples for Developers

[Contrib] Examples for Developers

Es un módulo que incluye numerosos módulos de ejemplo expresamente para desarrolladores. No es necesario activarlo, su fin es simplemente leer su código.

 

Definición del módulo

Comenzaremos definiendo el nombre del módulo real y el nombre de módulo máquina (más información).

Continuaremos generando la estructura básica del nuevo módulo mediante el comando drupal, el cual nos irá preguntando la información mínima necesaria para ello:

drupal generate:module

 // Welcome to the Drupal module generator

 Enter the new module name:
 > TicArte

 Enter the module machine name [ticarte]:
 > 

 Enter the module Path [modules/custom]:
 > 

 Enter module description [My Awesome Module]:
 > Example custom module

 Enter package name [Custom]:
 > TicArte         

 Enter Drupal Core version [8.x]:
 > 

 Do you want to generate a .module file? (yes/no) [yes]:
 > 

 Define module as feature (yes/no) [no]:
 > 

 Do you want to add a composer.json file to your module? (yes/no) [yes]:
 > no

 Would you like to add module dependencies? (yes/no) [no]:
 > yes

 Module dependencies separated by commas (i.e. context, panels):
 > ctools, token

 Do you want to generate a unit test class? (yes/no) [yes]:
 > 

 Do you want to generate a themeable template? (yes/no) [yes]:
 > 

 Do you want proceed with the operation? (yes/no) [yes]:
 > 

Generated or updated files
 Generation path: /opt/lampp/htdocs/drupal8/web
 1 - /modules/custom/ticarte/ticarte.info.yml
 2 - /modules/custom/ticarte/ticarte.module
 3 - /modules/custom/ticarte/tests/src/Functional/LoadTest.php
 4 - /modules/custom/ticarte/ticarte.module
 5 - /modules/custom/ticarte/templates/ticarte.html.twig

                                                                                
 Generated lines: 90   

 

Fichero .info.yml

Define la información general del módulo: nombre, descripción, versión, dependencias, etc. Único fichero obligatorio de la estructura.

Más información

 

Fichero .module

Define los hooks del sistema, que son estructuras que modifican el mismo y que continuan su funcionamiento desde Drupal 7 y aún no han evolucionado, por ejemplo, la ayuda del módulo, la definición de plantillas Twig, etc.

 

Instalación y desinstalación del módulo

Para instalar y desinstalar el módulo podemos hacerlo en un sólo paso gracias al módulo Devel:

  • Development -> Reinstalar módulos

 

Desde línea de comandos de la siguiente manera:

drush pm:enable ticarte

drush pm:uninstall ticarte

 

Reconstrucción de las cachés

Ante cualquier cambio de código que hagamos podremos necesitar reconstruir las cachés y que se vuelva a cargar el nuevo código:

drush cache:rebuild

 

Definición del controlador y su ruta

Como en Symfony, Drupal utiliza Controladores para gestionar la información de presentación, al cual se le asocia una ruta por la que se accede y, de manera opcional, una plantilla que renderice los resultados.

drupal generate:controller

 // Welcome to the Drupal Controller generator
 Enter the module name [admin_toolbar]:
 > ticarte

 Enter the Controller class name [DefaultController]:
 > HelloController

 Enter the Controller method title (to stop adding more methods, leave this empty) []:
 > Hello TicArte

 Enter the action method name [hello]:
 > hello

 Enter the route path [/ticarte/hello/{name}]:
 > 

 Enter the Controller method title (to stop adding more methods, leave this empty) []:
 > 

 Do you want to generate a unit test class? (yes/no) [yes]:
 > no

 Do you want to load services from the container? (yes/no) [no]:
 > 

 Do you want proceed with the operation? (yes/no) [yes]:
 > 


 // router:rebuild

 Rebuilding routes, wait a moment please

                                                                                
 [OK] Done rebuilding route(s).                                                 
                                                                                

Generated or updated files
 Generation path: /opt/lampp/htdocs/drupal8/web
 1 - modules/custom/ticarte/src/Controller/HelloController.php
 2 - modules/custom/ticarte/ticarte.routing.yml

                                                                                
 Generated lines: 33                                                            
                                                                                

El controlador se creará dentro de la carpeta src/Controller.

Más información sobre controladores.

El controlador puede devolver directamente la salida formateándola:

return [
      '#type' => 'markup',
      '#markup' => $this->t('Hello, World!'),
    ];

 

La ruta se creará dentro del archivo ticarte.routing.yml.

Más información sobre rutas.

 

Renderización de una plantilla Twig desde un controlador

Un controlador también puede renderizar una plantilla Twig, para ello necesitamos codificar los siguientes pasos.

Más información sobre renderización de plantillas Twig

 

a) Definir hook_theme que indique el nombre de la plantilla, en nuestro caso ticarte (/ticarte.module).

function ticarte_theme($existing, $type, $theme, $path) {
  return [
    'ticarte' => [
      'render element' => 'children',
      'template' => 'ticarte',
      'variables' => [
        'node_title' => NULL,
      ],
    ],
  ];
}

 

b) Llamar a la plantilla desde el controlador (/src/Controller/HelloController.php).

 return [
      '#theme' => 'ticarte',
      '#node_title' => $this->t('Value'),
    ];

 

c) Crear la plantilla Twig, que debe llamarse al igual que la hemos definido anteriormente (/templates/ticarte.twig.html).

<p>Test twig template!</p>
<p>Title: {{ node_title }}</p>

 

d) Reconstruir la caché.

 

Definición de formularios de configuración

Los modulos poseen configuración que se puede modificar desde formularios que normalmente están en la ruta /admin/config/. A continuación se desarrolla un ejemplo en el que el formulario posee un campo del tipo select con dos opciones.

drupal generate:form:config

 // Welcome to the Drupal Form Config generator Enter the module name [admin_toolbar]: 
> ticarte

 Enter the Form Class name [DefaultForm]: 
> SettingsForm

 Enter the Form id [settings_form]: 
> ticarte_settings_form

 Do you want to load services from the container? (yes/no) [no]: 
> no

 Do you want to generate a config file? (yes/no) [yes]: 
> yes

 Do you want to generate a form structure? (yes/no) [yes]: 
> yes

Available types: button, checkbox, checkboxes, color, date, datelist, datetime, email, entity_autocomplete, field_ui_table, fieldset, file, hidden, image_button, item, language_configuration, language_select, machine_name, managed_file, number, password, password_confirm, path, radio, radios, range, search, select, submit, table, tableselect, tel, text_format, textarea, textfield, token, token_tree_table, url, value, weight

 Enter a new field properties

 New field type (press <return> to stop adding fields) []: 
> select

 Input label: 
> Source

 Input machine name [source]: 
> source

 Size of multiselect box (in lines) [5]: 
> 1

 Input options separated by comma: 
> local, server

 Description []: 
> Source data

 Default value []: 
> local

 Weight for input item [0]: 
> 0

 Enter a new field properties

 New field type (press <return> to stop adding fields) []: 
>

 Enter the route path [/admin/config/ticarte/settings]: 
> /admin/config/ticarte/settings

 Generate a menu link (yes/no) [yes]: 
> yes

 A title for the menu link [SettingsForm]: 
> TicArte

 Menu parent [system.admin_config_system]: 
> system.admin_config_system

 A description for the menu link [A description for the menu entry]: 
> Ticarte settings    

 // router:rebuild

 Rebuilding routes, wait a moment please

[OK] Done rebuilding route(s).                                                                                                                                                                                                                 

Generated or updated files 
 Generation path: /opt/lampp/htdocs/drupal8/web 
 1 - modules/custom/ticarte/ticarte.routing.yml 
 2 - modules/custom/ticarte/src/Form/SettingsForm.php 
 3 - modules/custom/ticarte/config/install/ticarte.settings.yml 
 4 - modules/custom/ticarte/ticarte.links.menu.yml

Generated lines: 73                                                                                                                                   

 

A continuación se entra en detalle de los ficheros generados.

a) Definir el formulario de configuración (/src/Form/SettingsForm.php).

La función getEditableConfigNames() define la clave donde se almacenarán los valores de configuración

La función buildForm() define los campos del formulario y recupera el valor almacenado de cada campo (más información sobre el elemento Form y sobre los elementos  inputs del Form).

La función submitForm() almacena los valores del formulario.

 

b) Definir los valores por defecto que se cargarán en el formulario la primera vez que se instale el módulo (/config/install/ticarte.settings.yml). Deben aparecer diretactamente los nombre de los identificadores de los campos creados anteriormente. El fichero generado por Drupal console puede que nos lo haya incluido en otro valor superior, por lo que tenemos que repasarlo y modificarlo.

source: "local"

 

c) Definir la ruta de acceso al formulario, el título y sus permisos (/ticarte.routing.yml).

 

d) Definir el enlace del menú (/ticarte.links.menu.yml).

 

e) Acceso a las variables de configuración desde otras partes del módulo:

$source = \Drupal::config('ticarte.settings')->get('source');

 

Definición de tipos de campos

Las entidades permiten tener asociados campos en los que almacenar información su información. Los campos están divididos en tres secciones:

  • FieldType: Define la información que almacena el campo y la configuración previa del campo.
  • WidgetType: Define el formulario de introducción de datos del campo.
  • FormatterType: Define la presentación de los datos de campo.

Un campo puede tener varios WidgetType y FormatterType.

 

A continuación vamos a crear un tipo de campo para almacenar el Color de una entidad.

drupal generate:plugin:field

 // Welcome to the Drupal Field Plugin generator Enter the module name [admin_toolbar]: 
> ticarte

 Enter field type plugin class name [ExampleFieldType]: 
> ColorFieldType

 Enter the field type plugin label [Color field type]: 
> Color

 Enter the field type plugin id [color_field_type]: 
> color_field_type

 Enter the field type plugin description [My Field Type]: 
> Color field

 Enter the field widget plugin class name [ExampleWidgetType]: 
> ColorWidgetType

 Enter the field widget plugin label [Color widget type]: 
> Color

 Enter the field widget plugin id [color_widget_type]: 
> color_widget_type

 Enter the field formatter plugin class name [ExampleFormatterType]: 
> ColorFormatterType

 Enter the field formatter plugin label [Color formatter type]: 
> Color

 Enter the field formatter plugin id [color_formatter_type]: 
> color_formatter_type

 Enter the field type the formatter and widget plugin can be used with [color_field_type]: 
> color_field_type

 Enter the default field widget of the field type plugin [color_widget_type]: 
> color_widget_type

 Enter the default field formatter of field type plugin [color_formatter_type]: 
> color_formatter_type

 Do you want proceed with the operation? (yes/no) [yes]: 
> yes

 // generate:plugin:fieldtype
 // generate:plugin:fieldwidget
 // generate:plugin:fieldformatter
 // cache:rebuild

 Rebuilding cache(s), wait a moment please.

[OK] Done clearing cache(s).                                                                                                                                                                                                                   

Generated or updated files
 Generation path: /opt/lampp/htdocs/drupal8/web
 1 - modules/custom/ticarte/src/Plugin/Field/FieldType/ColorFieldType.php
 2 - modules/custom/ticarte/src/Plugin/Field/FieldWidget/ColorWidgetType.php
 3 - modules/custom/ticarte/ticarte.schema.yml
 4 - modules/custom/ticarte/src/Plugin/Field/FieldFormatter/ColorFormatterType.php

Generated lines: 302                                                                                                                                                                                                                           

 

A continuación se entra en detalle de los ficheros generados.

a) Definir la configuración de almacenamiento del campo (/src/Plugin/Field/FieldType/ColorFieldType.php).

La función propertyDefinitions() define los datos que va a almacenar el campo.

La función schema() define cómo los datos que va a almacenar el campo se almacenan en la base de datos.

La función getConstraints() aplica restricciones a los datos que se van a almacenar en la base de datos.

La función isEmpty() define cuándo se considera vacío dicho campo.

Si el campo lleva asociado algún valor de configuración al crearlo, debemos utilizar las funciones defaultStorageSettings() y storageSettingsForm() para definir y almacenar dichas configuraciones.

 

b) Definir la configuración por defecto del formulario de configuración del campo (/ticarte.schema.yml).

Sólo es necesario en caso de que el campo lleve asociado algún valor de configuración al crearlo.

 

c) Definir el fomulario de introducción de datos del campo (/src/Plugin/Field/FieldWidget/ColorWidgetType.php).

La función formElement() define el fomulario de entrada de datos al campo.

La función settingsForm() define el formulario de configuración de la entrada de datos al campo.

La función settingsSummary() define el resumen que se muestra al usuario con respecto al formulario de configuración de la entrada de datos al campo.

 

d) Definir la visualización de datos del campo (/src/Plugin/Field/FieldFormatter/ColorFormatterType.php).

La función viewElements() define la visualización de datos del campo.

La función settingsForm() define el formulario de configuración de la visualización de datos del campo.

La función settingsSummary() define el resumen que se muestra al usuario con respecto al formulario de configuración de la visualización de datos del campo.

 

Acceso a bases de datos

Podemos utilizar la API de Drupal para acceder tanto a la propia base de datos como a base de datos externas.

Más información sobre API Database

 

Para realizar una consulta puntual y estática se recomienda seguir los siguientes pasos (más información):

$database = \Drupal::database();
$query = $database->query("SELECT id, example FROM {mytable} WHERE created > :created", [ ':created' => REQUEST_TIME - 3600,]);
$result = $query->fetchAll();

 

Publicación de tu módulo en Drupal.org

Para contribuir con la comunidad de Drupal, puedes publicar tu módulo y compartirlo para que se beneficien el resto de usuarios de Drupal.

Prácticas para crear y mantener proyectos en Drupal.org