[仅限PaaS]{class="badge informative" title="仅适用于云项目(Adobe管理的PaaS基础架构)和内部部署项目上的Adobe Commerce 。"}
配置Data Migration Tool
有时,Magento 1与Magento 2之间由扩展或自定义代码创建的数据格式和结构会有所不同。 使用Data Migration Tool中的扩展点迁移此数据。 如果数据格式和结构相同,则该工具可以自动迁移数据,而无需用户干预。
在迁移过程中,Map Step扫描并比较所有Magento 1和Magento 2表,包括由扩展创建的表。 如果表相同,则该工具会自动迁移数据。 如果表不同,工具将终止并通知用户。
较小的数据格式和结构更改
在大多数情况下,映射步骤在map.xml文件中使用以下方法足以解析次要数据格式和结构更改:
- 使用映射规则更改表或字段名称
- 使用现有处理程序或自定义处理程序转换数据格式
下面显示了同时使用映射规则和处理程序的示例。 此示例使用名为“GreatBlog”的假定Magento 1扩展,该扩展已在Magento 2中进行了改进。
<source>
<document_rules>
<ignore>
<document>great_blog_index</document>
</ignore>
<rename>
<document>great_blog_publication</document>
<to>great_blog_post</to>
</rename>
</document_rules>
<field_rules>
<move>
<field>great_blog_publication.summary</field>
<to>great_blog_post.title</to>
</move>
<ignore>
<field>great_blog_publication.priority</field>
</ignore>
<transform>
<field>great_blog_publication.body</field>
<handler class="\Migration\Handler\GreatBlog\NewFormat">
<param name="switch" value="yes" />
</handler>
</transform>
</field_rules>
</source>
<destination>
<document_rules>
<ignore>
<document>great_blog_rating</document>
</ignore>
</document_rules>
<field_rules>
<ignore>
<field>great_blog_post.rating</field>
</ignore>
</field_rules>
</destination>
-
不要从
great_blog_index索引表中迁移不必要的数据。 -
在Magento 2中,表
great_blog_publication被重命名为great_blog_post,因此数据将迁移到新表。summary字段已重命名为title,因此数据将迁移到新字段。priority字段已删除,在Magento 2中不再存在。body字段中的数据已更改格式,应由自定义处理程序\Migration\Handler\GreatBlog\NewFormat处理。
-
在Magento 2中,为“GreatBlog”扩展开发了一个新的评级功能。
- 已创建新的
great_blog_rating表。 - 已创建新的
great_blog_post.rating字段。
- 已创建新的
在其他步骤中扩展映射
其他步骤支持映射,例如EAV步骤和客户属性步骤。 这些步骤将迁移预定义的Magento表列表。 例如,假设“GreatBlog”扩展在eav_attribute表中有一个额外的字段,并且名称在Magento 2中发生了更改。 由于表由EAV Step处理,因此应为map-eav.xml文件编写映射规则。 map.xml和map-eav.xml文件使用相同的map.xsd架构,因此映射规则保持不变。
主要数据格式和结构更改
除了映射步骤之外,config.xml文件中还有其他一些步骤,用于迁移具有主要格式和结构更改的数据,包括:
与映射步骤不同,这些步骤扫描的是预定义的表列表,而不是所有表。
对于主要的数据格式和结构更改,请创建一个自定义步骤。
创建自定义步骤
使用相同的“GreatBlog”示例,假设扩展在Magento 1中有一个表,但重新设计为在Magento 2中有两个表。
在Magento 1中,只有一个greatblog_post表:
| Field | Type |
|-----------|----------|
| post_id | INT |
| title | VARCHAR |
| content | TEXT |
| author_id | SMALLINT |
| tags | TEXT |
在Magento 2中,为标记greatblog_post_tags引入了新表:
| Field | Type |
|------------|----------|
| post_id | INT |
| tag | VARCHAR |
| sort_order | SMALLINT |
Magento 2 greatblog_post表现在看起来像这样:
| Field | Type |
|-----------|----------|
| post_id | INT |
| title | VARCHAR |
| content | TEXT |
| author_id | SMALLINT |
要将所有数据从旧表结构迁移到新表结构,您可以在config.xml文件中创建一个自定义步骤。 例如:
<steps mode="data">
...
<step title="GreatBlog Step">
<integrity>Vendor\Migration\Step\GreatBlog\Integrity</integrity>
<data>Vendor\Migration\Step\GreatBlog\Data</data>
<volume>Vendor\Migration\Step\GreatBlog\Volume</volume>
</step>
</steps>
<steps mode="delta">
...
<step title="GreatBlog Step">
<delta>Vendor\Migration\Step\GreatBlog\Delta</delta>
<volume>Vendor\Migration\Step\GreatBlog\Volume</volume>
</step>
</steps>
该工具根据步骤在config.xml文件中的位置运行步骤;从上到下。 在我们的示例中,GreatBlog Step最后运行。
步骤可以包含四种类型的类:
- 完整性检查
- 数据传送
- 音量检查
- 增量投放
可以在这些类中组合复杂的SQL查询,以获取和迁移数据。 此外,这些表应在映射步骤中“忽略”,因为它扫描所有现有表并尝试迁移数据,除非它位于<ignore>文件的map.xml标记中。
对于完整性检查,在config.xml文件中定义一个附加的映射文件,以验证表结构是否符合我们的预期。
<config xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
xs:noNamespaceSchemaLocation="urn:magento:module:Magento_DataMigrationTool:etc/config.xsd">
...
<options>
...
<greatblog_map_file>app/code/Vendor/Migration/etc/opensource-to-opensource/map-greatblog.xml</greatblog_map_file>
...
</options>
</config>
映射文件map-greatblog.xml:
<map xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
xs:noNamespaceSchemaLocation="urn:magento:module:Magento_DataMigrationTool:etc/map.xsd">
<source>
<field_rules>
<ignore>
<field>greatblog_post.tags</field>
</ignore>
</field_rules>
</source>
<destination>
<document_rules>
<ignore>
<document>greatblog_post_tags</document>
</ignore>
</document_rules>
</destination>
</map>
完整性检查类Vendor\Migration\Step\GreatBlog\Integrity扩展Migration\App\Step\AbstractIntegrity并包含我们验证表结构的perform方法:
class Integrity extends \Migration\App\Step\AbstractIntegrity
{
...
/**
* Integrity constructor.
* @param ProgressBar\LogLevelProcessor $progress
* @param Logger $logger
* @param Config $config
* @param ResourceModel\Source $source
* @param ResourceModel\Destination $destination
* @param MapFactory $mapFactory
* @param string $mapConfigOption
*/
public function __construct(
ProgressBar\LogLevelProcessor $progress,
Logger $logger,
Config $config,
ResourceModel\Source $source,
ResourceModel\Destination $destination,
MapFactory $mapFactory,
$mapConfigOption = 'greatblog_map_file'
) {
parent::__construct($progress, $logger, $config, $source, $destination, $mapFactory, $mapConfigOption);
}
/**
* @inheritDoc
*/
public function perform()
{
$this->progress->start($this->getIterationsCount());
$this->check(['greatblog_post'], MapInterface::TYPE_SOURCE);
$this->check(['greatblog_post', 'greatblog_post_tags'], MapInterface::TYPE_DEST);
$this->progress->finish();
return $this->checkForErrors();
}
...
}
接下来,必须创建一个类来处理数据并将其保存到Magento 2数据库Vendor\Migration\Step\GreatBlog\Data:
class Data implements \Migration\App\Step\StageInterface
{
...
/**
* Data constructor.
*
* @param ProgressBar\LogLevelProcessor $progress
* @param ResourceModel\Source $source
* @param ResourceModel\Destination $destination
* @param ResourceModel\RecordFactory $recordFactory
* @param RecordTransformerFactory $recordTransformerFactory
* @param MapFactory $mapFactory
*/
public function __construct(
ProgressBar\LogLevelProcessor $progress,
ResourceModel\Source $source,
ResourceModel\Destination $destination,
ResourceModel\RecordFactory $recordFactory,
RecordTransformerFactory $recordTransformerFactory,
MapFactory $mapFactory
) {
$this->progress = $progress;
$this->destination = $destination;
$this->recordFactory = $recordFactory;
$this->source = $source;
$this->recordTransformerFactory = $recordTransformerFactory;
$this->map = $mapFactory->create('greatblog_map_file');
}
/**
* @inheritDoc
*/
public function perform()
{
$sourceDocName = 'greatblog_post';
$sourceDocument = $this->source->getDocument($sourceDocName);
$destinationDocName = 'greatblog_post';
$destinationDocument = $this->destination->getDocument($destinationDocName);
/** @var \Migration\RecordTransformer $recordTransformer */
$recordTransformer = $this->recordTransformerFactory->create(
[
'sourceDocument' => $sourceDocument,
'destDocument' => $destinationDocument,
'mapReader' => $this->map
]
);
$recordTransformer->init();
$this->progress->start($this->source->getRecordsCount($sourceDocName));
$pageNumber = 0;
while (!empty($items = $this->source->getRecords($sourceDocName, $pageNumber))) {
$pageNumber++;
$recordsToSave = $destinationDocument->getRecords();
foreach ($items as $item) {
$sourceRecord = $this->recordFactory->create(
['document' => $sourceDocument, 'data' => $item]
);
$destinationRecord = $this->recordFactory->create(['document' => $destinationDocument]);
$recordTransformer->transform($sourceRecord, $destinationRecord);
$recordsToSave->addRecord($destinationRecord);
}
$this->destination->saveRecords($destinationDocName, $recordsToSave);
$tags = $this->getTags($items);
$this->destination->saveRecords('greatblog_post_tags', $tags);
$this->progress->advance();
}
$this->progress->finish();
return true;
}
...
}
在Volume类Vendor\Migration\Step\GreatBlog\Volume中,我们检查数据是否已完全迁移:
class Volume extends \Migration\App\Step\AbstractVolume
{
...
/**
* @inheritdoc
*/
public function perform()
{
$documentName = 'greatblog_post';
$sourceCount = $this->source->getRecordsCount($documentName);
$destinationCount = $this->destination->getRecordsCount($documentName);
if ($sourceCount != $destinationCount) {
$this->errors[] = sprintf(
'Mismatch of entities in the document: %s Source: %s Destination: %s',
$documentName,
$sourceCount,
$destinationCount
);
}
return $this->checkForErrors(Logger::ERROR);
}
...
}
要添加增量迁移功能,请将新组添加到deltalog.xml文件中。 在group中,指定必须检查更改的表的名称:
<groups>
...
<group name="delta_greatblog">
<document key="post_id">greatblog_post</document>
</group>
</groups>
然后,创建扩展Delta的Vendor\Migration\Step\GreatBlog\Delta类Migration\App\Step\AbstractDelta:
class Delta extends \Migration\App\Step\AbstractDelta
{
/**
* @var string
*/
protected $mapConfigOption = 'greatblog_map_file';
/**
* @var string
*/
protected $groupName = 'delta_greatblog';
/**
* @inheritDoc
*/
public function perform()
{
$sourceDocumentName = 'greatblog_post';
$idKeys = ['post_id'];
$page = 0;
while (!empty($items = $this->source->getChangedRecords($sourceDocumentName, $idKeys, $page++))) {
$this->destination->deleteRecords(
'greatblog_post_tags',
$idKeys,
$items
);
$tags = $this->getTags($items);
$this->destination->saveRecords('greatblog_post_tags', $tags);
}
//parent class takes care of greatblog_post records automatically
return parent::perform();
}
}
在示例中提供的自定义步骤实施之后,系统从单个Magento 1表中获取数据,
使用Vendor\Migration\Step\GreatBlog\Data类对其进行处理并将数据存储在两个Magento 2表中。 新记录和更改记录将通过Vendor\Migration\Step\GreatBlog\Delta类在增量迁移中传递。
禁止的扩展方法
由于Data Migration Tool和Magento 2在不断发展,因此现有步骤和处理程序可能会发生更改。 我们强烈建议不要通过扩展步骤映射步骤、URL重写步骤和处理程序的类来覆盖这些步骤的行为。
某些步骤不支持映射,因此不能在不更改代码的情况下更改这些步骤。 您可以在迁移结束时编写一个更改数据的额外步骤,或者创建GitHub问题并请求现有步骤上的新扩展点。