
Las migraciones en Drupal, aunque sus definiciones sean lineales, contienen mucha complejidad heredada. La razón es intuitiva: Migrate API ofrece una interfaz de interacción relativamente sencilla a quien desarrolla migraciones, pero durante el proceso colaboran numerosos subsistemas: entidades, base de datos, plugins… Incluso en la migración más sencilla intervienen muchas clases. Si añadimos que una migración suele producir errores hasta que la afinamos, está claro que una de nuestras primeras necesidades será aprender a depurarlas.
Foto de Unsplash, de Krzysztof Niewolny, @epan5
Índice
Siguiendo los últimos artículos, quiero ampliar la información sobre migraciones en Drupal. He pensado en una pequeña subserie sobre depuración dentro de esta serie principal. Esta es la primera parte: un conjunto de pasos básicos para obtener toda la información disponible de un proceso de migración. Los ejemplos proceden del módulo migration_google_sheet de mi cuenta de GitLab.
Empezaremos por una aproximación muy elemental a la detección de errores. Es fundamental reducir las posibles causas abordando la migración de forma iterativa e incremental: avanzaremos paso a paso y ampliaremos progresivamente los datos migrados.
Comenzamos con el paso más intuitivo. Hay errores que están a la vista y, por habituales que sean, no siempre resultan evidentes.
Antes de nada, revisaremos dos cuestiones que aparecen con frecuencia:
Espacios en blanco: un espacio adicional puede causar problemas en el archivo de definición. Recorre sus líneas para localizar espacios sobrantes.
Errores de sangrado: el formato YAML organiza los datos mediante claves y valores, con niveles padre e hijo y sangrado de dos espacios en cada nivel del ejemplo. Un sangrado incorrecto puede provocar un error al procesar el archivo. Revisa todos los niveles, igual que has revisado los espacios.
Puedes apoyarte en un validador de sintaxis YAML como www.yamllint.com, aunque también tendrás que comprobar el resultado.
En la instalación básica utilizada en el ejemplo, con el perfil estándar, tenemos setenta y tres tablas. Tras activar migrate, migrate_plus, migrate_tools y el módulo personalizado migration_google_sheet_wrong, pasan a ser setenta y cinco. Se han generado dos tablas nuevas:
cache_migrate
cache_discovery_migration
Después de ejecutar la migración con identificador taxonomy_google_sheet_wrong, incluida en el módulo personalizado, aparecen otras dos tablas relacionadas con la migración:
id_hash de la fila de origen:
Podemos obtener esta información con Drush: el contenido de la tabla es lo que aparece al ejecutar:
drush migrate:messages id-migration
Es una forma útil de empezar a recopilar información sobre los errores de la migración.
Durante la depuración también necesitamos actualizar los cambios realizados en el objeto de configuración creado a partir del archivo de definición de config/install.
Cada vez que instalamos el módulo se genera un objeto de configuración disponible en Drupal. Mientras depuramos tendremos que modificar el archivo y recargarlo para comprobar el efecto de los cambios. ¿Cómo podemos facilitar este ciclo?
Primero vincularemos el ciclo de vida del objeto de configuración de migración a la instalación de nuestro módulo. Como vimos al tratar las migraciones como configuración, declararemos el módulo personalizado como dependencia forzada:
dependencies:
enforced:
module:
- migration_google_sheet
Podemos realizar importaciones específicas con Drush o Drupal Console, aprovechando las opciones de importación individual.
Con Drupal Console:
drupal config:import:single --directory="/modules/custom/migration_google_sheet/config/install" --file="migrate_plus.migration.taxonomy_google_sheet.yml"
Con Drush:
drush cim --partial --source=/folder/
También podemos eliminar objetos de la configuración activa con ambas herramientas:
drush config-delete "migrate_plus.migration.taxonomy_google_sheet"
drupal config:delete active "migrate_plus.migration.taxonomy_google_sheet"
Si preferimos la interfaz de Drupal, el módulo contribuido Config Delete incorpora opciones al menú de sincronización para eliminar elementos de configuración. Basta con descargarlo con Composer y activarlo con Drush o Drupal Console:
composer require drupal/config_delete
drush en config_delete -y

Así podemos volver a importar objetos sin chocar con versiones ya existentes en la base de datos. Si prefieres actualizar y comparar versiones, puedes valorar Configuration Update Manager.
Hemos revisado los datos detenidamente y, aun así, la migración de términos desde Google Spreadsheet no parece funcionar.
Necesitamos técnicas intermedias para obtener más información. En esta fase nos ayudarán módulos y plugins que permiten observar mejor lo que ocurre durante la migración.
Migrate Devel es un módulo contribuido que añade funcionalidades mediante opciones nuevas de Drush. Funciona con migrate_tools y migrate_run.
Justo cuando publiqué este artículo, Andrew Macpherson, nuevo mantenedor de Migrate Devel y uno de los mantenedores de accesibilidad del core de Drupal, dejó un comentario al pie del original con novedades importantes. Desde que empecé el borrador se había publicado una versión nueva, el 28 de junio, compatible con Drush 9, ¡y no lo sabía! Esa versión evita instalar el parche que se describe más abajo.
Para instalarla, la descargamos con Composer y la activamos con Drush: Migrate Devel 8.x-2.0-alpha2.
composer require drupal/migrate_devel
# To install the 2.0 branch of the module:
composer require drupal/migrate_devel:^2.0
drush en migrate_devel -y
Si trabajas con versiones anteriores, hay algunas particularidades. Estaban preparadas para Drush 8 y la adaptación a Drush 9 y posteriores aún no parecía cerrada.
En la rama 8.x-1.x había una etiqueta publicada dos semanas antes: migrate_devel/tree/8.x-1.4.
Para utilizar esas versiones con Drush 9 o superior, el parche de esta incidencia resultaba prácticamente imprescindible. Aunque tampoco parecía definitivo, permitía ejecutar de forma controlada algunas funciones del módulo. Veremos sus aportaciones.
Podemos descargar el parche con wget y aplicarlo con git apply:
cd /web/modules/contrib/migrate_devel/
wget https://www.drupal.org/files/issues/2018-10-08/migrate_devel-drush9-2938677-6.patch
git apply migrate_devel-drush9-2938677-6.patch
Otra posibilidad es añadirlo a la sección de parches de composer.json, si tenemos activada la extensión composer-patches.
La instalamos con:
composer require cweagans/composer-patches
Y añadimos el parche a la sección extra de composer.json:

Al ejecutar una migración con las opciones de Migrate Devel obtenemos valores por consola que podemos inspeccionar. Por ejemplo, con –migrate-debug:

Es una vista parcial del procesamiento de una fila: muestra la fuente, los valores asociados y el identificador final de destino, almacenado en la tabla de correspondencias para seguir el proceso:

Vemos que al valor 1 de origen, en el primer array, se le asignó el identificador 117 en destino. También será el identificador interno de la nueva entidad de Drupal, aquí un término de taxonomía. Así relacionamos el registro de migración con la entidad creada y almacenada.
¿Y los suscriptores de eventos? Migrate Devel crea una clase que implementa EventSubscriberInterface y escucha eventos de Migrate API, disponibles en el módulo migrate del core:
Called from +56
/var/www/html/web/modules/contrib/migrate_devel/src/EventSubscriber/MigrationEventSubscriber.php
La llamada procede de la clase que escucha estos eventos. En modules/migrate/src/Event se definen muchos, pero Migrate Devel presta atención especialmente a dos:
Estos corresponden a las dos opciones Drush del módulo, que originalmente acababan llamando a dump() de Kint, proporcionado por Devel, para imprimir los mensajes. En la versión 8.x-2.0-alpha2 se sustituyen esas llamadas por Symfony VarDumper. Donde antes teníamos:
/**
* Pre Row Save Function for --migrate-debug-pre.
*
* @param \Drupal\migrate\Event\MigratePreRowSaveEvent $event
* Pre-Row-Save Migrate Event.
*/
public function debugRowPreSave(MigratePreRowSaveEvent $event) {
$row = $event->getRow();
$using_drush = function_exists('drush_get_option');
if ($using_drush && drush_get_option('migrate-debug-pre')) {
// Start with capital letter for variables since this is actually a label.
$Source = $row->getSource();
$Destination = $row->getDestination();
// We use kint directly here since we want to support variable naming.
kint_require();
\Kint::dump($Source, $Destination);
}
}
Ahora tenemos:
/**
* Pre Row Save Function for --migrate-debug-pre.
*
* @param \Drupal\migrate\Event\MigratePreRowSaveEvent $event
* Pre-Row-Save Migrate Event.
*/
public function debugRowPreSave(MigratePreRowSaveEvent $event) {
if (PHP_SAPI !== 'cli') {
return;
}
$row = $event->getRow();
if (in_array('migrate-debug-pre', \Drush\Drush::config()->get('runtime.options'))) {
// Start with capital letter for variables since this is actually a label.
$Source = $row->getSource();
$Destination = $row->getDestination();
// Uses Symfony VarDumper.
// @todo Explore advanced usage of CLI dumper class for nicer output.
// https://www.drupal.org/project/migrate_devel/issues/3151276
dump(
'---------------------------------------------------------------------',
'| $Source |',
'---------------------------------------------------------------------',
$Source,
'---------------------------------------------------------------------',
'| $Destination |',
'---------------------------------------------------------------------',
$Destination
);
}
}
Puedes consultar los cambios en MigrationEventSubscriber.php de 8.x-2.0-alpha2.
También encontrarás más información sobre eventos y suscriptores en el artículo de The Russian Lullaby Building Symfony events for Drupal.
Migrate Devel también incorpora un plugin de proceso llamado debug, definido en Debug.php. Lo encontrarás en /web/modules/contrib/migrate_devel/src/Plugin/migrate/process/Debug.php. Su documentación explica su responsabilidad:
/**
* Debug the process pipeline.
*
* Prints the input value, assuming that you are running the migration from the
* command line, and sends it to the next step in the pipeline unaltered.
*
* Available configuration keys:
* - label: (optional) a string to print before the debug output. Include any
* trailing punctuation or space characters.
* - multiple: (optional) set to TRUE to ask the next step in the process
* pipeline to process array values individually, like the multiple_values
* plugin from the Migrate Plus module.
El método transform(), heredado de la clase abstracta ProcessPluginBase, no transforma el valor: utiliza print_r de PHP para mostrar por consola tanto valores escalares como arrays.
Podemos incluir el plugin de forma autónoma en la cadena de procesamiento para que imprima resultados al procesar cada fila. En nuestro caso modificaremos el bloque process de la migración de términos para revisar los valores que se están migrando.
Primero cambiaremos la estructura. Como sabemos por las entregas anteriores, esta forma:
process:
name: name
description: description
path: url
status: published
Utiliza implícitamente el plugin Get.php y equivale a esta otra:
process:
name:
plugin: get
source: name
description:
plugin: get
source: description
path:
plugin: get
source: url
status:
plugin: get
source: published
Añadimos Debug a la cadena con una etiqueta informativa para cada campo:
process:
name:
plugin: debug
label: 'Processing name field value: '
plugin: get
source: name
description:
plugin: debug
label: 'Processing description field value: '
plugin: get
source: description
path:
plugin: debug
label: 'Processing path field value: '
plugin: get
source: url
status:
plugin: debug
label: 'Processing status field value: '
plugin: get
source: published
Después recargamos el objeto de configuración desinstalando e instalando nuestro módulo. Como figura como dependencia, al desinstalarlo también se elimina la configuración de la migración:
drush pmu migration_google_sheet && drush en migration_google_sheet -y
Al ejecutarla de nuevo aparecerá información sobre los valores:

Así obtenemos información más detallada de los datos que vamos a migrar. Para escenarios más avanzados podemos combinar argumentos y opciones. Por ejemplo, si queremos revisar un solo elemento:
drush migrate-import --migrate-debug taxonomy_google_sheet --limit="1 items"
Esto combina la salida posterior al almacenamiento —a diferencia de --migrate-debug-pre— con la información del plugin, los valores mostrados por la herramienta de volcado y el identificador final de la única entidad procesada.
Aquí los valores son sencillos y apenas los transformamos: los extraemos y los cargamos en destino. En migraciones posteriores introduciremos tratamientos más complejos y esta información resultará aún más valiosa. Piensa en valores que hay que concatenar, recortar, ampliar o adaptar: si mostramos el resultado en cada paso podremos observar mejor toda la secuencia de transformación.
Puedes revisar el código de Debug.php y la incidencia de Drupal.org en la que surgió la propuesta de este plugin.
Con esta aproximación empezamos la subserie de depuración de migraciones. ¡Pronto, más experiencias!
Recibe artículos sobre Drupal y desarrollo web directamente en tu bandeja de entrada.
Comments
Los comentarios los proporciona Disqus. Solo se cargan si permites la categoría de comentarios en tus preferencias de cookies.