[僅限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索引資料表中移轉不必要的資料。

  • 資料表great_blog_publication已在Magento 2中重新命名為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\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問題,並要求現有步驟上的新擴充點。

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