Com migrar contingut a Drupal 8 des d’un csv

Client
  • Omitsis
Technologies
Date
  • 15/09/2020

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_plusmigrate_toolsmigrate_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.

JU

julia

manager

Recent Posts