다른 단계에서 매핑 확장
EAV 단계 및 고객 특성 단계와 같은 다른 단계는 매핑을 지원합니다. 이 단계는 사전 정의된 Magento 테이블 목록을 마이그레이션합니다. 예를 들어, "GreatBlog" 확장에 eav_attribute
테이블에 추가 필드가 있고 Magento 2에서 이름이 변경되었다고 가정해 봅시다. 테이블이 EAV 단계에 의해 처리되므로 map-eav.xml
파일에 대해 매핑 규칙을 작성해야 합니다. map.xml
및 map-eav.xml
파일에서 동일한 map.xsd
스키마를 사용하므로 매핑 규칙은 동일하게 유지됩니다.
주요 데이터 형식 및 구조 변경
config.xml
파일에는 맵 단계 외에도 다음과 같은 주요 형식 및 구조 변경 사항으로 데이터를 마이그레이션하는 다른 단계가 있습니다.
- Url 다시 작성 단계
- OrderGrid 단계
- EAV 단계
맵 단계와 달리 이 단계는 모든 테이블 대신 미리 정의된 테이블 목록을 검사합니다.
주요 데이터 형식 및 구조 변경의 경우 사용자 지정 단계를 만듭니다.
사용자 지정 단계 만들기
동일한 "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
파일에 사용자 지정 단계를 만들 수 있습니다. For example:
<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 쿼리를 취합하여 데이터를 가져오고 마이그레이션할 수 있습니다. 또한 맵 단계에서 이러한 테이블을 "무시해야" 합니다. 기존의 모든 테이블을 검사하고 데이터가 map.xml
파일의 <ignore>
태그에 없으면 데이터를 마이그레이션하려고 시도하기 때문입니다.
무결성 검사를 위해 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;
}
...
}
볼륨 클래스 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>
그런 다음 Migration\App\Step\AbstractDelta
을(를) 확장하는 Delta
클래스 Vendor\Migration\Step\GreatBlog\Delta
을(를) 만듭니다.
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
클래스를 사용하여 델타 마이그레이션 시 전달됩니다.