Since Drupal 8.5, the Media module in core has been a reality and the recommended way to handle media content in Drupal.

If you're migrating from Drupal 7 to Drupal 8 or Drupal 9, this is something you need to take into consideration.

In this blog post, we'll go through a migration example where you'll learn how to migrate media items and their relationships in Drupal.

The Drupal 8/9 Migration Tutorial Series

Before We Start

The Problem

We have received a database dump and files backup from our imaginary client.

We need to:

  • Migrate files from articles into Drupal 8 files
  • Create media items for each migrated file
  • Migrate articles to Drupal 8 and add the related media items.

Setting up the Migration

Create the migration module

We first need to create a module for our migrations. In this example, we're naming it migrate_example_media

We then need to add the following modules as dependencies in the module declaration:

Create a migration group

To group the migrations, we also need to create a migration group. To do so, we’ll create a fairly simple configuration file so that the group gets created when the module is installed. The file’s contents should be as follows:

id: media
label: Media Group
source_type: Drupal 7
    key: migrate_d7

Define a new database connection

Next, you need to load the Drupal 7 database into your Drupal 8 installation. To do so, you need to define a new database connection in your settings.php file like this:

$databases['migrate_d7']['default'] = array(
  'driver' => 'mysql',
  'database' => 'migrate_d7',
  'username' => 'user',
  'password' => 'password',
  'host' => 'db',
  'prefix' => '',

And then you can import the database dump into this new database connection using your preferred method.

Writing the Migrations

Next thing to do is to write the actual migrations. Per our requirements, we need to write three different migrations: one for files, one for media and one for articles.

Since Drupal 8.1.x, migrations are plugins that should be stored in a migrations folder inside any module. You can still make them configuration entities as part of the migrate_plus module, but I personally prefer to follow the core recommendation because it's easier to develop (you can make an edit and just rebuild cache to update it).

Write the file migration

The first migration to write is the file migration. To speed things up, we're placing the files backup into a migratefiles folder in sites/default/files and we'll copy the files from there to the right folder during the migration.

The source section of the migrate plugin looks like this:

  plugin: d7_file
  scheme: public
    migrate_files_path: 'sites/default/files/migratefiles'

We're setting migrate_files_path to be the base path where we put the backup files. This will be used later to copy the files to the right location.

The process section of the migrate file looks like this:

  filename: filename
        plugin: str_replace
        source: filepath
        search: "sites/default/files/"
        replace: ""
        plugin: concat
        delimiter: /
            - constants/migrate_files_path
            - '@replaced_filepath'
        plugin: urlencode
    plugin: file_copy
        - '@source_full_path'
        - uri
  filemime: filemime
  status: status
  created: timestamp
  changed: timestamp
  uid: uid

First, we create a temporary replaced_filepath to remove the path prefix. Then, we'll use the concat plugin to create the source_full_path based on the migrate_files_path constant and the replaced_filepath. Then, for the uri, we use file_copy to copy from this source_full_path to the destination URI. The remaining fields are directly mapped from Drupal 7 values.

You can look at the full migration file in the code samples repo.

Write the media migration

The media migration also uses d7_file as the source. We use the skip_on_value plugin in a temporary temp1 field to skip files that are not images:

    plugin: skip_on_value
    method: row
    not_equals: true
        - image/png
        - image/jpg
        - image/jpeg
    source: filemime

We use migration_lookup plugin to add the actual image files like this:

    plugin: migration_lookup
    migration: file
    source: fid

You can look at the full migration file in the code samples repo.

Write the article migration 

The article migration is pretty similar to any other entity migrations. To set the image, we're using the sub_process and migration_lookup plugins like this:

  plugin: sub_process
  source: field_image
    plugin: migration_lookup
    source: fid
    migration: media_image

So that it looks for the right items in the media_image migration. You can look at the full migration file in the code samples repo.

Running the Migrations

Since we have set dependencies, we can instruct Drupal to run the migration group and it will run the migrations in the right order.

To do so, execute drush mim --group=media and the output will look like this:

 [notice] Processed 3 items (3 created, 0 updated, 0 failed, 0 ignored) - done with 'file'
 [notice] Processed 3 items (3 created, 0 updated, 0 failed, 0 ignored) - done with 'media_image'
 [notice] Processed 3 items (3 created, 0 updated, 0 failed, 0 ignored) - done with 'article'

You can also run drush ms to see current migration status:

--------------------- ----------------------------------- -------- ------- ---------- ------------- ---------------------
  Group                 Migration ID                        Status   Total   Imported   Unprocessed   Last Imported
 --------------------- ----------------------------------- -------- ------- ---------- ------------- ---------------------
  Media Group (media)   file                                Idle     3       3          0             2020-08-24 16:06:10
  Media Group (media)   media_image                         Idle     3       3          0             2020-08-24 16:06:10
  Media Group (media)   article                             Idle     3       3          0             2020-08-24 16:06:10

Next Steps