Skip to main content
30 Jun 2020

A Complete Guide to Drupal 8 Migration

With over one million users worldwide, Drupal is today, significantly one of the top website solutions due to its robust and flexible infrastructure, innovative design, and open-source modules. Providing manifold organizations access to countless free tools and information to help them get the right features, functionality, and design, is the reason why Drupal is scaling tremendously.

Why Migrate to Drupal 8?

Let’s focus on the top three reasons why you should migrate to Drupal 8.

Ease of use:

In the past, users have raised concerns regarding the tedious back-end experiences of managing and editing a Drupal website. However, thanks to Drupal 8, those complaints can now be successfully eradicated. First off, Drupal 8 (also known as D8) comes with a WYSIWYG editor designed specifically for Drupal’s use. Beyond the traditional basics — like buttons for bold, italic, hyperlinks, and so on — there are added extras, such as easily editable image captions that come with the editor’s new Widgets features.

Quick Edit Module:

Another difference that comes along with Drupal 8 is coming with a new module – the quick edit. This module allows users to make changes to the content directly from the website’s frontend.

Mobile First:

Drupal 8 is fully responsive out-of-the-box – meaning, there is no customization needed to make that happen. After all, it goes without saying that any web product should be fully responsive. This means, that when your site is built on D8, elements such as menus, blocks, and even images will automatically reshape to work and look good on any screen size.

Legacy application to Drupal 8 Migration


In order to execute the migration, a few steps are required.

  • Drush and command line access to the server.
  • Drupal 8 core Migrate module.
  • Migrate Tools contributed module.
  • Migrate Plus contributed module.

To migrate the custom "news" table data into Drupal 8, initially you have to use the News table (Id, Title, Description, Publish Date, Category and Image).

For this, a custom content type called “Article” has to be created. Following it is the fields’ structure.

From the images attached above, one could see what is about to be covered, such as, basic fields like title, body and date along with complex fields like image as well as referenced categories. Now that a basic setup is ready, it’s time to enter the action mode.

Follow the required steps.

Steps: -

  • Install/Download required plugins and modules
  • Define Source and destination data sources
  • Create custom migrate plugin which will handle migration process
  • Executing migration process

Install required plugins and modules: -

  • Drush and command line access to the server (
  • Download and install following migrate modules (
  • Migrate (
  • Migrate Tools (
  • Migrate Plus (

Define Source and Destination Data Sources

Open /sites/default/settings.php file and define your source and destination databases.

$databases['default']['default'] is our destination database

$databases['migrate']['default'] is our source database from where we’ll be migrating data to Drupal.

$databases['default']['default'] = array (

    'database' => 'drupal8',

    'username' => 'root',

    'password' => 'password',

    'host' => localhost',

    'port' => '3306',

    'namespace' => 'Drupal\\Core\\Database\\Driver\\mysql', 'driver' => 'mysql',


$databases['migrate']['default'] = array (

    'database' => 'sourcedb',

    'username' => 'root',

    'password' => 'password',

    'host' => 'localhost',

    'port' => '3306',

    'namespace' => 'Drupal\\Core\\Database\\Driver\\mysql',

    'driver' => 'mysql',


Create Plugin which will Process Migration

The most important component of the migration process is the custom plugin which is to be created next. First, create custom module named "custom_migrate” which will hold migrate plugin called "Article".

Next, follow the structure below to create custom directories.

Please Note:

  • Custom Directory is not created by default, therefore it should be created under modules directory.
  • custom_migration is a module name, it can be re-named as per an individual desire.
  • Create src, Plugin, migrate and source directory as displayed above, do not change folder names or hierarchy.
  • Article.php is our migrate plugin. it can be re-named as per an individual desire.

The SQL Source Plugin must Implement Three Methods:

  • query(): Returns the query that selects the data from the source database.
  • fields(): Returns available fields on the source.
  • getIds(): Defines the source fields uniquely identifying a source row

Example Plugin:


* @file
* Migration src file for Article.
* This module migrates data of Article type.

namespace Drupal\custom_migrate\Plugin\migrate\source;

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

* Source plugin for the article.

* @MigrateSource(
*   id = "article"
* )
class Article extends SqlBase {

   * {@inheritdoc}
  public function query() {
    $query = $this->select('custom_news', 'cn')
      ->fields('cn', ['id', 'title','desc', 'publish_date', 'category','img']);
    return $query;

   * {@inheritdoc}
  public function fields() {
    $fields = [
      'id' => $this->t('id'),

'title' => $this->t('title'),
      'desc' => $this->t('desc'),
      'publish_date' => $this->t('publish_date'),
      'category' => $this->t('category'),
      'img' => $this->t('img'),

    return $fields;

   * {@inheritdoc}
  public function getIds() {
    return [
      'id' => [
        'type' => 'integer',
        'alias' => 'cn',

   * {@inheritdoc}
  public function prepareRow(Row $row) {
    $publish_date = $row->getSourceProperty('publish_date');
    $new_date = date('Y-m-d\Th:i:s', strtotime($publish_date));
    $row->setSourceProperty('publish_date', $new_date);
    return parent::prepareRow($row);


The source plugin, Article.php, must be saved in src/Plugin/migrate/source subdirectory of custom_migrate module. The source plugin has @MigrateSource annotation with id 'games'. This ID will be used in the actual migration definition below. prepareRow() functions explains how a manipulation can be done on the source data.

Now that the plugin file id ready, place the mapping file for the Article.

Two mapping files will be required

  1. migrate_plus.migration.article.yml
  2. migrate_plus.migration.article_image.yml

The Article.yml file is to process article migration, whereas article_image.yml is to process and migrate images.


id: article
label: Migrate Article
migration_group: my_custom_migrate
  plugin: article
  key: migrate
  plugin: entity:node
  default_bundle: news_articles
  nid: id
  'body/value': desc
  'body/summary': desc

plugin: default_value
    default_value: "full_html"
  title: title
  field_publish_date: publish_date
  field_article_category: category
    plugin: migration
    migration: article_image
    source: id
    - article_image

In the file mentioned above, table fields to content type field ids have been mapped. As source we have provided article plugin we created above and destination as content type news_articles we created earlier. As we need to migrate images as well, article_image file has been added as a dependency at the bottom.


id: article_image
label: Image associated to article.

migration_group: my_custom_migrate
  - file
  - image
# Source.
  plugin: article
    file_source_uri: 'public://migration/source_images'
    file_dest_uri: 'public://migration/destination_image'
    - img
# Destination.
  # We will be creating entities of type "file" this time.
  plugin: 'entity:file'
# Mappings.
  img: img
    -plugin: concat
      delimiter: /
        - constants/file_source_uri
        - img
      plugin: concat
      delimiter: /
        - constants/file_dest_uri
        - img
    # Make sure we don't have any url-unfriendly characters.
  # We use the image file names as is.
  # Alternatively, if we wish to name them after some other
  # column, we can do it here.
  filename: img
    plugin: file_copy
    source:- '@file_source'
      - '@file_dest'
# Dependencies.
      - custom_migrate

In article_image.yml file, the source folder location as well as destination location folder is to be provided. The image names must match in the source folder to that of tables’ image field names.

Executing Migration

To execute the migration now, there are two ways

  • From browser
  • Using Drush.

From Browser:

Go to >> Structure >> Migrations >> my_custom_migrate (from article.yml file). Click on the execute button to run the migration. First run the article image migration since initially there is a need to generate files names for images in drupal and then reference them to article.

Using Drush:

Go to your drupal root directory on any command line tool, and execute following commands:

  • drush ms - to get migration status
  • drush mi article_image – to process article image migration
  • drush mi article - to migrate articles.

Every time a migration is run, make sure to clear migration tables which hold migration reports - in this case (migrate_map_article, migrate_map_article_image) before another migration execution.