CMS

Drupal8 Migrate API, Migrating Nodes From Drupal7 – Part 1

Introduction

Drupal8 has a powerful and flexible migration system that allows importing content from external sources which could either be an older Drupal(6,7) system or any other database. The migrate API contains many core plugins that can handle migration process as well as provide an option to create custom migration plugins.

In this blog, let us go through the process of creating a custom module to migrate nodes from an existing Drupal7 installation as a source to our destination drupal8 system using the migrate API.

Prerequisites

Before proceeding, we need to enable two core modules.

  1. Migrate –  Provides a framework for migrating data.
  2. Migrate Drupal – Provides a framework(set of plugins) based on the Migrate module to facilitate migration from older Drupal versions.

 

In addition, we need to install the following contributed modules as well.

  1. Migrate Plus – An extension to the core migration framework.
  2. Migrate Tools– Provides Drush command tools for running and managing Drupal 8 migrations.

1.Getting source ready for migration

In the Example that we will use, we would migrate nodes of a custom content type Fruit from an existing drupal7 setup. The content type has the following fields:

Note: The field vitamins is a term reference field for Vitamins vocabulary and can contain unlimited values.

We will create the same content Fruit type along with the fields specified above in the drupal8 setup as well. All the nodes from the drupal7 setup will be migrated under this content type.

Now we will add another database connection pointing to the drupal7 database in the drupal8’s sites/default/settings.php file.

$databases['db_d7']['default'] = array(
  'database' => 'drupal7', // drupal7 database name
  'username' => 'root', // username
  'password' => '', //password
  'prefix' => '',
  'host' => 'localhost',
  'port' => '3306',
  'namespace' => 'Drupal\\Core\\Database\\Driver\\mysql',
  'driver' => 'mysql',
);

Note: The connection name can be anything. Here we have set it as ‘db_d7’.

2.Creating the migration module

In this section, let us create the custom module where we would add our own migration templates and migration sources. To begin, let us create a module named fruit_migration and add the following info.yml file.

name: Migrate Fruits
type: module
description: To Migrate Fruit Nodes From d7 instance
core: 8.x
package: Other
dependencies:
  - migrate
  - migrate_plus
  - migrate_tools

2.1 Migration Group

Field Vitamins and Fruit Image in content type Fruit are term reference and image field respectively. As taxonomy terms and images are entities in Drupal8, they would be required to be migrated separately prior to migration of the nodes. Let us create a migration group and add all migration tasks to it.

Create a file /config/install/migrate_plus.migration_group.fruits.yml inside the module and add the following information to it.

id: fruit
label: Migrate Fruits
description: Migrate Fruits from 7 to 8 migration
source_type: Custom database
shared_configuration:
  source:
    #provide the drupal7 connection name here
    key: db_d7

2.2 Migration Definition

Migration definitions are YML files containing information about the migration source, destination, field mapping(process). The default migration template for nodes are found at  /core/modules/node/migration_templates folder. As our migration source is a Drupal7 system we will customize the template d7_node.yml.

Inside the module, let’s create a file named /config/install/migrate_plus.migration.migrate_fruits.yml and add the following content to it.

id: migrate_fruits
label: Migrate Fruit Nodes
deriver: Drupal\node\Plugin\migrate\D7NodeDeriver
#migration group
migration_group: fruit
#source plugin
source:
  #source plugin name
  plugin: migrate_fruits
  #provide the drupal7 connection name here
  target: db_d7
  #specify the node types that to be migrarted  
  node_type: fruit
process:
  #Here we provie the feild mappings 
  type:
    plugin: default_value
    default_value: fruit
  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
  path: alias
  field_price: price
  field_vitamins: vitamin_terms
  field_fruit_image: fruit_images
  'body/format':
    plugin: static_map
    bypass: true
    source: body_format
    map:
      plain_text: plain_text
      filtered_html: restricted_html
      full_html: full_html
  'body/value': body_value
  'body/summary': body_summary
destination:
  #how to store the data fetched from the source
  plugin: entity:node
migration_dependencies:
  required:
    - migrate_fruit_vitamins
    - migrate_fruit_images

2.3 Migration Source

In the above migration definition, the migration source plugin is specified as migration_fruits.  Let us create a file \src\Plugin\migrate\source\Fruit.php inside the module with following content.

<?php
/**
 * @file
 * Contains \Drupal\fruit_migration\Plugin\migrate\source\Fruit.
 */
namespace Drupal\fruit_migration\Plugin\migrate\source;
use Drupal\migrate\Row;
use Drupal\node\Plugin\migrate\source\d7\Node;
/**
 * Migration Source for fruit nodes  
 *
 * @MigrateSource(
 *   id = "migrate_fruits"
 * )
 */
class Fruit extends Node {
  /**
   * {@inheritdoc}
   */
  public function prepareRow(Row $row) {
    $nid = $row->getSourceProperty('nid');
    //Body field with value, summary, and format
    $result = $this->select('field_data_body', 'fdb')
      ->fields('fdb', ['body_value', 'body_summary', 'body_format'])
      ->condition('fdb.entity_id', $nid)
      ->execute();
    while($record = $result->fetchObject()){
      $row->setSourceProperty('body_value', $record->body_value );
      $row->setSourceProperty('body_summary', $record->body_summary );
      $row->setSourceProperty('body_format', $record->body_format );
    }
    //Price field
    $field_price_value = $this->select('field_data_field_price', 'p')
      ->fields('p', ['field_price_value'])
      ->condition('p.entity_id', $nid)
      ->execute()->fetchField();
    if (!empty($field_price_value)) {
      $row->setSourceProperty('price', $field_price_value);
    }
    // Vitamin Terms Referrence Field
    $vitamin_terms = [];
    $result = $this->select('field_data_field_vitamins', 'fdv')
      ->fields('fdv', ['field_vitamins_tid'])
      ->condition('fdv.entity_id', $nid)
      ->execute();
    while($record = $result->fetchObject()){
      $vitamin_terms[] = $record->field_vitamins_tid;
    } 
    if (!empty($vitamin_terms)) {
        $row->setSourceProperty('vitamin_terms', $vitamin_terms);
    }
    // Image field
    $images = [];
    $result = $this->select('field_data_field_fruit_image', 'fdi')
      ->fields('fdi', ['field_fruit_image_fid', 'field_fruit_image_alt', 'field_fruit_image_title', 'field_fruit_image_width', 'field_fruit_image_height'])
      ->condition('fdi.entity_id', $nid)
      ->execute();
    while($record = $result->fetchObject()){
      $images[] = [
        'target_id' => $record->field_fruit_image_fid,
        'alt' => $record->field_fruit_image_alt,
        'title' => $record->field_fruit_image_title,
        'width' => $record->field_fruit_image_width,
        'height' => $record->field_fruit_image_height,
      ];
    }
    if (!empty($images)) {
        $row->setSourceProperty('fruit_images', $images);
    }
    // Migrate URL alias.
    $alias = $this->select('url_alias', 'ua')
      ->fields('ua', ['alias'])
      ->condition('ua.source', 'node/' . $nid)
      ->execute()
      ->fetchField();
    if (!empty($alias)) {
      $row->setSourceProperty('alias', '/' . $alias);
    }
    return parent::prepareRow($row);
  }
}

In order to create the migration source plugin, @MigrateSource annotation is required to be used in our class. The migration source plugin is a PHP class which provides the source fields that are required to be migrated. The base source plugin class for the node type is Drupal\node\Plugin\migrate\source\d7\Node. Please refer this class to explore more about how the source plugin is actually structured.

Let us extend the class Drupal\node\Plugin\migrate\source\d7\Node and override the prepareRow() method to provide additional data fields. This method will be called once for each row, at the beginning of processing.

The above migration is dependent on two other migrations, migrate_fruit_vitamins and migrate_fruit_images. The first being the migration of the vitamin taxonomy terms and the second being for the images associated with the fruit nodes, which would be covered in the next part.

About The Author

Malay Nayak

Acquia Certified Drupal8 Developer

Leave a Reply

*