[仅限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表,包括由扩展创建的表。 如果表相同,则该工具会自动迁移数据。 如果表不同,工具将终止并通知用户。

NOTE
在尝试扩展之前,请阅读技术规范Data Migration Tool。 另外,有关使用迁移工具的一般信息,请查看迁移指南

较小的数据格式和结构更改

在大多数情况下,映射步骤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.xmlmap-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最后运行。

步骤可以包含四种类型的类:

  • 完整性检查
  • 数据传送
  • 音量检查
  • 增量投放
NOTE
有关详细信息,请参阅配置内部步骤阶段运行模式

可以在这些类中组合复杂的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>

然后,创建扩展DeltaVendor\Migration\Step\GreatBlog\DeltaMigration\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问题并请求现有步骤上的新扩展点。

recommendation-more-help
c2d96e17-5179-455c-ad3a-e1697bb4e8c3