Blog

Custom Process Plugins for Drupal 8 Migrations using Migrate

Keenan Holloway

Technical Architect, Forum One

Sarah LeNguyen

Chief Technology Officer, Forum One

If your organization is considering making the jump from Drupal 7 to Drupal 8, the major task of migrating all your content and data over may be sending shivers down your spine. The good news is that there is a great tool to make this step a walk in the park.

The name of the tool alone shouts simplicity: Migrate. Migrate is a powerful core module that,  when teamed up with Migrate Plus, has become a go-to feature on almost every Drupal website we work on with our clients. If you are  familiar with the Drupal 7 Feeds module, you can appreciate that pulling content in is a multi-step process.

Generally speaking, a migration follows three steps:

  1. Determine the content source (JSON, XML, CSV, etc)
  2. Determine the Drupal destination entity (Node, Taxonomy, Paragraph type, etc)
  3. Map the source content to the destination field(s)

Step three assumes that the content in your source code can be directly copied to your destination field; however, sometimes you need to ‘process’ it a bit. In D7 Feeds, this was where Feeds Tamper came in handy. Now, when using the Drupal 8 Migrate module, we instead use Process Plugins. It can give you a decent amount of out-of-the-box functionalities, but there are times where you will likely need custom ones. For this, at Forum One, we make our own.

The steps to make your own custom plugin are pretty straight forward:

  1. Create a custom module for your plugin to “live”
  2. Create the custom plugin itself by extending the ProcessPluginBase class
  3. List your custom plugin as a dependency in your migrate configuration

Let’s go through each one in more detail.

Step 1: Create a custom module for your plugin to “live”

This step is pretty straight forward. For D8 all we need is a directory and a .info file.

File name and path:

/modules/custom/my_module/my_module.info.yml

Contents:

name: My module
type: module
description: This Module provides custom functionality for content migrations
package: Custom
core: '8.x'
dependencies:
  - drupal:migrate
  - drupal:migrate_plus>

Step 2: Create the custom plugin itself by extending the ProcessPluginBase class

In this plugin, take the value from the input source and add a string to the end of it. Below is an example, however, this can used for any manipulation you can think of. An important note here: the path is important, so be sure that it follows the custom module in src/Plugin/migrate/process/yourPluginName.php.

File name and path: /modules/custom/my_module/src/Plugin/migrate/process/yourPluginName.php

Contents:

<?php

namespace Drupalmy_modulePluginmigrateprocess;

use DrupalmigrateProcessPluginBase;
use DrupalmigrateMigrateExecutableInterface;
use DrupalmigrateRow;

/**
 * Returns an href url string from the source string if link markup exists.
 *
 * Example:
 *
 * @code
 * process:
 *   field_your_field_name:
 *     -
 *       plugin: your_plugin_name
 *       source: some_source_value
 * @endcode
 *
 * This will add a string to the end of a value.
 *
 * @see DrupalmigratePluginMigrateProcessInterface
 *
 * @MigrateProcessPlugin(
 *   id = "your_plugin_name"
 * )
 */
class yourPluginName extends ProcessPluginBase {
  /**
   * {@inheritdoc}
   */
  public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
    // If the $value field which is the source value is a string add hello world to the end of it.
    if (is_string($value)) {
        return $value . ‘ hello world’;
    }
    return null;
  }
}

Let’s look at some of the important parts of this code

namespace Drupalmy_modulePluginmigrateprocess; 

This identifies your plugin (classes, functions, etc) in your module for use elsewhere if others want to extend it.

use DrupalmigrateProcessPluginBase;
use DrupalmigrateMigrateExecutableInterface;
use DrupalmigrateRow;

The “use” allows you to specify which classes you want to use from other modules. In this example, we use the Core Migrate module. In short, it allows you to use other migrate process building blocks so yours can be as simple as the code above.

/**
 * Returns an href url string from the source string if link markup exists.
 *
 * Example:
 *
 * @code
 * process:
 *   field_your_field_name:
 *     -
 *       plugin: your_plugin_name
 *       source: some_source_value
 * @endcode
 *
 * This adds a string to the end of a value.
 *
 * @see DrupalmigratePluginMigrateProcessInterface
 *
 * @MigrateProcessPlugin(
 *   id = "your_plugin_name"
 * )
 */

Another note: the documentation here is of utmost importance. It not only helps to explain how to use the plugin to others, but it also contains information about what the plugin is called to Drupal, i.e., the segment starting with @MigrateProcessPlugin.

class yourPluginName extends ProcessPluginBase

In this example, we have extended the ProcessPluginBase class to include the custom plugin. The only functionality we are adding here is the Transform function.

Inside this function, the $value represents the source content from the migration source that is set in the migrate configuration. Based on our example, in the comment documentation that would represent some_source_value. We then simply append a string to it and return it as the output of our Process plugin (or null if the value isn’t a string). It’s a good idea to make sure your plugin gets what you expect it to in case someone passes a value that you don’t want your plugin to handle.

Step 3: List your custom plugin as a dependency in your migrate configuration

Finally, we have the actual migration configuration. This file will vary depending on your data source, destination and structure, as well as the location of the file. That being said, in general it should include the following:

uuid: some-uid
langcode: en
status: true
dependencies:
  enforced:
    module:
      - my_module
id: your_migration_id
class: null
field_plugin_method: null
cck_plugin_method: null
migration_tags: null
migration_group: your_migration_group
label: ‘A custom migration'
source:
  …
process:
  …
  field_your_field_name:
    -
      plugin: your_plugin_name
      source: some_source_value
  …
destination:
  …

And there you have it! The main things to highlight are (1) how our custom module is listed as the dependency for the migration, and (2) that we have a migration process that uses our new custom plugin as it copies the value of some_source_value into field_your_field_name.

There are really a lot of things you can do with custom migration plugins, and I hope this this example is helpful as you get started on a large-scale migration.

Looking to migrate your site to Drupal 8? We can do that for you.

Leave the grunt work to us. Our Drupal expert developers (like Keenan!) love this stuff and can make sure your next website migration is in good hands. Get in touch today.

Written By

Keenan Holloway

Technical Architect, Forum One

Sarah LeNguyen

Chief Technology Officer, Forum One