back

Knowlegde

Knowledge Centre

How to Migrate Paragraphs from Drupal 7 to Drupal 8

by editor | 22.12.2017

How to Migrate Paragraphs from Drupal 7 to Drupal 8

A few days ago, we encountered an exciting challenge: migrating paragraphs from a Drupal 7 platform to Drupal 8. While searching for the right solution, we scoured the web but found nothing that perfectly fit our needs. As a result, our backend developers created a custom solution. Special thanks to mtech-llc.com and Ada Hernández, whose work on field collection migration inspired this solution.

While Ada's original article describes migrating field collections, we adapted the approach for paragraphs migration. Her insights proved invaluable to our process.

Before proceeding, ensure you have:

1. A functional Drupal 8 installation

2. The following modules installed:

  • migrate_drupal
  • migrate_plus
  • migrate_tools
  • migrate_drupal_ui

Alternatively, you can add these as dependencies to your custom module.

After installing the required modules, create a paragraph with bundle "contact" containing these fields:

  • Text plain (machine name: field_name)
  • Email (machine name: field_email)
  • Number (Integer) (machine name: field_phone)

Then create a content type called Organization (machine name: organization) with:

  • Default body field
  • Field paragraph type (machine name: field_contact)

This procedure is complex and requires careful attention to detail. If errors occur during any step, you may need to start over. Please read each step thoroughly before proceeding.

1. Specify Database Connection in settings.php

Add the second connection to Drupal 7 in your settings.php file. In this example, the database name is "drupal_7_56":

$databases['migrate']['default'] = array(
   'database' => 'drupal_7_56',
   'username' => 'root',
   'password' => '',
   'prefix' => '',
   'host' => '127.0.0.1',
   'port' => '33067',
   'namespace' => 'Drupal\\Core\\Database\\Driver\\mysql',
   'driver' => 'mysql',
);

2. Create a Custom Module

Create a custom module with the machine name "custom".

3. Create YML Templates

Place the following YML templates in the config/install folder:

First, create the paragraph migration template (filename: migrate_plus.migration.d7_paragraph_contact.yml):

langcode: en
status: true
dependencies: {}
id: d7_paragraph_contact
class: null
field_plugin_method: null
cck_plugin_method: null
migration_tags:
 - 'Drupal 7'
migration_group: migrate_drupal_7
label: Contacts
source:
 plugin: d7_paragraph_item
 key: migrate
 field_name: field_contact
process:
 field_name:
   plugin: iterator
   source: field_name
   process:
     value: value
   revision_id: revision_id
 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
destination:
 plugin: 'entity_reference_revisions:paragraph'
 default_bundle: contact
migration_dependencies:
 required: {}
 optional: {}

Then create the node migration template (filename: migrate_plus.migration.d7_node_organization.yml):

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: nid
 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_contact:
   - plugin: skip_on_empty
     method: process
     source: field_contact
   - plugin: migration_lookup
     migration: d7_paragraph_contact
     no_stub: true
   - plugin: iterator
     process:
       target_id: '0'
       target_revision_id: '1'
destination:
 plugin: 'entity:node'
 default_bundle: organization
migration_dependencies:
 required: {}
 optional: {}

4. Create the Source Plugin Class

Create a file named ContactParagraph.php in the /src/Plugin/migrate/source directory:

<?php
namespace Drupal\mparagraf\Plugin\migrate\source;
use Drupal\migrate\Row;
use Drupal\migrate_drupal\Plugin\migrate\source\d7\FieldableEntity;
/**
* D7_paragraph_item source.
*
* @MigrateSource(
*   id = "d7_paragraph_item"
* )
*/
class ContactParagraph extends FieldableEntity {
 /**
  * {@inheritdoc}
  */
 public function query() {
   // Select node in its last revision.
   $query = $this->select('paragraphs_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('paragraphs_item', 'contact')) as $field) {
     $item_id = $row->getSourceProperty('item_id');
     $revision_id = $row->getSourceProperty('revision_id');
     $row->setSourceProperty($field, $this->getFieldValues('paragraphs_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;
 }
}

5. Implement the Migration Hook

Add the following code to your module's .module file:

<?php
use Drupal\migrate\Plugin\MigrationInterface;
use Drupal\migrate\Plugin\MigrateSourceInterface;
use Drupal\migrate\Row;
/**
* Implements hook_migrate_MIGRATION_ID_prepare_row().
*/
function custom_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', $value_new);
 }
}

6. Final Steps

Once you've completed all the above steps successfully, you can run your migration and watch as your paragraphs are transferred from Drupal 7 to Drupal 8. Take a moment to verify the results before proceeding with any additional paragraph-related tasks.

Remember to test thoroughly in a development environment before attempting this migration on a production site.

migrate
Top
default
  • Knowlegde
    Knowledge Centre
    Fine-tuning LLaMA to Recreate Eminescu's Literary Style
    editor
  • Knowlegde
    Knowledge Centre
    A New Era Begins: Drupal CMS 1.0 Launches
    editor
  • Knowlegde
    Knowledge Centre
    Bringing AI to B2B
    editor

Post a Comment.