How to migrate Drupal from 7 to 8

digital conceptual business technology background arrows chip

Start from a fresh install of Drupal 8, in order to generate all the required YML files to use inside your custom migrate modules you must first install all these modules:

Drupal Upgrade

Drush support for direct upgrades from older Drupal versions.

Migrate

Handles migrations

Migrate Drupal

Contains migrations from older Drupal versions.

Migrate Drupal UI

Provides a user interface for migrating from older Drupal versions.

Migrate Plus

Enhancements to core migration support

Migrate Tools

Tools to assist in developing and running migrations.

When you run the migrate upgrade you also need to install all the modules in Drupal 8 for the entities that you want to migrate. You could set a bash script to run these drush commands:

drush en migrate_upgrade -y
drush en migrate_tools -y
drush en module_filter -y
drush en migrate_plus -y
drush en migrate_tools -y
drush en pathauto -y
drush en field_collection -y
drush en backup_migrate -y
drush en metatag -y
drush en migrate_drupal_ui -y
drush en migrate_source_csv -y
drush en webform -y
drush en webform_ui -y
drush en features -y

You can generate all your YML configuration files using the Drupal Upgrade module which provides this Drush command:

drush migrate-upgrade --legacy-db-url=mysql://d7_user:d7_pass@127.0.0.1/d7_database_name --legacy-root=/pathtodrupalsix --configure-only

You can make it even simpler if you add this to your settings.php a database connection called 'upgrade' so the it will read the details of your drupal 7 database connection directly from:

// Database entry for `drush migrate-upgrade --configure-only`
$databases['upgrade']['default'] = array (
  'database' => 'd7_database_name',
  'username' => 'd7_user',
  'password' => 'd7_pass',
  'prefix' => '',
  'host' => 'localhost',
  'port' => '3306',
  'namespace' => 'Drupal\\Core\\Database\\Driver\\mysql',
  'driver' => 'mysql',
);

The you can run it without passing the DB connection details:

drush migrate-upgrade --configure-only

In order to export all your YML configuration files use all the following drush commands:

drush config-export --destination=/tmp/migrate
$ cd /tmp/migrate

$ ls

migrate_plus.migration.upgrade_block_content_body_field.yml               migrate_plus.migration.upgrade_d7_search_settings.yml

migrate_plus.migration.upgrade_block_content_type.yml                     migrate_plus.migration.upgrade_d7_shortcut.yml

migrate_plus.migration.upgrade_d7_block.yml                               migrate_plus.migration.upgrade_d7_shortcut_set.yml

migrate_plus.migration.upgrade_d7_comment.yml                             migrate_plus.migration.upgrade_d7_shortcut_set_users.yml

migrate_plus.migration.upgrade_d7_comment_entity_display.yml               migrate_plus.migration.upgrade_d7_system_authorize.yml

migrate_plus.migration.upgrade_d7_comment_entity_form_display.yml         migrate_plus.migration.upgrade_d7_system_cron.yml

migrate_plus.migration.upgrade_d7_comment_entity_form_display_subject.yml migrate_plus.migration.upgrade_d7_system_date.yml

migrate_plus.migration.upgrade_d7_comment_field.yml                       migrate_plus.migration.upgrade_d7_system_file.yml

migrate_plus.migration.upgrade_d7_comment_field_instance.yml               migrate_plus.migration.upgrade_d7_system_mail.yml

migrate_plus.migration.upgrade_d7_comment_type.yml                         migrate_plus.migration.upgrade_d7_system_performance.yml

migrate_plus.migration.upgrade_d7_custom_block.yml                         migrate_plus.migration.upgrade_d7_taxonomy_term_tags.yml

migrate_plus.migration.upgrade_d7_dblog_settings.yml                   migrate_plus.migration.upgrade_d7_taxonomy_vocabulary.yml

migrate_plus.migration.upgrade_d7_field.yml                               migrate_plus.migration.upgrade_d7_url_alias.yml

migrate_plus.migration.upgrade_d7_field_formatter_settings.yml             migrate_plus.migration.upgrade_d7_user.yml

migrate_plus.migration.upgrade_d7_field_instance.yml                       migrate_plus.migration.upgrade_d7_user_flood.yml

migrate_plus.migration.upgrade_d7_field_instance_widget_settings.yml       migrate_plus.migration.upgrade_d7_user_mail.yml

migrate_plus.migration.upgrade_d7_file.yml                                 migrate_plus.migration.upgrade_d7_user_role.yml

migrate_plus.migration.upgrade_d7_filter_format.yml                       migrate_plus.migration.upgrade_d7_view_modes.yml

migrate_plus.migration.upgrade_d7_filter_settings.yml                     migrate_plus.migration.upgrade_file_settings.yml

migrate_plus.migration.upgrade_d7_global_theme_settings.yml               migrate_plus.migration.upgrade_menu_settings.yml

migrate_plus.migration.upgrade_d7_image_settings.yml                       migrate_plus.migration.upgrade_search_page.yml

migrate_plus.migration.upgrade_d7_image_styles.yml                         migrate_plus.migration.upgrade_system_image.yml

migrate_plus.migration.upgrade_d7_menu.yml                                 migrate_plus.migration.upgrade_system_image_gd.yml

migrate_plus.migration.upgrade_d7_menu_links.yml                           migrate_plus.migration.upgrade_system_logging.yml

migrate_plus.migration.upgrade_d7_node_article.yml                         migrate_plus.migration.upgrade_system_maintenance.yml

migrate_plus.migration.upgrade_d7_node_page.yml                           migrate_plus.migration.upgrade_system_rss.yml

migrate_plus.migration.upgrade_d7_node_revision_article.yml               migrate_plus.migration.upgrade_system_site.yml

migrate_plus.migration.upgrade_d7_node_revision_page.yml                   migrate_plus.migration.upgrade_taxonomy_settings.yml

migrate_plus.migration.upgrade_d7_node_revision_service.yml               migrate_plus.migration.upgrade_text_settings.yml

migrate_plus.migration.upgrade_d7_node_revision_webform.yml               migrate_plus.migration.upgrade_update_settings.yml

migrate_plus.migration.upgrade_d7_node_service.yml                 migrate_plus.migration.upgrade_user_picture_entity_display.yml

migrate_plus.migration.upgrade_d7_node_settings.yml         migrate_plus.migration.upgrade_user_picture_entity_form_display.yml

migrate_plus.migration.upgrade_d7_node_title_label.yml             migrate_plus.migration.upgrade_user_picture_field.yml

migrate_plus.migration.upgrade_d7_node_type.yml                   migrate_plus.migration.upgrade_user_picture_field_instance.yml

migrate_plus.migration.upgrade_d7_node_webform.yml                     migrate_plus.migration_group.migrate_drupal_7.yml

Copy the relevant YML configuration files inside your module(s): /custom/ /config/install.

Most of the times you may need to apply some customisation. For example in Maria Consulting we did not need to use all the above YML files. We set up these 2 custom modules:

migrate_maria and migrate_maria_types. I placed only 5 yml files inside migrate_maria/config/install in order to start migrating node_service, taxonomy_vocabulary, taxonomy_term, url_alias and files. The migrate_maria.info.yml looks like:

name: migrate_maria
type: module
description: Migrate Maria Consulting content to D8
core: 8.x
package: Custom
dependencies:
  - migrate_plus
  - migrate_drupal

Remember that your module must depends from migrate_plus and migrate_drupal. After copying these 5 files inside migrate_maria/config/install in order to make them more custom I renamed these part of the file name from "upgrade_d7" to be "maria_d7": migrate_plus.migration.maria_d7_node_service, migrate_plus.migration.maria_d7_taxonomy_vocabulary, migrate_plus.migration.maria_d7_taxonomy_term, migrate_plus.migration.maria_d7_url_alias and migrate_plus.migration.maria_d7_file

Then I also changed the content of the file to reflect the change of the name. For example the content of migrate_plus.migration.maria_d7_taxonomy_term became:

uuid: 5325b50e-7f9c-4ed4-b139-d09debb43497
langcode: en
status: true
dependencies: {  }
id: maria_d7_taxonomy_term
migration_tags:
  - 'Drupal 7'
migration_group: migrate_maria
label: 'Taxonomy terms (Services)'
source:
  plugin: d7_taxonomy_term
  bundle: tags
process:
  tid: tid
  vid:
    plugin: migration
    migration: maria_d7_taxonomy_vocabulary
    source: vid
  name: name
  description/value: description
  description/format: format
  weight: weight
  parent_id:
    -
      plugin: skip_on_empty
      method: process
      source: parent
    -
      plugin: migration
      migration: maria_d7_taxonomy_term
  parent:
    plugin: default_value
    default_value: 0
    source: '@parent_id'
  changed: timestamp
  field_right_body:
    plugin: iterator
    source: field_right_body
    process:
      value: value
      format:
        -
          plugin: static_map
          bypass: true
          source: format
          map:
            - null
        -
          plugin: skip_on_empty
          method: process
        -
          plugin: migration
          migration:
            - upgrade_d6_filter_format
            - maria_d7_filter_format
          source: format

destination:
  plugin: 'entity:taxonomy_term'
  default_bundle: tags

migration_dependencies:
  required:
    - maria_d7_taxonomy_vocabulary
  optional:
    - maria_d7_field_instance
    - maria_d7_taxonomy_vocabulary
    - maria_d7_taxonomy_term_tags
    - upgrade_d6_filter_format
    - maria_d7_filter_format
    - maria_d7_taxonomy_term

As you can see I changed id: to reflect the name of the file: maria_d7_taxonomy_termmigration_group: migrate_maria, I defined an new plugin: d7_taxonomy_term. Remember to create an install file: migrate_maria.install with hook_uninstall() in order to delete these configuration in case you need to uninstall the module:

/**
 * Implements hook_uninstall().
 */
function migrate_maria_uninstall() {
  \Drupal::configFactory()->getEditable('migrate_plus.migration.maria_d7_node_service')->delete();
  \Drupal::configFactory()->getEditable('migrate_plus.migration.maria_d7_taxonomy_term')->delete();
  \Drupal::configFactory()->getEditable('migrate_plus.migration.mria_d7_taxonomy_vocabulary')->delete();
  \Drupal::configFactory()->getEditable('migrate_plus.migration.maria_d7_url_alias')->delete();
  \Drupal::configFactory()->getEditable('migrate_plus.migration.maria_d7_file')->delete();
}

As I mentioned earlier I defined an new plugin: d7_taxonomy_term.In order to define it, I create this file:migrate_maria\Plugin\migrate\source\Term.php:

<?php

/**
 * @file
 * Contains \Drupal\migrate_maria\Plugin\migrate\source\Term.
 */

namespace Drupal\migrate_maria\Plugin\migrate\source;

use Drupal\migrate\Row;
use Drupal\migrate\Plugin\migrate\source\SqlBase;

/**
 * Drupal 7 taxonomy terms source from database.
 *
 * @todo Support term_relation, term_synonym table if possible.
 *
 * @MigrateSource(
 *   id = "maria_d7_taxonomy_term",
 *   source_provider = "taxonomy"
 * )
 */
class Term extends SqlBase {

  /**
   * {@inheritdoc}
   */
  public function query() {
    $query = $this->select('taxonomy_term_data', 'td')
      ->fields('td', array('tid', 'vid', 'name', 'description', 'weight', 'format'))
      ->distinct();
    return $query;
  }

  /**
   * {@inheritdoc}
   */
  public function fields() {
    return array(
      'tid' => $this->t('The term ID.'),
      'vid' => $this->t('Existing term VID'),
      'name' => $this->t('The name of the term.'),
      'description' => $this->t('The term description.'),
      'weight' => $this->t('Weight'),
      'parent' => $this->t("The Drupal term IDs of the term's parents."),
    );
  }

  /**
   * {@inheritdoc}
   */
  public function prepareRow(Row $row) {
    // Find parents for this row.
    $parents = $this->select('taxonomy_term_hierarchy', 'th')
      ->fields('th', array('parent', 'tid'))
      ->condition('tid', $row->getSourceProperty('tid'))
      ->execute()
      ->fetchCol();
    $row->setSourceProperty('parent', $parents);
    return parent::prepareRow($row);
  }

  /**
   * {@inheritdoc}
   */
  public function getIds() {
    $ids['tid']['type'] = 'integer';
    return $ids;
  }

}

The reason why I had to create this custom plugin was because the original one (upgrade_d7_taxonomy_term) defined by the core (please see Drupal\migrate_drupal\Plugin\migrate\source) was not setting the parents (default parent was 0!). As you see in the PrepareRow() method I added the logic to get the parents from the taxonomy_term_hierarchy table so that my taxonomy terms keep the hierarchy structure.

Now that all my migrate configuration are set up, it comes the most exiting part of the process it self: RUN the drush mi commands! Obviously the first thing to do is to enable our custom module migrate_maria:

drush en migrate_maria -y
dursh cr
drush ms
 Group: Import Maria Consulting Content from Drupal 7 (migrate_maria)        Status  Total  Imported  Unprocessed  Last imported
 upgrade_d7_file                                                             Idle    22     22        0            2017-03-11
 upgrade_d7_taxonomy_vocabulary                                              Idle    1      1         0            2017-03-11
 upgrade_d7_url_alias                                                        Idle    57     57        0            2017-03-11
 maria_d7_taxonomy_term                                                      Idle    29     29        0            2017-03-11
 upgrade_d7_node_service                                                     Idle    22     22        0            2017-03-11

For example when I ran “drush mi maria_d7_taxonomy_term” magically 29 terms were imported and the hierarchy was kept! You can also run all these migrations in only one command using the group option: “drush mi --group=migrate_maria”.

In conclusion I have to admit that initially I found the migration process very different from the way I was used to know it from the previous version of Drupal, but actually in Drupal 8 I found it much simpler because all the settings are all in YML files which are very easy to configure and there is a complete separation between configuration and PHP code: all the behaviors are added in Class which defines Plugin and they have a very simple Interface and all the methods are self explanatory: query(), fields(), prepareRow(Row $row), getIds(). There are so many Plugins already defined in the core that actually you need to do very little to make your migration works! Actually for Maria Consulting I had only to define one custom Plugin which was Term.php.

1. Consulting

tower bridge on bright sunny day

Contact the Drupal Experts for a free consultation, get a fast and effective support. Get your custom solution, we can create the module that does the job.

2. Install

Man near keyboard

Drupal 8 is an open source Content Management System (CMS), read our Guide on how to install it on your local environment using Docker4Drupal.

3. Website

writing codes and database

Great expertise in web development, Agile environment, JIRA and project management, Github and SVN. Mostly specialised in Drupal Backend..

4. Keywords

seo search engine optimization

Read our article on how to use and configure Drupal to use Simple XML sitemap, Meta Tags, Keywords, ALT and titles on images.