D8: How to migrate content and be happy

About me

D7

Import of large Ecommerce Catalogs (XLS, CSV, with up to 350k products ).

 

Daily imports of SKU, Price, Stock (XML files). - ~3k updates per day

D8

Migrate content from EzPublish to Drupal 8. (~50K nodes, 20Gb files)

 

Import content nodes (XML) daily. ~1-3k per day.

Practical Experience with Imports/Migrations

D7 vs D8

D7:

  • Migrate
  • Feeds
  • Custom

D8:

  • Migrate
  • Custom

Advantages of Migrate API

  • Universal tool for import
  • Allows to track changes
    • Hashes
    • Highwater mark
  • Has Drush integration
  • Flexible Plugins system

Cons

  • Only Drush oriented
  • Missing advanced examples

Structure

Source

Destination

Row

DataFetcher

DataParser

Process

Migration API

built using Drupal 8 Plugin System

Getting started

  • Migrate_tools
  • Migrate_plus
  • Migrate_example

Practice

WTF?

  • Migrations are not plain
  • Migrations have dependencies on other migrations
  • Advanced cases are not that "well-documented"
  • You'd better know how to deal with Paragraphs, Terms, Media

Do it fast

  • Prepare Specification
  • Prepare Source (and put down an "interface agreement")
  • Implement POC
  • Add Logs
  • Finalize it
  • Forget about it

Possible sources

Anything:

  • DB
  • REST service
  • XML
  • CSV
  • JSON

Specifications

Source DB of another CMS

Download template at the link

Specifications

Source XML file

Download template at the link

Sample Migration

# Migration configuration for offers.
id: foo
label: foo
migration_group: bar
migration_dependencies: {}

 


destination:
  plugin: entity:node

process:
  type:
    plugin: default_value
    default_value: offer

  title: o_name
  field_description: o_description

  sticky:
    plugin: default_value
    default_value: 0
  uid:
    plugin: default_value
    default_value: 1


source:
  plugin: url
  track_changes: true
  data_fetcher_plugin: file
  data_parser_plugin: xml
  urls: private://import/offer.xml
  item_selector: /client/offer

  ids:
    o_id:
      type: string

  fields:
    -
      name: o_id
      label: 'External id'
      selector: @id
    -
      name: o_name
      label: 'Name'
      selector: titre/text()
    -
      name: o_description
      label: 'Description'
      selector: description/text()

Process plugins, pipeline

drupal plugin:debug migrate.process

"Sometimes, a source value must pass through multiple plugins to end up with the right value and structure for the destination property."

 

"The second plugin and so on does not need a source as their input is the output of the previous plugin."

But what if it actually need?

Process plugins, pipeline

  field_photos:
    -
      plugin: migration
      migration: program_label
      source: p_labels
      no_stub: true
    -
      plugin: foo
      prefix_source: p_title
      suffix_source: p_labels
      handle_multiples: TRUE
    -
      plugin: iterator
      process:
        target_id: fid
        alt: alt

  field_availability:
    -

    plugin: static_map
      source: off_availaibility
      map:
        "true": 1
        "false": 0

    -
      plugin: bar
      source:
         - value
         - origin_name

drupal plugin:debug migrate.process

Hints and tips

#Migration status

$drush ms

#Reset a active migration's status to idle.

$drush migration-reset-status foo

#Reload configuration from .yml config

$drush cim --partial --source=modules/custom/foo/config/install

$drush cr

Don't forget to clear cache!

Maitainance

Hints and tips

#Keep your code clean in foo.install

<?php
/**
 * Implements hook_uninstall().
 */
function migrate_foo_uninstall() {
  // Delete this module's migrations.
  $migrations = [
    'foo',
    'bar',
  ];
  foreach ($migrations as $migration) {
    Drupal::configFactory()
      ->getEditable('migrate_plus.migration.' . $migration)
      ->delete();
  }
}

Uninstall

Hints and tips

#Execute migration (without executing dependencies!).

$drush mi foo

#Execute migration with limited number of items

$drush mi foo --limit=10

#Execute migration limited to Comma-separated list of item IDs to import

$drush mi foo --idlist=45301,45303

#Execute migration (Execute all dependent migrations first.).

$drush mi foo --execute-dependencies

Run

Debug migration

$export XDEBUG_CONFIG="idekey=PHPSTORM"

$drush mi foo --limit=2

Make sure that param "max. simultaneous connections" is set to 3 or higher

Profile migration

$blackfire run ~/.composer/vendor/bin/drush.php mi foo --limit=2

Profile migration

Links

  • https://www.drupal.org/node/2129651
  • https://www.drupal.org/node/2127611
  • https://www.jeffgeerling.com/blog/2016/migrating-20000-images-audio-clips-and-video-clips-drupal-8
  • https://blog.liip.ch/archive/2016/05/04/using-the-new-drupal-8-migration-api-module.html
  • https://www.palantir.net/blog/migrating-xml-drupal-8

Questions?

How to migrate content and be happy

By Ivan Tsekhmistro

How to migrate content and be happy

  • 772