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.