Migración - De Field Collection D7 a Paragraphs a Node D8

Publicado por Ada Hernández el 28 de Septiembre de 2017

Si usted necesita migrar campos field_collection de D7 a un nuevo sitio en D8, puede echar un vistazo a este blog y espero le sea de utilidad.

Antes de continuar dejaré este enlace:

[META] Migrate support for importing field collections as paragraphs:  Parent Issue

Éste issue y sus hijos fueron creados en la comunidad de Drupal para que el módulo Paragraphs pueda soportar las migraciones de Field Colecction y generar sus plantillas cuando hagamos migrate-upgrade.

Ahora mientras se resuelven estos issues,  le mostraré cómo hemos hecho nuestras migraciones ahora.

Como no tenemos una plantilla aún para generar los paragraphs  de field_collections en D7, entonces debemos hacer todo esto manual.

Mi ejemplo consiste en crear un paragraph llamado Contact con 3 campos luego migrarlo a un nodo llamado Organization:

Campos Contact: Email (Email), Phone(Telephone number), Website(Link)

Campo Organization: Contact (Entity reference revisions).

En un custom módulo (en nuestro caso migrate_drupal): en src/Plugin/migrate/source nos creamos una clase llamada FieldColection.php con los siguientes datos:

<?php

namespace Drupal\custom_migrate\Plugin\migrate\source;

use Drupal\migrate\Row;
use Drupal\migrate_drupal\Plugin\migrate\source\d7\FieldableEntity;

/**
 * d7_field_collection_item source.
 *
 * @MigrateSource(
 *   id = "d7_field_collection_item"
 * )
 */
class FieldCollection extends FieldableEntity {

  /**
   * {@inheritdoc}
   */
  public function query() {
    // Select node in its last revision.
    $query = $this->select('field_collection_item', 'fci')
      ->fields('fci', [
        'item_id',
        'field_name',
        'revision_id'
      ]);
    if (isset($this->configuration['field_name'])) {
      $query->innerJoin('field_data_' . $this->configuration['field_name'], 'fd', 'fd.' . $this->configuration['field_name'] . '_value = fci.item_id');
      $query->fields('fd', ['entity_type', 'bundle', 'entity_id', $this->configuration['field_name'] . '_revision_id']);
      $query->condition('fci.field_name', $this->configuration['field_name']);
    }
    return $query;
  }

  /**
   * {@inheritdoc}
   */
  public function prepareRow(Row $row) {
    // If field specified, get field revision ID so there aren't issues mapping.
    if(isset($this->configuration['field_name'])) {
      $row->setSourceProperty('revision_id', $row->getSourceProperty($this->configuration['field_name'] . '_revision_id'));
    }

    // Get field API field values.
    foreach (array_keys($this->getFields('field_collection_item', $row->getSourceProperty('field_name'))) as $field) {
      $item_id = $row->getSourceProperty('item_id');
      $revision_id = $row->getSourceProperty('revision_id');
      $row->setSourceProperty($field, $this->getFieldValues('field_collection_item', $field, $item_id, $revision_id));
    }
    return parent::prepareRow($row);
  }

  /**
   * {@inheritdoc}
   */
  public function fields() {
    $fields = [
      'item_id' => $this->t('Item ID'),
      'revision_id' => $this->t('Revision ID'),
      'field_name' => $this->t('Name of field')
    ];
    return $fields;
  }

  /**
   * {@inheritdoc}
   */
  public function getIds() {
    $ids['item_id']['type'] = 'integer';
    $ids['item_id']['alias'] = 'fci';
    return $ids;
  }

}

Luego creamos nuestra plantilla yml de migración en nuestra carpeta config/sync: 

migrate_plus.migration.d7_field_collection_contacts.yml con los siguientes datos: 

langcode: en
status: true
dependencies: {  }
id: d7_field_collection_contacts
class: null
field_plugin_method: null
cck_plugin_method: null
migration_tags:
  - 'Drupal 7'
migration_group: migrate_drupal_7
label: Contacts
source:
  plugin: d7_field_collection_item
  key: migrate
#  field_name is used in our custom plugin to get data about this field_collection_item.
  field_name: field_contact
process:
  field_email:
    plugin: iterator
    source: field_email
    process:
      value: email
    revision_id: revision_id
  field_phone:
    plugin: iterator
    source: field_phone
    process:
      value: value
    revision_id: revision_id
  field_website:
    plugin: iterator
    source: field_website
    process:
      uri: value
    revision_id: revision_id
destination:
  plugin: 'entity_reference_revisions:paragraph'
  default_bundle: contact
migration_dependencies:
  required: {  }
  optional: {  }

Luego de crearla debemos hacer limpiar caché (drush cr) luego exportar nuestras configuraciones (drush cex -y) entonces podremos usar o probar nuestra migración, debemos asegurarnos de revisar los process de los campos, por ejemplo en d7 un link era field_website_uri ahora en d8 es remapeado como field_website_value y así sucesivamente. Una vez más, todo esto finalmente se hará para usted con el sistema de plugin de campo. Pero por ahora tenemos que hacerlo manualmente

Ahora procedemos a la migración de paragraph a nuestro nodo Organization; tenemos nuestra plantilla generada y modificamos nuestro campo Contact porque anteriormente era un field_collection_item:

langcode: en
status: true
dependencies: {  }
id: d7_node_organization
class: null
field_plugin_method: null
cck_plugin_method: null
migration_tags:
  - 'Drupal 7'
  - Content
migration_group: migrate_drupal_7
label: 'Nodes (Organization)'
source:
  plugin: d7_node
  node_type: organization
process:
  nid: tnid
  vid: vid
  langcode:
    plugin: default_value
    source: language
    default_value: und
  title: title
  uid: node_uid
  status: status
  created: created
  changed: changed
  promote: promote
  sticky: sticky
  revision_uid: revision_uid
  revision_log: log
  revision_timestamp: timestamp
  body:
    plugin: iterator
    source: body
    process:
      value: value
      format:
        -
          plugin: static_map
          bypass: true
          source: format
          map:
            - null
        -
          plugin: skip_on_empty
          method: process
        -
          plugin: migration
          migration:
            - d6_filter_format
            - d7_filter_format
          source: format
  field_contacts:
    -
      plugin: skip_on_empty
      method: process
      source: field_contact_new
    -
      plugin: migration_lookup
      migration: d7_field_collection_contacts
      no_stub: true
    -
      plugin: iterator
      process:
        target_id: '0'
        target_revision_id: '1'
destination:
  plugin: 'entity:node'
  default_bundle: organization
migration_dependencies:
  required:
    - d7_user
    - d7_node_type
  optional:
    - d7_field_instance
    - d6_filter_format
    - d7_filter_format

Necesitamos cambiar el valor del source de field_contact y lo he hecho en un hook_migrate_MIGRATION_ID_prepare_row en nuestro archivo .module; esto es debido a que necesitamos enviarle la clave item_id al plugin migration_lookup.

/**
 * Implements hook_migrate_MIGRATION_ID_prepare_row().
 */
function custom_migrate_migrate_d7_node_organization_prepare_row(Row $row, MigrateSourceInterface $source, MigrationInterface $migration) {
    $values = $row->getSourceProperty('field_contact');
    $value_new = [];
    if ($values) {
      foreach ($values as $value) {
        $value_new[] = ['item_id' => $value['value']];
      }
      $row->setSourceProperty('field_contact_new', $value_new);
  }

 

Y de esta manera podemos ahora migrar los field_collection_item a Paragraph y tenerlo en nuestro nodo; le invito a que deje su contribución en los issues que se han creado para automatizar estas migraciones en el módulo de Paragraphs. Muchas Gracias a todos.

¿Está buscando ayuda para una migración o actualización de Drupal? Independientemente de la complejidad del sitio o de los datos, MTech puede ayudarle a pasar de un CMS privado o actualizarlo a la última versión: Drupal 8.

Escríbanos sobre su proyecto y nos pondremos en contacto con usted dentro de 48 horas.