drupal-migration
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseDrupal 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
undefinedbash
undefinedCore 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
undefinedyaml
undefinedmigrations/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
undefinedid: 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
undefinedKey Components
核心组件
- Source - Where data comes from (D7 database, CSV, JSON API)
- Process - Transform data between source and destination
- Destination - Where data goes (nodes, users, taxonomy terms)
- Source(数据源) - 数据来源(D7数据库、CSV、JSON API)
- Process(处理层) - 在数据源与目标端之间转换数据
- 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_uibash
drush en migrate_drupal_uiVisit /upgrade to use wizard
访问 /upgrade 路径使用向导
undefinedundefinedUsing Drush (Recommended)
使用Drush(推荐方式)
bash
undefinedbash
undefinedGenerate 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
undefineddrush migrate:rollback upgrade_d7_node_article
undefinedCommon D7 Migration Customizations
D7迁移常见自定义配置
Override generated migrations with custom YAML:
yaml
undefined通过自定义YAML覆盖自动生成的迁移配置:
yaml
undefinedmigrations/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
undefinedid: 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
undefinedCSV Migrations
CSV迁移
Source Plugin Configuration
源插件配置
yaml
undefinedyaml
undefinedmigrations/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
undefinedid: 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
undefinedRunning CSV Migrations
运行CSV迁移
bash
undefinedbash
undefinedImport
执行导入
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
undefineddrush migrate:reset-status import_products
undefinedJSON/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: 200yaml
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: 200Entity 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: nameyaml
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: nameMultiple 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: nameyaml
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: nameConditional 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
undefinedbash
undefinedList 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
undefineddrush migrate:messages migration_id
undefinedDebugging Migrations
迁移调试
Enable Verbose Output
启用详细输出
bash
drush migrate:import migration_id -vvvbash
drush migrate:import migration_id -vvvCheck Migration Status
检查迁移状态
yaml
undefinedyaml
undefinedAdd to migration YAML
添加到迁移YAML配置中
migration_tags:
- debug
undefinedmigration_tags:
- debug
undefinedLog 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_idMemory 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=500Process in batches
分批处理数据
**Missing dependencies:**
Check `migration_dependencies` in YAML matches actual migration IDs.
**依赖缺失:**
检查YAML配置中的`migration_dependencies`是否与实际迁移任务ID匹配。Best Practices
最佳实践
- Always use migration groups to organize related migrations
- Set migration_dependencies to ensure correct order
- Test with before full import
--limit=10 - Use for re-running updated migrations
--update - Keep source data until migration is verified
- Document field mappings in migration YAML comments
- Create rollback plan before production migration
- Monitor memory usage for large migrations
- 始终使用迁移分组来管理相关迁移任务
- 设置迁移依赖以确保任务执行顺序正确
- 先使用测试,再执行完整导入
--limit=10 - 使用参数重新运行已更新的迁移任务
--update - 保留源数据直到迁移结果验证通过
- 在迁移YAML中添加注释记录字段映射关系
- 在生产迁移前制定回滚方案
- 针对大型迁移任务监控内存使用情况
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.csvmy_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