Moving data to Drupal 8

Moving Up

Destination Plugins

The next section of the migration defines a destination plugin, which tells Drupal where to save incoming data:

destination:
  plugin: "entity:node"

Each pet will be saved as a new node using the entity:node plugin. Drupal 8 provides most of the destination plugins you will need. Many contributed modules include destination plugins for their own entity types and configuration. You might need to create a custom destination if you want to migrate data into a custom table. For example, pretend that pets should be imported into a custom table instead of nodes:

destination:
  plugin: pet_dest

The destination plugin is placed in src/Plugin/migrate/destination/PetDestination.php and must extend \Drupal\migrate\Plugin\migrate\destination\DestinationBase. Destinations are defined using the @MigrateDestination annotation. Similar to source plugins, the annotation id key is referenced in the configuration:

/**
 * Pet destination.
 *
 * @MigrateDestination(
*   id = "pet_dest"
 * )
 */
class PetDestination extends   DestinationBase {

Destination plugins must also describe available fields by implementing MigrateDestinationInterface::fields() and MigrateDestinationInterface::getIds() (Listing 5). The plugin overrides MigrateDestinationInterface::import() to save data to a custom table.

Listing 5

Describe Available Fields

/**
 * {@inheritdoc}
 */
public function fields(MigrationInterface $migration = NULL) {
  return [
    'pid' => $this->t('Pet id'),
    'oid' => $this->t('Owner id'),
    'type' => $this->t('Pet type'),
    'name' => $this->t('Pet name'),
    'photo' => $this->t('Pet photo'),
  ];
}
/**
 * {@inheritdoc}
 */
public function getIds() {
  return [
    'pid' => ['type' => 'integer']
  ];
}

Assume $this->save() is defined and handles inserting a new row into the appropriate table. If something goes wrong, throw a MigrateException (Listing 6). When an exception is thrown, the source row is marked as failed and processing continues to the next row. You can review and fix failed rows after the migration is executed.

Listing 6

Mark Source Row as Failed

/**
 * {@inheritdoc}
 */
public function import(Row $row,
  array $old_destination_id_values = []) {
  $pet = $this->save($row);
  if (!$pet) {
    throw new MigrateException('Could not save pet');
  }
10 }

Migrations can be rolled back after testing in case of errors. Rolling back a migration deletes all imported data. The destination plugin is responsible for cleanly removing imported rows in MigrateDestinationInterface::rollback(). This method is called once for each imported row (Listing 7).

Listing 7

Remove Imported Rows Cleanly

/**
 * {@inheritdoc}
 */
public function rollback(array $destination_identifier) {
  $pet = $this->load(reset($destination_identifier));
  if ($pet) {
    $pet->delete();
  }
}

Process Plugins

The process key contains mappings from source fields (on the right) to destination fields (on the left). Each mapping is provided by a process plugin (Listing 8). Several process plugins are included in core [5] (see Table 1 for a list of common plugins). If no plugin is provided (e.g., nid: pid), then Drupal assumes the default get plugin, which copies source values without any modification. Be sure to include the source key if a plugin is specified.

Listing 8

Mappings

process:
  nid: pid
  title:
    plugin: callback
    callable: trim
    source: name
  type:
    plugin: default_value
    default_value: pet
  uid:
    plugin: migration
    migration: pet_owners
    source: oid
  field_pet_type:
    plugin: cat_to_dog
    source: type
  field_photo: picture

Table 1

Common Process Plugins Included in Core

Process Plugin Function Example
get Copies a value verbatim. The example is the default plugin if none is specified. name: source_name
callback Passes source value through a callable function title: plugin: callback callable: trim source: title
dedupe_entity Ensure a field is unique. A numeric counter will be appended to the value until it is unique. machine_name: plugin: dedupe_entity entity_type: node field: type postfix: '_'
default_value Provide a default value if the source is null, zero, or an empty string. type: plugin: default_value default_value: dog
migration Resolve an ID field mapping from another migration. uid: plugin: migration migration: pet_owners source: oid
skip_on_empty Skip the row if the source value is empty (empty string, FALSE, or 0). title: plugin: skip_on_empty method: row source: name
static map Define a custom mapping for source to destination values. type: plugin: static_map source: type map: cat: dog fish: bird

Process plugins can have different configuration options. The callback plugin uses the callable option to pass values through a function. For example, you can trim whitespace from pet names using the callback plugin with the PHP trim() function:

title:
  plugin: callback
  callable: trim
  source: name

Several process plugins may be used for a single field. Simply provide a YAML array with multiple plugins and options. The field value will be passed through each plugin with the result from one passed as the input to the next. This is called a process pipeline. The following pipeline passes the source value through the trim() callback followed by substr to return the last 10 characters:

title:
  - plugin: callback
    callable: trim
    source: title
  - plugin: substr
    start: -1
    length: 10

Note that the source key is omitted for all but the first plugin definition. The source is assumed to be the output of the previous plugin.

The type field (field_pet_type) uses a custom process plugin (cat_to_dog):

field_pet_type:
    plugin: cat_to_dog
    source: type

Custom process plugins extend \ Drupal\migrate\ProcessPluginBase, implement MigrateProcessInterface::transform(), and are defined using the @MigrateProcessPlugin annotation. The cat_to_dog plugin changes pet type from "cat" to "dog" and is located in src/Plugin/migrate/process/CatToDog.php (Listing 9).

Listing 9

Custom Process Plugin

/**
 * Turn cats into dogs.
 *
 * @MigrateProcessPlugin(
 *   id = "cat_to_dog",
 * )
 */
class CatToDog extends ProcessPluginBase {
  /**
   * {@inheritdoc}
   */
  public function transform($value,
    MigrateExecutableInterface $migrate_executable,
    Row $row, $destination_property) {
    return ($value == 'cat') ? 'dog' : $value;
  }
}

Migration Dependencies

A migration might require others to be executed first (see the "Watch Out!" box). For example, the uid field mapping in Table 1 uses the migration process plugin to reference the pet_owners migration. Add the pet_owners migration as a required dependency to make sure it runs first. You can also specify optional dependencies thus,

Watch Out!

While developing your migrations, you will need to disable and re-enable your module regularly. Add the following dependency to all of your YAML configuration files to make sure the configuration is removed and reinstalled along with your module.

dependencies:
  enforced:
    module:
      - MODULENAME

Without this explicit dependency, the configuration will remain in the database, even when your module is disabled, and you will not be able to re-enable the module.

migration_dependencies:
  required:
     - pet_owners

to help order migrations correctly.

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy ADMIN Magazine

SINGLE ISSUES
 
SUBSCRIPTIONS
 
TABLET & SMARTPHONE APPS
Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

  • WordPress, Joomla, and Drupal compared
    Open source content management systems have an abundance of extensions scattered across the web. We compare the features of three CMSs against various consumer requirements.
  • Drush vs. Drupal Console
    Let's take a stroll through the brave new world of Drupal 8, now with Drupal Console; lowering the learning curve for the newbie and the seasoned pro alike.
  • Unprecedented built-in support for diverse languages
    Thanks to the efforts of the Drupal 8 Multilingual Initiative, you can install Drupal in at least 100 languages and exercise granular control over the languages on your website.
  • Moving your data – It's not always pretty
    The world is swimming in data, and the pool is getting deeper at an alarming rate. At some point, you will have to migrate data from one set of storage devices to another. Although it sounds easy, is it? We take a look at some tools that can help.
  • Migration from LDAP to FreeIPA
    The change from centralized user authentication on a vanilla LDAP server to the FreeIPA identity management solution is easier than many admins think. Given attention to a few points, the migration takes very little time and effort.
comments powered by Disqus