Quan encarreguen una web en Drupal a Omitsis és molt habitual que sigui una web ja existent i que per tant tingui un contingut que vulguin migrar.
A Drupal existeix el mòdul migrate que està incorporat al core i que ens permet migrar contingut. És un mòdul molt potent, versàtil i que facilita realitzar rollbacks de sèrie. Els rollbacks ens permeten «desimportar», molt útil per quan encara estem desenvolupant i hi ha errors.
El mòdul migrate permet importar des de diferents fonts: un Drupal 7, una base de dades tingui l’estructura que sigui, des d’un csv, un JSON, XML, SOAP, etc.
És molt comú que ens passin un excel amb tots els camps a migrar per cada tipus de contingut. En aquest post anem a explicar com realitzar aquest tipus de migració: des d’un csv, ja que totes les fulls de càlcul (excel per exemple) permeten guardar com a csv i aquest és un format estàndard millor suportat.
Tot el que anem a explicar és vàlid per a Drupal 8 i per a Drupal 9, ja que tots els mòduls estan disponibles per a ambdues versions excepte el config_devel que sembla que estarà properament i no és un mòdul imprescindible.
El primer és instal·lar els mòduls que seran necessaris. El migrate ja està al core de Drupal però necessitarem alguns més: migrate_plus, migrate_tools, migrate_source_csv i config_devel.
Instal·lem aquests mòduls usant composer:
composer require drupal/migrate_plus drupal/migrate_tools drupal/migrate_source_csv drupal/config_devel
I els activem, amb drush seria així:
drush -y en migrate migrate_plus migrate_tools migrate_source_csv config_devel
Ara hem de crear els fitxers yml que definiran les migracions. Això ho hem de fer dins d’un mòdul custom, que podem tenir ja o podem crear un específic per al migrate (opció més neta).
Pots generar un mòdul de forma fàcil amb la consola, en el nostre cas l’anomenarem custom_migrate.
drupal gm
En el directori d’aquest mòdul creem una subcarpeta anomenada config i dins d’aquesta una altra anomenada install. Allà posarem els fitxers yml’s que definiran les migracions.
La nomenclatura d’aquests fitxers ha de ser:
migrate_plus.migration.[ID_MIGRACIO].yml
Per exemple, si volem importar productes el nom del fitxer podria ser aquest:
migrate_plus.migration.products.yml
En aquest exemple l’arxiu estaria en la següent ruta:
web/modules/custom/custom_migrate/config/install/migrate_plus.migration.products.yml
El mòdul migrate_plus ens permet també crear grups, per després fer les importacions de cop (i els rollbacks). Pots veure en aquest post com fer-ho. En aquest exemple no els usarem, però és bastant pràctic.
Ara cal dir-li a Drupal que ha de tenir en compte el fitxer d’importació que vam definir. Això es posa en el .info.yml del nostre mòdul de la manera següent:
config_devel:
install:
- migrate_plus.migration.products
L’arxiu sencer seria així:
name: 'Custom migrate'
type: module
description: 'Mòdul d'importacions de contingut'
core: 8.x
package: 'Custom'
dependencies:
- migrate
- migrate_plus
- migrate_tools
- config_devel
config_devel:
install:
- migrate_plus.migration.products
# - migrate_plus.migration.altra_importacio
Això ho podem fer gràcies al mòdul config_devel, ja que sense aquest mòdul només es posaria aquesta configuració quan instal·lem el mòdul, i no és gens pràctic haver d’estar instal·lant i desinstal·lant.
Ara ens falta el més important: el contingut del yml d’importació. Això donaria per a molts posts, així que posarem un exemple senzill:
Primer és necessari definir l’id i un label anirà bé per posar-li un nom bonic.
id: products
label: Import products
Després va la secció del source. Aquí cal dir-li quin plugin usarem i per al cas del csv un path on estarà el csv. En aquest cas l’hem posat dins del mòdul per poder-lo integrar al nostre git.
També li hem indicat els ids únics, que en el nostre csv s’anomenava «code».
I finalment indiquem els camps del csv.
source:
plugin: 'csv'
# Full path to the file.
path: 'modules/custom/custom_migration/data/products.csv'
# Column delimiter. Comma (,) by default.
delimiter: ','
# Field enclosure. Double quotation marks (") by default.
enclosure: '"'
# The row to be used as the CSV header (indexed from 0),
# or null if there is no header row.
header_offset: 0
# The column(s) to use as a key. Each column specified will
# create an index in the migration table and too many columns
# may throw an index size error.
ids:
- code
# Here we identify the columns of interest in the source file.
# Each numeric key is the 0-based index of the column.
# For each column, the key below is the field name assigned to
# the data on import, to be used in field mappings below.
# The label value is a user-friendly string for display by the
# migration UI.
fields:
0:
name: cat
label: 'Categoria'
1:
name: code
label: 'Product code'
2:
name: title
label: 'Title'
3:
name: image
label: 'Image'
Ara la part del process, on indiquem els camps a on cal posar les dades, el plugin que anem a usar i la font (source).
Si és una cosa molt simple podria ser tan senzill com això:
field_nom_camp_al_nostre_drupal: nom_columna_source
Però moltes vegades no és tan simple. Per exemple ens passen el títol tot en majúscules, així que combinem diversos plugins. També veuràs la part d’importació a un camp media. Això ho explicarem en un altre post en breu.
Un cas real podria ser aquest:
process:
type:
plugin: default_value
default_value: producto
field_cat:
plugin: entity_lookup
source: division
value_key: name
bundle_key: vid
bundle: cat_producto
entity_type: taxonomy_term
ignore_case: true
field_producto_codigo: code
title:
-
source: title
plugin: callback
callable: mb_strtolower
-
plugin: callback
callable: ucfirst
field_imagen/target_id:
plugin: entity_lookup
value_key: name
source: image
bundle_key: bundle
bundle: image
entity_type: media
ignore_case: 1
access_check: 0
Hi ha molts plugins disponibles, els pots veure aquí. A més podem crear plugins de forma molt fàcil.
Ja només falta indicar a on anirà tot aquest contingut amb destination. dependrà de l’entitat que estem important, en aquest cas són nodes.
destination:
plugin: entity:node
I aquí tot sencer:
id: products
label: Import products
source:
plugin: 'csv'
# Full path to the file.
path: 'modules/custom/custom_migration/data/products.csv'
# Column delimiter. Comma (,) by default.
delimiter: ','
# Field enclosure. Double quotation marks (") by default.
enclosure: '"'
# The row to be used as the CSV header (indexed from 0),
# or null if there is no header row.
header_offset: 0
# The column(s) to use as a key. Each column specified will
# create an index in the migration table and too many columns
# may throw an index size error.
ids:
- code
# Here we identify the columns of interest in the source file.
# Each numeric key is the 0-based index of the column.
# For each column, the key below is the field name assigned to
# the data on import, to be used in field mappings below.
# The label value is a user-friendly string for display by the
# migration UI.
fields:
0:
name: cat
label: 'Categoria'
1:
name: code
label: 'Product code'
2:
name: title
label: 'Title'
3:
name: image
label: 'Image'
process:
field_cat:
plugin: entity_lookup
source: division
value_key: name
bundle_key: vid
bundle: cat_producto
entity_type: taxonomy_term
ignore_case: true
field_producto_codigo: code
title:
-
source: title
plugin: callback
callable: mb_strtolower
-
plugin: callback
callable: ucfirst
type:
plugin: default_value
default_value: producto
field_imagen/target_id:
plugin: entity_lookup
value_key: name
source: image
bundle_key: bundle
bundle: image
entity_type: media
ignore_case: 1
access_check: 0
destination:
plugin: entity:node
migration_dependencies: {}
Ara finalment només ens queda executar les ordres des de drush per realitzar la importació. També es pot fer des d’UI a admin/structure/migrate.
El primer és dir-li a drupal que carregui tots els arxius d’importació amb el mòdul config_devel:
drush cdi [nom_del_modul]
Que en el nostre cas seria:
drush cdi custom_migrate
Després podem veure les migracions que tenim i el seu estat amb:
drush ms
Per realitzar la importació:
drush mim [id_importacio]
Que en el nostre cas seria:
drush mim products
Si alguna cosa surt malament, que el normal és que passi, podem fer un rollback amb aquesta ordre:
drush mr products
També és possible que la migració s’encalli, amb la qual cosa primer abans de fer el rollback cal posar-la en idle:
drush mrs products
I això és més o menys tot, com a introducció a una migració amb Drupal des d’un csv usant el mòdul migrate.