drupal-migration

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Drupal Migration Expert

Drupal迁移专家

You are an expert in Drupal's Migrate API, helping with D7 to D10/D11 migrations, CSV imports, and custom data migrations.
您是Drupal Migrate API领域的专家,可提供D7到D10/D11迁移、CSV导入及自定义数据迁移相关帮助。

Essential Modules

必备模块

bash
undefined
bash
undefined

Core migration modules

Core migration modules

drush en migrate migrate_drupal migrate_drupal_ui
drush en migrate migrate_drupal migrate_drupal_ui

Contrib essentials

Contrib essentials

composer require drupal/migrate_plus drupal/migrate_tools drupal/migrate_file drush en migrate_plus migrate_tools migrate_file

| Module | Purpose |
|--------|---------|
| `migrate` | Core migration framework |
| `migrate_drupal` | D6/D7 migration support |
| `migrate_drupal_ui` | Browser-based migration wizard |
| `migrate_plus` | Config-based migrations, extra source plugins |
| `migrate_tools` | Drush commands for migrations |
| `migrate_file` | File migration handling |
composer require drupal/migrate_plus drupal/migrate_tools drupal/migrate_file drush en migrate_plus migrate_tools migrate_file

| 模块 | 用途 |
|--------|---------|
| `migrate` | 核心迁移框架 |
| `migrate_drupal` | 支持D6/D7版本迁移 |
| `migrate_drupal_ui` | 基于浏览器的迁移向导 |
| `migrate_plus` | 基于配置的迁移、扩展源插件 |
| `migrate_tools` | 用于迁移的Drush命令 |
| `migrate_file` | 文件迁移处理 |

Migration Architecture

迁移架构

Migration YAML Structure

迁移YAML结构

yaml
undefined
yaml
undefined

migrations/migrate_plus.migration.my_migration.yml

migrations/migrate_plus.migration.my_migration.yml

id: my_migration label: 'My Migration' migration_group: my_group
source: plugin: source_plugin_name

Source configuration

process:

Field mappings

destination_field: source_field
destination: plugin: 'entity:node' default_bundle: article
migration_dependencies: required: - other_migration
undefined
id: my_migration label: 'My Migration' migration_group: my_group
source: plugin: source_plugin_name

Source configuration

process:

Field mappings

destination_field: source_field
destination: plugin: 'entity:node' default_bundle: article
migration_dependencies: required: - other_migration
undefined

Key Components

核心组件

  1. Source - Where data comes from (D7 database, CSV, JSON API)
  2. Process - Transform data between source and destination
  3. Destination - Where data goes (nodes, users, taxonomy terms)
  1. Source(数据源) - 数据来源(D7数据库、CSV、JSON API)
  2. Process(处理层) - 在数据源与目标端之间转换数据
  3. Destination(目标端) - 数据存储位置(节点、用户、分类术语)

D7 to D10 Migration

D7到D10迁移

Setup Database Connection

配置数据库连接

php
// settings.php
$databases['migrate']['default'] = [
  'driver' => 'mysql',
  'database' => 'drupal7_db',
  'username' => 'db_user',
  'password' => 'db_pass',
  'host' => 'localhost',
  'prefix' => '',
];
php
// settings.php
$databases['migrate']['default'] = [
  'driver' => 'mysql',
  'database' => 'drupal7_db',
  'username' => 'db_user',
  'password' => 'db_pass',
  'host' => 'localhost',
  'prefix' => '',
];

Using the UI

使用UI界面

bash
drush en migrate_drupal_ui
bash
drush en migrate_drupal_ui

Visit /upgrade to use wizard

访问 /upgrade 路径使用向导

undefined
undefined

Using Drush (Recommended)

使用Drush(推荐方式)

bash
undefined
bash
undefined

Generate migrations from D7

从D7生成迁移配置

drush migrate:upgrade --legacy-db-key=migrate --configure-only
drush migrate:upgrade --legacy-db-key=migrate --configure-only

List generated migrations

列出已生成的迁移任务

drush migrate:status
drush migrate:status

Run all migrations

运行所有迁移任务

drush migrate:import --all
drush migrate:import --all

Run specific migration

运行指定迁移任务

drush migrate:import upgrade_d7_node_article
drush migrate:import upgrade_d7_node_article

Rollback

回滚迁移

drush migrate:rollback upgrade_d7_node_article
undefined
drush migrate:rollback upgrade_d7_node_article
undefined

Common D7 Migration Customizations

D7迁移常见自定义配置

Override generated migrations with custom YAML:
yaml
undefined
通过自定义YAML覆盖自动生成的迁移配置:
yaml
undefined

migrations/migrate_plus.migration.upgrade_d7_node_article.yml

migrations/migrate_plus.migration.upgrade_d7_node_article.yml

id: upgrade_d7_node_article label: 'Article nodes from D7' migration_group: migrate_drupal_7
source: plugin: d7_node node_type: article
process: type: plugin: default_value default_value: article title: title uid: plugin: migration_lookup migration: upgrade_d7_user source: uid body: plugin: sub_process source: body process: value: value format: plugin: static_map source: format map: full_html: full_html filtered_html: basic_html default_value: basic_html field_image: plugin: migration_lookup migration: upgrade_d7_file source: field_image/0/fid
destination: plugin: 'entity:node' default_bundle: article
migration_dependencies: required: - upgrade_d7_user - upgrade_d7_file
undefined
id: upgrade_d7_node_article label: 'Article nodes from D7' migration_group: migrate_drupal_7
source: plugin: d7_node node_type: article
process: type: plugin: default_value default_value: article title: title uid: plugin: migration_lookup migration: upgrade_d7_user source: uid body: plugin: sub_process source: body process: value: value format: plugin: static_map source: format map: full_html: full_html filtered_html: basic_html default_value: basic_html field_image: plugin: migration_lookup migration: upgrade_d7_file source: field_image/0/fid
destination: plugin: 'entity:node' default_bundle: article
migration_dependencies: required: - upgrade_d7_user - upgrade_d7_file
undefined

CSV Migrations

CSV迁移

Source Plugin Configuration

源插件配置

yaml
undefined
yaml
undefined

migrations/migrate_plus.migration.import_products.yml

migrations/migrate_plus.migration.import_products.yml

id: import_products label: 'Import products from CSV' migration_group: imports
source: plugin: csv path: 'modules/custom/my_module/data/products.csv' ids: - sku header_row_count: 1

Optionally define columns explicitly

column_names: 0: sku: 'Product SKU' 1: name: 'Product Name' 2: price: 'Price' 3: category: 'Category'
process: type: plugin: default_value default_value: product title: name field_sku: sku field_price: price field_category: plugin: entity_lookup source: category entity_type: taxonomy_term bundle: product_categories bundle_key: vid value_key: name
destination: plugin: 'entity:node' default_bundle: product
undefined
id: import_products label: 'Import products from CSV' migration_group: imports
source: plugin: csv path: 'modules/custom/my_module/data/products.csv' ids: - sku header_row_count: 1

可选:显式定义列信息

column_names: 0: sku: 'Product SKU' 1: name: 'Product Name' 2: price: 'Price' 3: category: 'Category'
process: type: plugin: default_value default_value: product title: name field_sku: sku field_price: price field_category: plugin: entity_lookup source: category entity_type: taxonomy_term bundle: product_categories bundle_key: vid value_key: name
destination: plugin: 'entity:node' default_bundle: product
undefined

Running CSV Migrations

运行CSV迁移

bash
undefined
bash
undefined

Import

执行导入

drush migrate:import import_products
drush migrate:import import_products

Update existing records

更新已有记录

drush migrate:import import_products --update
drush migrate:import import_products --update

Reset status if stuck

重置卡住的迁移状态

drush migrate:reset-status import_products
undefined
drush migrate:reset-status import_products
undefined

JSON/API Migrations

JSON/API迁移

HTTP JSON Source

HTTP JSON数据源

yaml
id: import_api_users
label: 'Import users from API'

source:
  plugin: url
  data_fetcher_plugin: http
  data_parser_plugin: json
  urls:
    - 'https://api.example.com/users'
  item_selector: data
  ids:
    id:
      type: integer
  fields:
    - name: id
      selector: id
    - name: email
      selector: email
    - name: full_name
      selector: attributes/name

process:
  name: full_name
  mail: email
  init: email
  status:
    plugin: default_value
    default_value: 1

destination:
  plugin: 'entity:user'
yaml
id: import_api_users
label: 'Import users from API'

source:
  plugin: url
  data_fetcher_plugin: http
  data_parser_plugin: json
  urls:
    - 'https://api.example.com/users'
  item_selector: data
  ids:
    id:
      type: integer
  fields:
    - name: id
      selector: id
    - name: email
      selector: email
    - name: full_name
      selector: attributes/name

process:
  name: full_name
  mail: email
  init: email
  status:
    plugin: default_value
    default_value: 1

destination:
  plugin: 'entity:user'

Common Process Plugins

常用处理插件

Basic Transformations

基础转换

yaml
process:
  # Direct mapping
  title: source_title

  # Default value
  status:
    plugin: default_value
    default_value: 1

  # Static mapping
  field_type:
    plugin: static_map
    source: type
    map:
      old_type_1: new_type_1
      old_type_2: new_type_2
    default_value: default_type

  # Concatenate
  title:
    plugin: concat
    source:
      - first_name
      - last_name
    delimiter: ' '

  # Substring
  field_summary:
    plugin: substr
    source: body
    start: 0
    length: 200
yaml
process:
  # 直接映射
  title: source_title

  # 默认值
  status:
    plugin: default_value
    default_value: 1

  # 静态映射
  field_type:
    plugin: static_map
    source: type
    map:
      old_type_1: new_type_1
      old_type_2: new_type_2
    default_value: default_type

  # 字符串拼接
  title:
    plugin: concat
    source:
      - first_name
      - last_name
    delimiter: ' '

  # 字符串截取
  field_summary:
    plugin: substr
    source: body
    start: 0
    length: 200

Entity References

实体引用

yaml
process:
  # Migration lookup (referenced entity was migrated)
  uid:
    plugin: migration_lookup
    migration: users
    source: author_id

  # Entity lookup (entity already exists)
  field_category:
    plugin: entity_lookup
    source: category_name
    entity_type: taxonomy_term
    bundle: categories
    bundle_key: vid
    value_key: name

  # Entity generate (create if not exists)
  field_tags:
    plugin: entity_generate
    source: tags
    entity_type: taxonomy_term
    bundle: tags
    bundle_key: vid
    value_key: name
yaml
process:
  # 迁移查找(引用的实体已完成迁移)
  uid:
    plugin: migration_lookup
    migration: users
    source: author_id

  # 实体查找(实体已存在)
  field_category:
    plugin: entity_lookup
    source: category_name
    entity_type: taxonomy_term
    bundle: categories
    bundle_key: vid
    value_key: name

  # 实体生成(不存在则创建)
  field_tags:
    plugin: entity_generate
    source: tags
    entity_type: taxonomy_term
    bundle: tags
    bundle_key: vid
    value_key: name

Multiple Values

多值处理

yaml
process:
  # Handle multiple values
  field_tags:
    plugin: sub_process
    source: tags
    process:
      target_id:
        plugin: entity_generate
        source: name
        entity_type: taxonomy_term
        bundle: tags
        value_key: name

  # Explode string to array
  field_keywords:
    - plugin: explode
      source: keywords
      delimiter: ','
    - plugin: entity_generate
      entity_type: taxonomy_term
      bundle: keywords
      value_key: name
yaml
process:
  # 处理多值字段
  field_tags:
    plugin: sub_process
    source: tags
    process:
      target_id:
        plugin: entity_generate
        source: name
        entity_type: taxonomy_term
        bundle: tags
        value_key: name

  # 将字符串拆分为数组
  field_keywords:
    - plugin: explode
      source: keywords
      delimiter: ','
    - plugin: entity_generate
      entity_type: taxonomy_term
      bundle: keywords
      value_key: name

Conditional Processing

条件处理

yaml
process:
  # Skip if empty
  field_image:
    plugin: skip_on_empty
    method: process
    source: image_url

  # Skip row if condition
  pseudo_skip:
    plugin: skip_on_value
    source: status
    method: row
    value: 'draft'
yaml
process:
  # 为空则跳过处理
  field_image:
    plugin: skip_on_empty
    method: process
    source: image_url

  # 满足条件则跳过整行
  pseudo_skip:
    plugin: skip_on_value
    source: status
    method: row
    value: 'draft'

Custom Source Plugin

自定义源插件

php
<?php

declare(strict_types=1);

namespace Drupal\my_module\Plugin\migrate\source;

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

/**
 * Custom source for legacy products.
 */
#[MigrateSource(
  id: 'legacy_products',
  source_module: 'my_module',
)]
class LegacyProducts extends SqlBase {

  /**
   * {@inheritdoc}
   */
  public function query() {
    $query = $this->select('legacy_products', 'p');
    $query->fields('p', ['id', 'name', 'price', 'description']);
    $query->condition('p.status', 'active');
    $query->orderBy('p.id');
    return $query;
  }

  /**
   * {@inheritdoc}
   */
  public function fields() {
    return [
      'id' => $this->t('Product ID'),
      'name' => $this->t('Product name'),
      'price' => $this->t('Price'),
      'description' => $this->t('Description'),
    ];
  }

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

  /**
   * {@inheritdoc}
   */
  public function prepareRow(Row $row) {
    // Add computed fields or modify data
    $price = $row->getSourceProperty('price');
    $row->setSourceProperty('price_with_tax', $price * 1.21);

    return parent::prepareRow($row);
  }

}
php
<?php

declare(strict_types=1);

namespace Drupal\my_module\Plugin\migrate\source;

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

/**
 * Custom source for legacy products.
 */
#[MigrateSource(
  id: 'legacy_products',
  source_module: 'my_module',
)]
class LegacyProducts extends SqlBase {

  /**
   * {@inheritdoc}
   */
  public function query() {
    $query = $this->select('legacy_products', 'p');
    $query->fields('p', ['id', 'name', 'price', 'description']);
    $query->condition('p.status', 'active');
    $query->orderBy('p.id');
    return $query;
  }

  /**
   * {@inheritdoc}
   */
  public function fields() {
    return [
      'id' => $this->t('Product ID'),
      'name' => $this->t('Product name'),
      'price' => $this->t('Price'),
      'description' => $this->t('Description'),
    ];
  }

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

  /**
   * {@inheritdoc}
   */
  public function prepareRow(Row $row) {
    // 添加计算字段或修改数据
    $price = $row->getSourceProperty('price');
    $row->setSourceProperty('price_with_tax', $price * 1.21);

    return parent::prepareRow($row);
  }

}

Custom Process Plugin

自定义处理插件

php
<?php

declare(strict_types=1);

namespace Drupal\my_module\Plugin\migrate\process;

use Drupal\migrate\Attribute\MigrateProcess;
use Drupal\migrate\MigrateExecutableInterface;
use Drupal\migrate\ProcessPluginBase;
use Drupal\migrate\Row;

/**
 * Converts price from cents to decimal.
 *
 * Example usage:
 * @code
 * process:
 *   field_price:
 *     plugin: cents_to_decimal
 *     source: price_cents
 * @endcode
 */
#[MigrateProcess(id: 'cents_to_decimal')]
class CentsToDecimal extends ProcessPluginBase {

  /**
   * {@inheritdoc}
   */
  public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
    if (empty($value) || !is_numeric($value)) {
      return NULL;
    }

    return number_format((float) $value / 100, 2, '.', '');
  }

}
php
<?php

declare(strict_types=1);

namespace Drupal\my_module\Plugin\migrate\process;

use Drupal\migrate\Attribute\MigrateProcess;
use Drupal\migrate\MigrateExecutableInterface;
use Drupal\migrate\ProcessPluginBase;
use Drupal\migrate\Row;

/**
 * Converts price from cents to decimal.
 *
 * Example usage:
 * @code
 * process:
 *   field_price:
 *     plugin: cents_to_decimal
 *     source: price_cents
 * @endcode
 */
#[MigrateProcess(id: 'cents_to_decimal')]
class CentsToDecimal extends ProcessPluginBase {

  /**
   * {@inheritdoc}
   */
  public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
    if (empty($value) || !is_numeric($value)) {
      return NULL;
    }

    return number_format((float) $value / 100, 2, '.', '');
  }

}

Drush Commands Reference

Drush命令参考

bash
undefined
bash
undefined

List all migrations

列出所有迁移任务

drush migrate:status
drush migrate:status

Run migration

运行指定迁移任务

drush migrate:import migration_id
drush migrate:import migration_id

Run with options

带参数运行迁移

drush migrate:import migration_id --limit=100 drush migrate:import migration_id --update drush migrate:import migration_id --sync
drush migrate:import migration_id --limit=100 drush migrate:import migration_id --update drush migrate:import migration_id --sync

Rollback

回滚迁移

drush migrate:rollback migration_id
drush migrate:rollback migration_id

Reset stuck migration

重置卡住的迁移任务状态

drush migrate:reset-status migration_id
drush migrate:reset-status migration_id

Stop running migration

停止正在运行的迁移

drush migrate:stop migration_id
drush migrate:stop migration_id

Show messages/errors

查看迁移消息/错误

drush migrate:messages migration_id
undefined
drush migrate:messages migration_id
undefined

Debugging Migrations

迁移调试

Enable Verbose Output

启用详细输出

bash
drush migrate:import migration_id -vvv
bash
drush migrate:import migration_id -vvv

Check Migration Status

检查迁移状态

yaml
undefined
yaml
undefined

Add to migration YAML

添加到迁移YAML配置中

migration_tags:
  • debug
undefined
migration_tags:
  • debug
undefined

Log Process Results

记录处理结果

php
// In custom process plugin
\Drupal::logger('my_migration')->notice('Processing: @value', ['@value' => $value]);
php
// 在自定义处理插件中添加
\Drupal::logger('my_migration')->notice('Processing: @value', ['@value' => $value]);

Common Issues

常见问题

"Migration is busy":
bash
drush migrate:reset-status migration_id
Memory errors:
bash
drush migrate:import migration_id --limit=500
"Migration is busy"(迁移任务繁忙):
bash
drush migrate:reset-status migration_id
内存错误:
bash
drush migrate:import migration_id --limit=500

Process in batches

分批处理数据


**Missing dependencies:**
Check `migration_dependencies` in YAML matches actual migration IDs.

**依赖缺失:**
检查YAML配置中的`migration_dependencies`是否与实际迁移任务ID匹配。

Best Practices

最佳实践

  1. Always use migration groups to organize related migrations
  2. Set migration_dependencies to ensure correct order
  3. Test with
    --limit=10
    before full import
  4. Use
    --update
    for re-running updated migrations
  5. Keep source data until migration is verified
  6. Document field mappings in migration YAML comments
  7. Create rollback plan before production migration
  8. Monitor memory usage for large migrations
  1. 始终使用迁移分组来管理相关迁移任务
  2. 设置迁移依赖以确保任务执行顺序正确
  3. 先使用
    --limit=10
    测试
    ,再执行完整导入
  4. 使用
    --update
    参数
    重新运行已更新的迁移任务
  5. 保留源数据直到迁移结果验证通过
  6. 在迁移YAML中添加注释记录字段映射关系
  7. 在生产迁移前制定回滚方案
  8. 针对大型迁移任务监控内存使用情况

Migration Module Structure

迁移模块结构

my_migration/
├── my_migration.info.yml
├── my_migration.module
├── config/
│   └── install/
│       ├── migrate_plus.migration_group.my_group.yml
│       ├── migrate_plus.migration.users.yml
│       └── migrate_plus.migration.content.yml
├── src/
│   └── Plugin/
│       └── migrate/
│           ├── source/
│           │   └── LegacyProducts.php
│           └── process/
│               └── CentsToDecimal.php
└── data/
    └── import.csv
my_migration/
├── my_migration.info.yml
├── my_migration.module
├── config/
│   └── install/
│       ├── migrate_plus.migration_group.my_group.yml
│       ├── migrate_plus.migration.users.yml
│       └── migrate_plus.migration.content.yml
├── src/
│   └── Plugin/
│       └── migrate/
│           ├── source/
│           │   └── LegacyProducts.php
│           └── process/
│               └── CentsToDecimal.php
└── data/
    └── import.csv

Sources

参考资料