Com migrar fitxers i imatges amb Drupal 8 a un camp mitjana

Client
  • Omitsis
Technologies
Date
  • 15/09/2020

En migrar a Drupal 8 contingut des d’una altra font és habitual que alguns camps siguin imatges o fitxers. Aquí veurem com importar-los a un camp media.

Ja des de fa un temps el mòdul media es va incorporar al core i l’han anat millorant perquè ara ja sigui més útil posar els fitxers i imatges en aquest tipus de camps, en lloc d’un camp image o file.

El mòdul media permet, entre altres coses, reutilitzar els continguts, tenir una biblioteca multimèdia i afegir els camps que necessitis. És quasi un estàndard per als projectes en Drupal que realitzem a Omitsis.

Però a l’hora d’importar, de moment és més complicat que fer-ho directament a un camp de tipus file. Aquí veurem com fer-ho.

Aquest post és una continuació d’un article anterior on veiérem com usar el mòdul migrate per importar des d’un csv. És recomanable llegir-se primer aquest article si no tens experiència amb el mòdul migrate.

Hi ha un mòdul que permet migrar entitats file a entitats media, el migrate files to media. També permet importar-los des de Drupal 7. En aquest cas no l’usarem, usarem només el mòdul migrate i els que comentem al post anterior.

Primer instal·lem aquests mòduls amb composer:

composer require drupal/migrate_plus drupal/migrate_tools drupal/migrate_source_csv drupal/config_devel

I els habilitem:

drush -y en migrate migrate_plus migrate_tools migrate_source_csv config_devel

Tots aquests mòduls estan a Drupal 8 i Drupal 9, excepte el config_devel que encara no està per a la 9, però sembla que estarà en breu. Així que tot el que explicarem servirà també per a Drupal 9.

Crearem un yml primer per importar les imatges a files. Aquest fitxer l’anomenarem migrate_plus.migration.migrate_files.yml.

id: migrate_files
label: Migrate Files
source:
  plugin: 'csv'
  # Full path to the file.
  path: 'modules/custom/custom_migrate/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'
  constants:
    file_source_uri: 'public://import/source/images'
    file_dest_uri: 'public://imports/dest/images'
process:
  file_source:
    -
      plugin: concat
      delimiter: /
      source:
        - constants/file_source_uri
        - image
    -
      plugin: urlencode

  file_dest:
    -
      plugin: concat
      delimiter: /
      source:
        - constants/file_dest_uri
        - image
    -
      plugin: urlencode

  # We use the image file names as is.
  #
  # Alternatively, if we wish to name them after some other
  # column, we can do it here.
  uri:
    plugin: file_copy
    source:
      - '@file_source'
      - '@file_dest'

  uid:
    plugin: default_value
    default_value: 1

destination:
  plugin: entity:file

migration_dependencies: {}

La primera part d’aquest fitxer és quasi idèntica a la que vam usar per migrar el contingut, ja que usarem la mateixa font, un csv.

Els punts destacats a ressaltar:

  • Ús de constants per indicar on estaran les imatges i on les posarem.
  • En process usarem dues entrades per definir el source i el destination. Com pots veure és simplement concatenar el directori d’origen/destinació amb el camp del csv imatge que té només el nom de la imatge.
  • En process en el camp uri de la imatge usem els valors anteriors amb el plugin file_copy. Aquest plugin té més opcions per definir-li què fer si ja existeix el fitxer. Les pots veure al seu codi font.

El següent pas és usar aquests files per crear les entitats media. Al fitxer l’anomenarem migrate_plus.migration.media_images

id: media_images
label: Media images
source:
  plugin: 'csv'
  # Full path to the file.
  path: 'modules/custom/custom_migrate/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:
  # Skip if already existent
  skip:
    -
      plugin: entity_lookup
      value_key: name
      source: image
      bundle_key: bundle
      bundle: image
      entity_type: media
      ignore_case: 1
      access_check: 0
    -
      plugin: skip_on_not_empty
      method: row
      message: 'Skipping already existent node'

  field_media_image/target_id:
    plugin: migration_lookup
    migration: calier_files
    source: code
    no_stub: true
  uid:
    plugin: default_value
    default_value: 1

destination:
  plugin: entity:media
  default_bundle: image

migration_dependencies:
  optional:
    - migrate_files

A destacar:

  • La part inicial és la mateixa
  • El skip és per no crear un media per a cada entrada al csv si el fitxer ja existeix, ja que el que volem amb el media és reutilitzar les imatges.
  • Per a això usarem el plugin cutom skip_on_not_empty que vaig copiar d’aquí.
  • El que fa el skip és usar el plugin entity_lookup per buscar una entitat media que el seu nom sigui el nom del fitxer. Si la troba amb el plugin skip_on_not_empty aconseguirem que no la importi i així evitar duplicats.
  • Després amb el migration_lookup li indicarem el fid del fitxer que vam importar anteriorment.
  • Només falta indicar un destination que serà un entity_media. El bundle es podria posar en el process si fos canviant per cada fila però en aquest cas són sempre imatges, per la qual cosa podem definir un default_bundle directament.
  • Finalment indiquem que aquesta migració depèn de l’anterior. Està posada com «optional» perquè amb required donava problemes. Hi ha un issue sobre això que s’està resolent.

I ara només queda la importació del contingut en si, fent referència a les entitats media creades.

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:
  optional:
    - migrate_files
    - media_images

A destacar:

  • Per referenciar les entitats media usem el plugin entity_lookup que busca pel nom de la imatge.
  • Tot l’altre ja ho vam explicar al post anterior.

Indiquem aquests fitxers en el yml del mòdul:

config_devel:
  install:
    - migrate_plus.migration.products
    - migrate_plus.migration.migrate_files    
    - migrate_plus.migration.media_images

I ja només queden uns comandaments:

Primer dir-li a Drupal que tingui en compte els ymls d’importació creats.

drush cdi custom_migrate

I després les importacions de cadascun dels fitxers:

drush mim migrate_files
drush mim media_images
drush mim products

I amb tot això ja tindríem a punt la importació d’imatges a entitats media i després creat els nodes que contenen aquests camps media.

JU

julia

manager

Recent Posts