Phần 2: Migrating users từ Drupal 7 sang Drupal 8 site

Phần 2: Migrating users từ Drupal 7 sang Drupal 8 site

In this article, we will be building a custom migration which will import users from a Drupal 7 site into a Drupal 8 site. The migration will include the standard user profile fields like username and email address, plus a few custom fields added to the user profile.

The Drupal 8 Migration system is very flexible and can be used to migrate many types of data. For an overview of its capabilities and architecture, see Part 1 of this series.

Try this at home!

If you want to try this code yourself, you will need to set up the following:

A clean install of Drupal 7, with the following customizations:

In Admin -> Configuration -> People -> Account Settings -> Manage Fields, add the following fields:

  • "First Name" - Text
  • "Last Name" - Text
  • "Biography" - Long text (summary is not necessary)
  • The default widget and settings are OK for all fields

Once the field configuration is complete, you will need to create a few users so you have some data to migrate.

A clean install of Drupal 8, with the following:

In Admin -> Configuration -> People -> Account Settings -> Manage Fields, add the following fields:

  • "First Name" - Text (plain)
  • "Last Name" - Text (plain)
  • "Biography" - Text (formatted, long)
  • The default widgets and settings are OK for all fields

Create the custom migration module

Migrations are contained within Drupal 8 modules. All migration modules depend on the core "Migrate" module, which provides the migration framework. In addition, for our custom migration here, we are depending on the core "Migrate Drupal" module (which provides Drupal 6 to Drupal 8 migrations) for some base classes.

To begin, we will create our own custom module called "migrate_custom" and add the following information to the file "migrate_custom.info.yml":

name: Custom Migration
description: Custom migration module for migrating data from a Drupal 7 site.
package: Migrations
type: module
core: 8.x
dependencies:
  - migrate
  - migrate_drupal

The migration definition

For our migration, we need to create a YAML file containing the source, destination, and field mappings (called "process").

Source and destination

These configuration parameters inform the Migrate module how to get the data and where to save it. In our example, "source" will refer to a custom plugin we define, and "destination" will refer to the built-in "entity:user" plugin defined by the core Migrate module.

To start our migration definition, create a new file within your module, in the location {module root}/config/install/migrate.migration.custom_user.yml with the following contents:

id: custom_user
source:
  plugin: custom_user
destination:
  plugin: entity:user
process:
  # Field mappings and transformations will go here. We will get to this in a minute.

Creating the source plugin

Our definition above will request data from a source plugin called "custom_user". (Note that this name does not need to be the same as the name of the migration itself.) So, we need to create a new PHP class to contain the source definition.

Create a new file with the path {module root}/src/Plugin/migrate/source/User.php with the following contents:

<?php
/**
 * @file
 * Contains \Drupal\migrate_custom\Plugin\migrate\source\User.
 */
 
namespace Drupal\migrate_custom\Plugin\migrate\source;
 
use Drupal\migrate\Plugin\SourceEntityInterface;
use Drupal\migrate\Row;
use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
 
/**
 * Extract users from Drupal 7 database.
 *
 * @MigrateSource(
 *   id = "custom_user"
 * )
 */
class User extends DrupalSqlBase implements SourceEntityInterface {
 
  /**
   * {@inheritdoc}
   */
  public function query() {
    return $this->select('users', 'u')
      ->fields('u', array_keys($this->baseFields()))
      ->condition('uid', 0, '>');
  }
 
  /**
   * {@inheritdoc}
   */
  public function fields() {
    $fields = $this->baseFields();
    $fields['first_name'] = $this->t('First Name');
    $fields['last_name'] = $this->t('Last Name');
    $fields['biography'] = $this->t('Biography');
    return $fields;
  }
 
  /**
   * {@inheritdoc}
   */
  public function prepareRow(Row $row) {
    $uid = $row->getSourceProperty('uid');
 
    // first_name
    $result = $this->getDatabase()->query('
      SELECT
        fld.field_first_name_value
      FROM
        {field_data_field_first_name} fld
      WHERE
        fld.entity_id = :uid
    ', array(':uid' => $uid));
    foreach ($result as $record) {
      $row->setSourceProperty('first_name', $record->field_first_name_value );
    }
 
    // last_name
    $result = $this->getDatabase()->query('
      SELECT
        fld.field_last_name_value
      FROM
        {field_data_field_last_name} fld
      WHERE
        fld.entity_id = :uid
    ', array(':uid' => $uid));
    foreach ($result as $record) {
      $row->setSourceProperty('last_name', $record->field_last_name_value );
    }
 
    // biography
    $result = $this->getDatabase()->query('
      SELECT
        fld.field_biography_value,
        fld.field_biography_format
      FROM
        {field_data_field_biography} fld
      WHERE
        fld.entity_id = :uid
    ', array(':uid' => $uid));
    foreach ($result as $record) {
      $row->setSourceProperty('biography_value', $record->field_biography_value );
      $row->setSourceProperty('biography_format', $record->field_biography_format );
    }
 
    return parent::prepareRow($row);
  }
 
  /**
   * {@inheritdoc}
   */
  public function getIds() {
    return array(
      'uid' => array(
        'type' => 'integer',
        'alias' => 'u',
      ),
    );
  }
 
  /**
   * Returns the user base fields to be migrated.
   *
   * @return array
   *   Associative array having field name as key and description as value.
   */
  protected function baseFields() {
    $fields = array(
      'uid' => $this->t('User ID'),
      'name' => $this->t('Username'),
      'pass' => $this->t('Password'),
      'mail' => $this->t('Email address'),
      'signature' => $this->t('Signature'),
      'signature_format' => $this->t('Signature format'),
      'created' => $this->t('Registered timestamp'),
      'access' => $this->t('Last access timestamp'),
      'login' => $this->t('Last login timestamp'),
      'status' => $this->t('Status'),
      'timezone' => $this->t('Timezone'),
      'language' => $this->t('Language'),
      'picture' => $this->t('Picture'),
      'init' => $this->t('Init'),
    );
    return $fields;
 
}
 
  /**
   * {@inheritdoc}
   */
  public function bundleMigrationRequired() {
    return FALSE;
  }
 
  /**
   * {@inheritdoc}
   */
  public function entityTypeId() {
    return 'user';
  }
 
}
?>

Pay attention to the docblock immediately preceding the class definition. The lines

 * @MigrateSource(
 *   id = "custom_user"
 * )

set the ID of the plugin. This ID must match the ID we used in the migration definition above. Failure to keep these the same will result in a "source plugin not found" error.

Also noteworthy here are a few required methods:

query() defines the basic query used to retrieve data from Drupal 7's `users` table.

prepareRow() will be called once for each row, at the beginning of processing. Here, we are using it to load the related data from the field tables (first name, last name, and biography). Any property we create using $row->setSourceProperty() will be available in our "process" step. Notice that the biography is slightly different from the other fields, because it is a formatted text field. In addition to the contents, its field table also contains a formatting setting, which we want to import.

baseFields() contains an array of the basic fields within the `users` table. These are used by query() and also are used by the Migrate Upgrade contrib module to describe the fields. The field descriptions are not used by the Drush "migrate-manifest" command.

The destination plugin

The destination: setting in custom_user.yml informs the Migrate module where to store your data. In our case, we are using the "entity:user" plugin, which is built in to the core Migrate module. For importing other content, there are several other built-in destination plugins, such as "entity:node", "entity:user_role", "entity:taxonomy_term", "url_alias", and more. For the complete list, inspect the files in your Drupal 8 site at core/modules/migrate/src/Plugin/migrate/destination.

You can define your own destination plugin if you require, but the built-in ones are sufficient to handle the most common Drupal content.

Process plugins and field mapping

The "process" section contains instructions to map fields from the source to the destination. It also allows for many different types of transformations, such as replacing values, providing a default value, or de-duplicating machine names.

Back in our custom_user.yml file, we now add our process settings:

id: custom_user
source:
  plugin: custom_user
destination:
  plugin: entity:user
process:
  uid: uid
  name: name
  pass: pass
  mail: mail
  status: status
  created: created
  changed: changed
  access: access
  login: login
  timezone: timezone
  langcode: language
  preferred_langcode: language
  preferred_admin_langcode: language
  init: init
  field_first_name: first_name
  field_last_name: last_name
  'field_biography/value': biography_value
  'field_biography/format':
    plugin: static_map
    bypass: true
    source: biography_format
    map:
      1: plain_text
      2: basic_html
      3: full_html
      4: full_html

The simplest process mappings take the form of destination_field: source_field. The source field can be anything your source plugin defines. The destination fields must match the fields available in the destination plugin.

In our example, most of the fields in the source match fields of the same name in the destination. A few fields have been renamed from Drupal 7 to Drupal 8, for example "language" is now "langcode". The process field mappings reflect this.

The Biography field is a special case here. It contains both a value and a format. So, we need to supply values for both of these. Also, the "field_biography_format" field in Drupal 7 contains integers, where in Drupal 8 it contains the machine names of the formats. To convert the old values to the new, we are using the "static_map" process plugin.

If specified in the process field mappings, the UID field will cause migrate to preserve the UIDs in the imported data. This may cause migrate to overwrite existing users. It should be used with care.

The core Migrate module includes several useful process plugins which are not covered here. See the official documentation at https://www.drupal.org/node/2129651 for a complete list. You can also write your own process plugins if your data requires custom processing. Any custom process plugins can be saved in the directory modules/{module name}/src/Plugin/migrate/process.

Uninstall hook

While developing a migration module, you will often need to make changes to your migration definitions. Because these are configuration entities, you will need to reinstall your module for any changes to take effect.

Additionally, since Drupal 8 beta 2, Drupal modules no longer delete their configuration when they are uninstalled. Therefore, you will need to write a quick uninstall hook to handle this.

In the file migrate_custom.module, add the following function:

/**
 * Implements hook_uninstall().
 *
 * Cleans up config entities installed by this module.
 */
function migrate_custom_uninstall() {
  db_query("DELETE FROM {config} WHERE name LIKE 'migrate.migration.custom%'");
  drupal_flush_all_caches();
}

Running the migration using Drush 7

Now, it's time to install our new module and run the migrations. Use Drush or the Admin -> Extend page to enable the migrate_custom module, along with its dependencies, migrate and migrate_drupal.

Next, we need to create a manifest file. Create a file named manifest.yml (the location is not important) with the following contents:

- custom_user

To run the migration, open a command prompt and run the following command:

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

Replace {dbuser}, {dbpass}, and {dbname} with the MySQL username, password, and database name for your Drupal 7 database.

If you prefer, you can try out the Migrate Upgrade module which provides a UI to run migrations.

Log in to your Drupal 8 site, clear the cache, and view the users list. You should see your imported users from the Drupal 7 site.

Next steps

This migration omits a few things for brevity, including user roles and signatures. As an exercise, you could expand the migration to include the "signature" and "signature_format" fields. Migrating the user roles would require a second migration, with its own definition and source plugin.

Bạn thấy bài viết này như thế nào?: 
No votes yet
Ảnh của Tommy Tran

Tommy owner Express Magazine

Drupal Developer having 9+ year experience, implementation and having strong knowledge of technical specifications, workflow development. Ability to perform effectively and efficiently in team and individually. Always enthusiastic and interseted to study new technologies

  • Skype ID: tthanhthuy

Tìm kiếm bất động sản

 

Advertisement

 

jobsora

Dich vu khu trung tphcm

Dich vu diet chuot tphcm

Dich vu diet con trung

Quảng Cáo Bài Viết

 
Nhìn lại chặng đường thống trị thế giới của Facebook

Nhìn lại chặng đường thống trị thế giới của Facebook

Ra đời trong một căn phòng trong ký túc xá Đại học Harvard, sau hơn 10 năm,

Phân tích của một chiến dịch SEO audit bao gồm 5 yếu tố

Phân tích của một chiến dịch SEO audit bao gồm 5 yếu tố

Với tất cả những finding mình khuyến cáo các bạn nên copy đường link của trang đó vào một file excel

Muốn biết ai đang xem trang Facebook của bạn?

Muốn biết ai đang xem trang Facebook của bạn?

Không như những ứng dụng lừa bịp trên Facebook, chẳng hạn như hứa hẹn sẽ cho bạn biết người nào đang xem profile của mình..

Công ty diệt chuột T&C

 

Diet con trung