Wordpress to Drupal 8 migration setup

Here’s an example of an existing WordPress site, which has registered users with comments, anonymous comments and an archive of previous postings with metadata (tags) that we will import. We’ll finish with this posting displayed in Drupal 8 version of the blog.

The following tables in Wordpress are relevant to the migration:

  • wp_posts (contains blog posts and pages)
  • wp_comments (comments)
  • wp_users (users)
  • wp_terms (categories - taxonomy)

We will started writing our migration module using Migration API (migrate) that comes packaged with D8 core. As usual in Drupal 8 module development, we had .info.yml, files, which uses the Symfony 2 YAML to map data types into an XML-like schema for easy migration from one environment to another, and traditional Drupal .module files. The only thing worth noting here is a dependency on the migrate module in our info.yml file.

For each Entity migration (users, posts, comments) we created 2 more files:

Code breakdown

Users

Let start understanding things by migrating Users first. Here is the complete user YAML file for this migration. Let’s break it down:

In this migrate.migration.users.yml file, ID, Source, Destination and Process (field mapping) are important keys.

 

id: users # Links this file with wordpress.migrate.yaml
label: Wordpress Users
migration_groups:
  - Wordpress

ID key is ‘users’ which has been referred in manifest_wordpress.yml and also in Source Plugin (User.php) as part of Annotation.

 

source:
  plugin: users

Source key is referring to Source Plugin (User.php) created by us to fetch Wordpress users data, which we will see shortly in detail.

 

destination:
  plugin: entity:user

Destination key is user entity as defined by Migration API.

 

process:
  uid: id
  name: user_login
  pass: user_pass
  mail: user_email
  status:
    -
     plugin: default_value
     default_value: 1
  created:
    -
     plugin: callback
     callable: strtotime
     source: user_registered

Process is key to mapping source (Wordpress) and destination (Drupal) fields. Some fields are mapped directly like ‘id’ with ‘sid’ while some fields are pre-processed by Migration API before being assigned to a destination field. For instance, ‘user_registered’ will be processed by PHP function strtotime() before it’s value gets assigned to destination field, because the target Drupal site must recognize those users as registered to associate their previous activity in WordPress with their Drupal activity on the new site. We can even write our own process plugins if required.

Now let’s look at Source Plugin (Users.php) which has been used to fetch the Wordpress users data,

 

<?php
/**
* @file
* Contains \Drupal\migrate_wordpress\Plugin\migrate\source\Users.
*/

namespace Drupal\migrate_wordpress\Plugin\migrate\source;

use Drupal\migrate\Row;
use Drupal\migrate\Plugin\SourceEntityInterface;
use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;

/**
* Extracts users from Wordpress database.
*
* @MigrateSource(
*   id = "users"
* )
*/

Remember the id field in the YAML file? The id field tells Drupal that this class should be used to process data for that migration set, taking the results of “users” in WordPress and adding them to a new table in the Drupal database, wp_users.

 

class Users extends DrupalSqlBase implements SourceEntityInterface {

  /**
   * {@inheritdoc}
   */
  public function query() {
    return $this->select('wp_users', 'u')
      ->fields('u', array_keys($this->userFields()));
  }

The query() method is used to fetch records from Wordpress database table (wp_users).

 

  /**
   * Returns the User fields to be migrated.
   *
   * @return array
   *   Associative array having field name as key and description as value.
   */
  protected function userFields() {
    $fields = array(
      'id' => $this->t('User ID'),
      'user_login' => $this->t('Username'),
      'user_pass' => $this->t('Password'),
      'user_email' => $this->t('Email address'),
      'user_registered' => $this->t('Created time'),
    );
    return $fields;
  }

  /**
   * {@inheritdoc}
   */
  public function fields() {
    $fields = $this->userFields();
  }


userFields() method is called in query() and fields() methods. It contains the list of fields that describe a WordPress user along with their field descriptions.

 

  /**
   * {@inheritdoc}
   */
  public function bundleMigrationRequired() {
    return false;
  }

  /**
   * {@inheritdoc}
   */
  public function entityTypeId() {
    return 'users';
  }

  /**
   * {@inheritdoc}
   */
  public function getIds() {
    return array(
      'id' => array(
        'type' => 'integer',
        'alias' => 'u',
      ),
    );
  }


As we are also implementing an interface SourceEntityInterface, we need to define 2 more methods namelybundleMigrationRequired() and entityTypeId() to map user-generated content, such as postings and comments, to Drupal. The getIds() method is used to return the key field for this Entity.

Posts

The Posts Entity migration (YAML, Source Plugin) is quite similar to User Entity migration. There is one additional method prepareRow() defined in post migration source plugin. This method gets called for each row to do an additional processing of the source data.

 

public function prepareRow(Row $row) {
$post_type = $row->getSourceProperty('post_type');
$type = $post_type == 'page' ? 'page' : 'article';
$row->setSourceProperty('type', $type);

return parent::prepareRow($row);
}


In this method, we are first checking the post type of source data and based on that we set the ‘type’ property. As we have two types of posts in Wordpress (Pages, Blogs posts) we want to map them to two content types in Drupal (Basic page, Article). The ‘type’ property is available in the post-migration YAML file Process step, as discussed above, and we have mapped it to the Drupal content type field.

Comments

The Comments Entity migration is more or less similar to Users and Posts migration. There are some following additions in Comments migration YAML file which are quite interesting to know.

 

uid:
-
   plugin: skip_process_on_empty
   source: user_id
-
   plugin: migration
   migration: users
'comment_body/value': comment_content
'comment_body/format': 'basic_html'

migration_dependencies:
required:
   - posts
   - users

Here we are using ‘skip_process_on_empty’ plugin which means skip the process if the source value is empty. As key fields can have different values in source and destination databases so we are using migration plugin to map ‘user_id’ field with ‘uid’ field. This will look for destination id in migration map tables for given source id (user_id).

Next, the ‘comment_body’ field is a text field and made up of two fields in Drupal 8, so we must map the Wordpress ‘comment_content’ field to value field and mapping basic html to format field. Next we have defined comment migration dependencies to posts and users which means comments will be migrated once both posts and users are already migrated.

Putting it all together

Finally you can run the migration using 'migrate-manifest' drush command as below. Make sure to run this command in Drush 7.

 

drush migrate-manifest manifest_wordpress.yml --legacy-db-url=mysql://{dbuser}:{dbpass}@localhost/{dbname}


The manifest_wordpress.yml is a YAML file which contains reference to IDs of each migration (users, posts, comments etc.).

 

# A Wordpress posts/users/comments migration, with dependencies.
- posts
- users
- comments

Finished: WordPress postings shown in Drupal

Development

We do Drupal development

Go to our Drupal page!

Visit page!

Browse cities

Recommended Stories

Drupal: Age 1-11 in a Nutshell
For over two decades, Drupal has evolved from a simple message board to one of the most powerful and versatile… (Read more)
10 minutes /
Drupal Security Best Practices: Protecting Your Website from Common Threats
As Drupal and other technologies have grown, so have the stakes for keeping websites secure. Security isn’t a… (Read more)
10 minutes /
The Future of Drupal: What’s Next in Web Technologies
The digital world never sits still, and neither should your website. As users demand faster, smarter, and more… (Read more)
10 mins /