Configurar o Data Migration Tool

Às vezes, o formato e a estrutura dos dados criados por extensões ou código personalizado são diferentes entre Magento 1 e Magento 2. Use pontos de extensão no Data Migration Tool para migrar esses dados. Se o formato e a estrutura dos dados forem iguais, a ferramenta poderá migrar os dados automaticamente sem a intervenção do usuário.

Durante a migração, a Etapa do mapa verifica e compara todas as tabelas de Magento 1 e Magento 2, incluindo aquelas criadas por extensões. Se as tabelas forem iguais, a ferramenta migrará os dados automaticamente. Se as tabelas forem diferentes, a ferramenta será finalizada e notificará o usuário.

NOTE
Leia a Especificação Técnica antes de tentar estender o Data Migration Tool. Além disso, consulte o Guia de Migração para obter informações gerais sobre o uso da ferramenta de migração.

Pequenas alterações no formato de dados e na estrutura

Na maioria dos casos, a Etapa do mapa resolve suficientemente pequenas alterações de estrutura e formato de dados usando os seguintes métodos no arquivo map.xml:

  • Alterar nomes de tabela ou campo com regras de mapeamento
  • Transformar formatos de dados com manipuladores existentes ou um manipulador personalizado

O exemplo a seguir mostra como usar as regras de mapeamento e um manipulador. Este exemplo usa uma extensão Magento 1 hipotética chamada "GreatBlog" que foi aprimorada para 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>
  • Não migre dados desnecessários da tabela de índice great_blog_index.

  • A tabela great_blog_publication foi renomeada para great_blog_post na Magento 2, portanto, os dados são migrados para a nova tabela.

    • O campo summary foi renomeado para title, portanto, os dados são migrados para o novo campo.
    • O campo priority foi removido e não existe mais na Magento 2.
    • Os dados no campo body foram alterados e devem ser processados pelo manipulador personalizado: \Migration\Handler\GreatBlog\NewFormat.
  • Um novo recurso de classificação foi desenvolvido para a extensão "GreatBlog" no Magento 2.

    • Uma nova tabela great_blog_rating foi criada.
    • Um novo campo great_blog_post.rating foi criado.

Estender mapeamento em outras etapas

Outras etapas oferecem suporte ao mapeamento, como a Etapa EAV e a Etapa de Atributos do Cliente. Essas etapas migram uma lista predefinida de tabelas de Magento. Por exemplo, suponha que a extensão "GreatBlog" tenha um campo adicional na tabela eav_attribute e o nome alterado no Magento 2. Como a tabela é processada pela Etapa EAV, as regras de mapeamento devem ser gravadas para o arquivo map-eav.xml. Os arquivos map.xml e map-eav.xml usam o mesmo esquema map.xsd, portanto as regras de mapeamento permanecem as mesmas.

Principais alterações no formato de dados e na estrutura

Além da Etapa Mapear, há outras etapas no arquivo config.xml que migram dados com alterações importantes de formato e estrutura, incluindo:

Ao contrário da Etapa do mapa, essas etapas examinam uma lista predefinida de tabelas em vez de todas as tabelas.

Para alterações importantes no formato de dados e na estrutura, crie uma etapa personalizada.

Criar uma etapa personalizada

Usando o mesmo exemplo de "GreatBlog", suponha que a extensão tenha uma tabela no Magento 1, mas foi reprojetada para ter duas tabelas no Magento 2.

No Magento 1, havia uma única tabela greatblog_post:

| Field     | Type     |
|-----------|----------|
| post_id   | INT      |
| title     | VARCHAR  |
| content   | TEXT     |
| author_id | SMALLINT |
| tags      | TEXT     |

Na Magento 2, uma nova tabela para tags greatblog_post_tags foi introduzida:

| Field      | Type     |
|------------|----------|
| post_id    | INT      |
| tag        | VARCHAR  |
| sort_order | SMALLINT |

A tabela Magento 2 greatblog_post agora se parece com isto:

| Field     | Type     |
|-----------|----------|
| post_id   | INT      |
| title     | VARCHAR  |
| content   | TEXT     |
| author_id | SMALLINT |

Para migrar todos os dados da estrutura de tabelas antigas para uma nova, você pode criar uma etapa personalizada no arquivo config.xml. Por exemplo:

<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>

A ferramenta executa etapas de acordo com sua posição no arquivo config.xml; de cima para baixo. No nosso exemplo, o GreatBlog Step é executado por último.

As etapas podem incluir quatro tipos de classes:

  • Verificação de integridade
  • Entrega de dados
  • Verificação de volume
  • Entrega Delta
NOTE
Consulte Configuração, Etapas internas, Estágios e Modos de execução para obter mais informações.

Consultas SQL complexas podem ser montadas dentro dessas classes para buscar e migrar dados. Além disso, essas tabelas devem ser "ignoradas" na Etapa do mapa, pois ela verifica todas as tabelas existentes e tenta migrar os dados, a menos que estejam na marca <ignore> do arquivo map.xml.

Para verificação de integridade, defina um arquivo de mapa adicional no arquivo config.xml para verificar se a estrutura das tabelas é a esperada.

<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>

Arquivo de mapa 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>

A classe de verificação de integridade Vendor\Migration\Step\GreatBlog\Integrity estende Migration\App\Step\AbstractIntegrity e contém o método perform onde verificamos a estrutura da tabela:

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();
    }
    ...
}

Em seguida, você deve criar uma classe para processar e salvar dados no banco de dados 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;
    }
    ...
}

Em uma classe de Volume Vendor\Migration\Step\GreatBlog\Volume, verificamos se os dados foram totalmente migrados:

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);
    }
    ...
}

Para adicionar a funcionalidade de migração delta, adicione um novo grupo ao arquivo deltalog.xml. Em group, especifique o nome das tabelas nas quais devem ser verificadas as alterações:

<groups>
    ...
    <group name="delta_greatblog">
        <document key="post_id">greatblog_post</document>
    </group>
</groups>

Em seguida, crie a classe Delta Vendor\Migration\Step\GreatBlog\Delta que estende 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();
    }
}

Após a implementação da etapa personalizada fornecida nos exemplos, o sistema retira os dados da tabela de Magento 1 único,
processe-o usando a classe Vendor\Migration\Step\GreatBlog\Data e armazene os dados em duas tabelas Magento 2. Registros novos e alterados são entregues na migração delta usando a classe Vendor\Migration\Step\GreatBlog\Delta.

Métodos de extensão proibidos

Como o Data Migration Tool e o Magento 2 estão em constante evolução, as etapas e os manipuladores existentes estão sujeitos a alterações. É altamente recomendável não substituir o comportamento de etapas como Etapa do Mapa, Etapa de Regravação de URL e manipuladores, estendendo suas classes.

Algumas etapas não suportam mapeamento e não podem ser alteradas sem a alteração do código. Você pode gravar uma etapa extra que altere os dados no final da migração ou criar um problema do GitHub e solicitar um novo ponto de extensão na etapa existente.

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