Creating a new migration profile
If you want to migrate your data from a different source system than Shopware, create a new migration profile for the Migration Assistant. But if you want to convert your plugin data from a Shopware system to Shopware 6, please have a look at this HowTo: extend a shopware migration profile.

Setup

First of all, it is required that you already have installed the Migration Assistant plugin in Shopware 6 and have created a demo source system database with a product table. To create the table, use this SQL statement:
1
CREATE TABLE product
2
(
3
id int PRIMARY KEY NOT NULL AUTO_INCREMENT,
4
product_number varchar(255) NOT NULL,
5
price float NOT NULL,
6
stock int NOT NULL,
7
product_name varchar(255) NOT NULL,
8
tax float NOT NULL
9
);
Copied!
This table should simulate simple a third party source system, which should be migrated in the following steps.

Creating a profile

In the first step, you have to create a new profile for your source system:
1
<?php declare(strict_types=1);
2
​
3
namespace SwagMigrationOwnProfileExample;
4
​
5
use SwagMigrationAssistant\Migration\Profile\ProfileInterface;
6
​
7
class OwnProfile implements ProfileInterface
8
{
9
public const PROFILE_NAME = 'ownProfile';
10
​
11
public const SOURCE_SYSTEM_NAME = 'MySourceSystem';
12
​
13
public const SOURCE_SYSTEM_VERSION = '1.0';
14
​
15
public const AUTHOR_NAME = 'shopware AG';
16
​
17
public const ICON_PATH = '/swagmigrationassistant/static/img/migration-assistant-plugin.svg';
18
​
19
public function getName(): string
20
{
21
return self::PROFILE_NAME;
22
}
23
​
24
public function getSourceSystemName(): string
25
{
26
return self::SOURCE_SYSTEM_NAME;
27
}
28
​
29
public function getVersion(): string
30
{
31
return self::SOURCE_SYSTEM_VERSION;
32
}
33
​
34
public function getAuthorName(): string
35
{
36
return self::AUTHOR_NAME;
37
}
38
​
39
public function getIconPath(): string
40
{
41
return self::ICON_PATH;
42
}
43
}
Copied!
The profile itself does not contain any logic and is used to bundle the executing classes. To use this profile, you have to register and tag it in the service.xml with shopware.migration.profile:
1
<service id="SwagMigrationOwnProfileExample\Profile\OwnProfile\OwnProfile">
2
<tag name="shopware.migration.profile"/>
3
</service>
Copied!

Creating a gateway

Next, you have to create a new gateway, which supports your profile:
1
<?php declare(strict_types=1);
2
​
3
namespace SwagMigrationOwnProfileExample\Profile\OwnProfile\Gateway;
4
​
5
use Shopware\Core\Framework\Context;
6
use SwagMigrationAssistant\Migration\EnvironmentInformation;
7
use SwagMigrationAssistant\Migration\Gateway\GatewayInterface;
8
use SwagMigrationAssistant\Migration\Gateway\Reader\ReaderRegistry;
9
use SwagMigrationAssistant\Migration\MigrationContextInterface;
10
use SwagMigrationAssistant\Migration\RequestStatusStruct;
11
use SwagMigrationAssistant\Profile\Shopware\Exception\DatabaseConnectionException;
12
use SwagMigrationAssistant\Profile\Shopware\Gateway\Connection\ConnectionFactoryInterface;
13
use SwagMigrationOwnProfileExample\Profile\OwnProfile\OwnProfile;
14
​
15
class OwnLocaleGateway implements GatewayInterface
16
{
17
public const GATEWAY_NAME = 'local';
18
​
19
private ConnectionFactoryInterface $connectionFactory;
20
​
21
private ReaderRegistry $readerRegistry;
22
​
23
public function __construct(
24
ReaderRegistry $readerRegistry,
25
ConnectionFactoryInterface $connectionFactory
26
) {
27
$this->readerRegistry = $readerRegistry;
28
$this->connectionFactory = $connectionFactory;
29
}
30
​
31
public function getName(): string
32
{
33
return self::GATEWAY_NAME;
34
}
35
​
36
public function supports(MigrationContextInterface $migrationContext): bool
37
{
38
return $migrationContext->getProfile() instanceof OwnProfile;
39
}
40
​
41
public function getSnippetName(): string
42
{
43
return 'swag-migration.wizard.pages.connectionCreate.gateways.shopwareLocal';
44
}
45
​
46
/**
47
* Reads the given entity type from via context from its connection and returns the data
48
*/
49
public function read(MigrationContextInterface $migrationContext): array
50
{
51
// TODO: Implement read() method.
52
return [];
53
}
54
​
55
public function readEnvironmentInformation(
56
MigrationContextInterface $migrationContext,
57
Context $context
58
): EnvironmentInformation {
59
$connection = $this->connectionFactory->createDatabaseConnection($migrationContext);
60
$profile = $migrationContext->getProfile();
61
​
62
try {
63
$connection->connect();
64
} catch (\Exception $e) {
65
$error = new DatabaseConnectionException();
66
​
67
return new EnvironmentInformation(
68
$profile->getSourceSystemName(),
69
$profile->getVersion(),
70
'-',
71
[],
72
[],
73
new RequestStatusStruct($error->getErrorCode(), $error->getMessage())
74
);
75
}
76
$connection->close();
77
​
78
$totals = $this->readTotals($migrationContext, $context);
79
​
80
return new EnvironmentInformation(
81
$profile->getSourceSystemName(),
82
$profile->getVersion(),
83
'Example Host Name',
84
$totals,
85
[],
86
new RequestStatusStruct(),
87
false
88
);
89
}
90
​
91
public function readTotals(MigrationContextInterface $migrationContext, Context $context): array
92
{
93
$readers = $this->readerRegistry->getReaderForTotal($migrationContext);
94
​
95
$totals = [];
96
foreach ($readers as $reader) {
97
$total = $reader->readTotal($migrationContext);
98
​
99
if ($total === null) {
100
continue;
101
}
102
​
103
$totals[$total->getEntityName()] = $total;
104
}
105
​
106
return $totals;
107
}
108
}
Copied!
As you have seen above, the gateway uses the ConnectionFactory to test the connection to the source system. You can also implement your own way to check this, but to use this factory is the simplest way for a gateway to connect a local database. Like the profile you have to register the new gateway in the service.xml and tag it with shopware.migration.gateway:
1
<service id="SwagMigrationOwnProfileExample\Profile\OwnProfile\Gateway\OwnLocaleGateway">
2
<argument type="service" id="SwagMigrationAssistant\Migration\Gateway\Reader\ReaderRegistry"/>
3
<argument type="service" id="SwagMigrationAssistant\Profile\Shopware\Gateway\Connection\ConnectionFactory"/>
4
<tag name="shopware.migration.gateway"/>
5
</service>
Copied!

Creating a credentials page

If you would like to try your current progress in the administration, you could select the profile and gateway in the migration wizard already. If you try to go to the next page, there will be an error message, because no credentials page was found. To create a new credentials page, you have to add an index.js for your new component into Resources/app/administration/src/own-profile/profile:
1
import { Component } from 'src/core/shopware';
2
import template from './swag-migration-profile-ownProfile-local-credential-form.html.twig';
3
​
4
Component.register('swag-migration-profile-ownProfile-local-credential-form', {
5
template,
6
​
7
props: {
8
credentials: {
9
type: Object,
10
default() {
11
return {};
12
}
13
}
14
},
15
​
16
data() {
17
return {
18
inputCredentials: {
19
dbHost: '',
20
dbPort: '3306',
21
dbUser: '',
22
dbPassword: '',
23
dbName: ''
24
}
25
};
26
},
27
​
28
watch: {
29
credentials: {
30
immediate: true,
31
handler(newCredentials) {
32
if (newCredentials === null) {
33
this.emitCredentials(this.inputCredentials);
34
return;
35
}
36
​
37
this.inputCredentials = newCredentials;
38
this.emitOnChildRouteReadyChanged(
39
this.areCredentialsValid(this.inputCredentials)
40
);
41
}
42
},
43
​
44
inputCredentials: {
45
deep: true,
46
handler(newInputCredentials) {
47
this.emitCredentials(newInputCredentials);
48
}
49
}
50
},
51
​
52
methods: {
53
areCredentialsValid(newInputCredentials) {
54
return (newInputCredentials.dbHost !== '' &&
55
newInputCredentials.dbPort !== '' &&
56
newInputCredentials.dbName !== '' &&
57
newInputCredentials.dbUser !== '' &&
58
newInputCredentials.dbPassword !== ''
59
);
60
},
61
​
62
emitOnChildRouteReadyChanged(isReady) {
63
this.$emit('onChildRouteReadyChanged', isReady);
64
},
65
​
66
emitCredentials(newInputCredentials) {
67
this.$emit('onCredentialsChanged', newInputCredentials);
68
this.emitOnChildRouteReadyChanged(
69
this.areCredentialsValid(newInputCredentials)
70
);
71
},
72
​
73
onKeyPressEnter() {
74
this.$emit('onTriggerPrimaryClick');
75
}
76
}
77
});
Copied!
As you can see above, currently the template does not exists and you have to create this file: swag-migration-profile-ownProfile-local-credential-form.html.twig
1
<div data-gb-custom-block data-tag="block">
2
​
3
<div class="swag-migration-wizard swag-migration-wizard-page-credentials"
4
@keypress.enter="onKeyPressEnter">
5
6
​
7
<div data-gb-custom-block data-tag="block">
8
​
9
<div class="swag-migration-wizard__content">
10
11
​
12
<div data-gb-custom-block data-tag="block">
13
​
14
<div class="swag-migration-wizard__content-information">
15
16
​
17
<div data-gb-custom-block data-tag="block">
18
​
19
{{ $tc('swag-migration.wizard.pages.credentials.shopware55.local.contentInformation') }}
20
21
​
22
</div>
23
​
24
</div>
25
26
​
27
</div>
28
​
29
30
​
31
<div data-gb-custom-block data-tag="block">
32
​
33
<div class="swag-migration-wizard__form">
34
35
​
36
<div data-gb-custom-block data-tag="block">
37
​
38
<sw-container columns="1fr 80px"
39
gap="16px">
40
41
​
42
<div data-gb-custom-block data-tag="block">
43
​
44
<sw-text-field v-autofocus
45
name="sw-field--dbHost"
46
:label="$tc('swag-migration.wizard.pages.credentials.shopware55.local.dbHostLabel')"
47
:placeholder="$tc('swag-migration.wizard.pages.credentials.shopware55.local.dbHostPlaceholder')"
48
v-model="inputCredentials.dbHost">
49
</sw-text-field>
50
51
​
52
</div>
53
​
54
55
​
56
<div data-gb-custom-block data-tag="block">
57
​
58
<sw-field name="sw-field--dbPort"
59
:label="$tc('swag-migration.wizard.pages.credentials.shopware55.local.dbPortLabel')"
60
v-model="inputCredentials.dbPort">
61
</sw-field>
62
63
​
64
</div>
65
​
66
</sw-container>
67
68
​
69
</div>
70
​
71
72
​
73
<div data-gb-custom-block data-tag="block">
74
​
75
<sw-field name="sw-field--dbUser"
76
:label="$tc('swag-migration.wizard.pages.credentials.shopware55.local.dbUserLabel')"
77
:placeholder="$tc('swag-migration.wizard.pages.credentials.shopware55.local.dbUserPlaceholder')"
78
v-model="inputCredentials.dbUser">
79
</sw-field>
80
81
​
82
</div>
83
​
84
85
​
86
<div data-gb-custom-block data-tag="block">
87
​
88
<sw-field name="sw-field--dbPassword"
89
type="password"
90
:label="$tc('swag-migration.wizard.pages.credentials.shopware55.local.dbPasswordLabel')"
91
:placeholder="$tc('swag-migration.wizard.pages.credentials.shopware55.local.dbPasswordPlaceholder')"
92
v-model="inputCredentials.dbPassword">
93
</sw-field>
94
95
​
96
</div>
97
​
98
99
​
100
<div data-gb-custom-block data-tag="block">
101
​
102
<sw-field name="sw-field--dbName"
103
:label="$tc('swag-migration.wizard.pages.credentials.shopware55.local.dbNameLabel')"
104
:placeholder="$tc('swag-migration.wizard.pages.credentials.shopware55.local.dbNamePlaceholder')"
105
v-model="inputCredentials.dbName">
106
</sw-field>
107
108
​
109
</div>
110
​
111
</div>
112
113
​
114
</div>
115
​
116
</div>
117
118
​
119
</div>
120
​
121
</div>
122
​
123
</div>
Copied!
A few things to notice: The component name isn't random, it has to consist of:
  1. 1.
    The prefix: swag-migration-profile-
  2. 2.
    The name of the profile
  3. 3.
    The name of the gateway
  4. 4.
    The suffix: -credential-form
To see your credentials page, you have to register this component in your main.js:
1
import './own-profile/profile';
Copied!

Creating a DataSet and DataSelection

Now the credentials page is loaded in the administration and the connection check will succeed. But there is no data selection, if you open the data selection table. To add an entry to this table, you have to create a ProductDataSet first:
1
<?php declare(strict_types=1);
2
​
3
namespace SwagMigrationOwnProfileExample\Profile\OwnProfile\DataSelection\DataSet;
4
​
5
use SwagMigrationAssistant\Migration\DataSelection\DataSet\DataSet;
6
use SwagMigrationAssistant\Migration\MigrationContextInterface;
7
use SwagMigrationOwnProfileExample\Profile\OwnProfile\OwnProfile;
8
​
9
class ProductDataSet extends DataSet
10
{
11
/**
12
* Returns the entity identifier of this DataSet
13
*/
14
public static function getEntity(): string
15
{
16
return 'product';
17
}
18
​
19
/**
20
* Supports only an OwnProfile
21
*/
22
public function supports(MigrationContextInterface $migrationContext): bool
23
{
24
return $migrationContext->getProfile() instanceof OwnProfile;
25
}
26
}
Copied!
Now you have to use this ProductDataSet in the new ProductDataSelection:
1
<?php declare(strict_types=1);
2
​
3
namespace SwagMigrationOwnProfileExample\Profile\OwnProfile\DataSelection;
4
​
5
use SwagMigrationAssistant\Migration\DataSelection\DataSelectionInterface;
6
use SwagMigrationAssistant\Migration\DataSelection\DataSelectionStruct;
7
use SwagMigrationAssistant\Migration\MigrationContextInterface;
8
use SwagMigrationOwnProfileExample\Profile\OwnProfile\DataSelection\DataSet\ProductDataSet;
9
use SwagMigrationOwnProfileExample\Profile\OwnProfile\OwnProfile;
10
​
11
class ProductDataSelection implements DataSelectionInterface
12
{
13
/**
14
* Identifier of this DataSelection
15
*/
16
public const IDENTIFIER = 'products';
17
​
18
/**
19
* Supports only an OwnProfile
20
*/
21
public function supports(MigrationContextInterface $migrationContext): bool
22
{
23
return $migrationContext->getProfile() instanceof OwnProfile;
24
}
25
​
26
public function getData(): DataSelectionStruct
27
{
28
return new DataSelectionStruct(
29
self::IDENTIFIER,
30
$this->getDataSets(),
31
$this->getDataSetsRequiredForCount(),
32
/*
33
* Snippet of the original ProductDataSelection, if you
34
* want to use your own title, you have to create a new snippet
35
*/
36
'swag-migration.index.selectDataCard.dataSelection.products',
37
100
38
);
39
}
40
​
41
/**
42
* Returns all DataSets, which should be migrated with this DataSelection
43
*/
44
public function getDataSets(): array
45
{
46
return [
47
new ProductDataSet()
48
];
49
}
50
​
51
public function getDataSetsRequiredForCount(): array
52
{
53
return $this->getDataSets();
54
}
55
}
Copied!
The order in the getDataSets array is important, because this it determines the order in which the entities are processed. Because of that, the manufacturers, for example, have to be positioned before the products, so that the products can use those later on.
To see the created ProductDataSelection in the administration, you have to register it both in the services.xml and tag them with shopware.migration.data_selection and shopware.migration.data_set:
1
<service id="SwagMigrationOwnProfileExample\Profile\OwnProfile\DataSelection\ProductDataSelection">
2
<tag name="shopware.migration.data_selection"/>
3
</service>
4
​
5
<service id="SwagMigrationOwnProfileExample\Profile\OwnProfile\DataSelection\DataSet\ProductDataSet">
6
<tag name="shopware.migration.data_set"/>
7
</service>
Copied!

Creating a product gateway reader

Currently, you can see the DataSelection in the administration, but if you select it and start a migration, there will be no product migrated. That's because the gateway read function isn't implemented, yet. But before you can implement this function, you have to create a new ProductReader first:
1
<?php declare(strict_types=1);
2
​
3
namespace SwagMigrationOwnProfileExample\Profile\OwnProfile\Gateway\Reader;
4
​
5
use Doctrine\DBAL\Driver\ResultStatement;
6
use SwagMigrationAssistant\Migration\MigrationContextInterface;
7
use SwagMigrationAssistant\Migration\TotalStruct;
8
use SwagMigrationAssistant\Profile\Shopware\Gateway\Local\Reader\AbstractReader;
9
use SwagMigrationAssistant\Profile\Shopware\Gateway\Local\ShopwareLocalGateway;
10
use SwagMigrationOwnProfileExample\Profile\OwnProfile\DataSelection\DataSet\ProductDataSet;
11
use SwagMigrationOwnProfileExample\Profile\OwnProfile\OwnProfile;
12
​
13
class ProductReader extends AbstractReader
14
{
15
/**
16
* Supports only an OwnProfile and the ProductDataSet
17
*/
18
public function supports(MigrationContextInterface $migrationContext): bool
19
{
20
return $migrationContext->getProfile() instanceof OwnProfile
21
&& $migrationContext->getDataSet()::getEntity() === ProductDataSet::getEntity();
22
}
23
​
24
/**
25
* Supports only an OwnProfile and the ProductDataSet for totals
26
*/
27
public function supportsTotal(MigrationContextInterface $migrationContext): bool
28
{
29
return $migrationContext->getProfile() instanceof OwnProfile
30
&& $migrationContext->getGateway()->getName() === ShopwareLocalGateway::GATEWAY_NAME;
31
}
32
​
33
/**
34
* Creates a database connection and sets the connection class variable
35
*/
36
protected function setConnection(MigrationContextInterface $migrationContext): void
37
{
38
$this->connection = $this->connectionFactory->createDatabaseConnection($migrationContext);
39
}
40
​
41
public function readTotal(MigrationContextInterface $migrationContext): ?TotalStruct
42
{
43
$this->setConnection($migrationContext);
44
​
45
$query = $this->connection->createQueryBuilder()
46
->select('COUNT(*)')
47
->from('product')
48
->execute();
49
​
50
$total = 0;
51
if ($query instanceof ResultStatement) {
52
$total = (int) $query->fetchColumn();
53
}
54
​
55
return new TotalStruct(ProductDataSet::getEntity(), $total);
56
}
57
​
58
/**
59
* Fetches all entities out of the product table with the given limit
60
*/
61
public function read(MigrationContextInterface $migrationContext, array $params = []): array
62
{
63
$this->setConnection($migrationContext);
64
​
65
$query = $this->connection->createQueryBuilder();
66
$query->from('product');
67
$query->addSelect('*');
68
​
69
$query->setFirstResult($migrationContext->getOffset());
70
$query->setMaxResults($migrationContext->getLimit());
71
​
72
return $query->execute()->fetchAll(\PDO::FETCH_ASSOC);
73
}
74
}
Copied!
Then you have to register this in services.xml and tag it with shopware.migration.reader:
1
<service id="SwagMigrationOwnProfileExample\Profile\OwnProfile\Gateway\Reader\ProductReader"
2
parent="SwagMigrationAssistant\Profile\Shopware\Gateway\Local\Reader\AbstractReader">
3
<argument type="service" id="SwagMigrationAssistant\Profile\Shopware\Gateway\Connection\ConnectionFactory"/>
4
<tag name="shopware.migration.reader"/>
5
</service>
Copied!
Once the ProductReader is created and registered, you can use it in the read method of the OwnLocaleGateway:
1
<?php declare(strict_types=1);
2
​
3
namespace SwagMigrationOwnProfileExample\Profile\OwnProfile\Gateway;
4
​
5
use Shopware\Core\Framework\Context;
6
use SwagMigrationAssistant\Migration\EnvironmentInformation;
7
use SwagMigrationAssistant\Migration\Gateway\GatewayInterface;
8
use SwagMigrationAssistant\Migration\Gateway\Reader\ReaderRegistry;
9
use SwagMigrationAssistant\Migration\MigrationContextInterface;
10
use SwagMigrationAssistant\Migration\RequestStatusStruct;
11
use SwagMigrationAssistant\Profile\Shopware\Exception\DatabaseConnectionException;
12
use SwagMigrationAssistant\Profile\Shopware\Gateway\Connection\ConnectionFactoryInterface;
13
use SwagMigrationOwnProfileExample\Profile\OwnProfile\OwnProfile;
14
​
15
class OwnLocaleGateway implements GatewayInterface
16
{
17
public const GATEWAY_NAME = 'local';
18
​
19
private ConnectionFactoryInterface $connectionFactory;
20
​
21
private ReaderRegistry $readerRegistry;
22
​
23
public function __construct(
24
ReaderRegistry $readerRegistry,
25
ConnectionFactoryInterface $connectionFactory
26
) {
27
$this->readerRegistry = $readerRegistry;
28
$this->connectionFactory = $connectionFactory;
29
}
30
​
31
public function getName(): string
32
{
33
return self::GATEWAY_NAME;
34
}
35
​
36
public function supports(MigrationContextInterface $migrationContext): bool
37
{
38
return $migrationContext->getProfile() instanceof OwnProfile;
39
}
40
​
41
public function getSnippetName(): string
42
{
43
return 'swag-migration.wizard.pages.connectionCreate.gateways.shopwareLocal';
44
}
45
​
46
public function read(MigrationContextInterface $migrationContext): array
47
{
48
$reader = $this->readerRegistry->getReader($migrationContext);
49
​
50
return $reader->read($migrationContext);
51
}
52
​
53
public function readEnvironmentInformation(
54
MigrationContextInterface $migrationContext,
55
Context $context
56
): EnvironmentInformation {
57
$connection = $this->connectionFactory->createDatabaseConnection($migrationContext);
58
$profile = $migrationContext->getProfile();
59
​
60
try {
61
$connection->connect();
62
} catch (\Exception $e) {
63
$error = new DatabaseConnectionException();
64
​
65
return new EnvironmentInformation(
66
$profile->getSourceSystemName(),
67
$profile->getVersion(),
68
'-',
69
[],
70
[],
71
new RequestStatusStruct($error->getErrorCode(), $error->getMessage())
72
);
73
}
74
$connection->close();
75
​
76
$totals = $this->readTotals($migrationContext, $context);
77
​
78
return new EnvironmentInformation(
79
$profile->getSourceSystemName(),
80
$profile->getVersion(),
81
'Example Host Name',
82
$totals,
83
[],
84
new RequestStatusStruct(),
85
false
86
);
87
}
88
​
89
public function readTotals(MigrationContextInterface $migrationContext, Context $context): array
90
{
91
$readers = $this->readerRegistry->getReaderForTotal($migrationContext);
92
​
93
$totals = [];
94
foreach ($readers as $reader) {
95
$total = $reader->readTotal($migrationContext);
96
​
97
if ($total === null) {
98
continue;
99
}
100
​
101
$totals[$total->getEntityName()] = $total;
102
}
103
​
104
return $totals;
105
}
106
}
Copied!

Creating a converter

By using the gateway reader, you fetch all products, but don't use this data yet. In this step you implement the logic of the converter:
1
<?php declare(strict_types=1);
2
​
3
namespace SwagMigrationOwnProfileExample\Profile\OwnProfile\Converter;
4
​
5
use Shopware\Core\Framework\Context;
6
use SwagMigrationAssistant\Migration\Converter\ConvertStruct;
7
use SwagMigrationAssistant\Migration\DataSelection\DefaultEntities;
8
use SwagMigrationAssistant\Migration\MigrationContextInterface;
9
use SwagMigrationAssistant\Profile\Shopware\Converter\ShopwareConverter;
10
use SwagMigrationOwnProfileExample\Profile\OwnProfile\DataSelection\DataSet\ProductDataSet;
11
use SwagMigrationOwnProfileExample\Profile\OwnProfile\OwnProfile;
12
​
13
class ProductConverter extends ShopwareConverter
14
{
15
private string $connectionId;
16
​
17
private Context $context;
18
​
19
public function getSourceIdentifier(array $data): string
20
{
21
return $data['id'];
22
}
23
​
24
/**
25
* Supports only an OwnProfile and the ProductDataSet
26
*/
27
public function supports(MigrationContextInterface $migrationContext): bool
28
{
29
return $migrationContext->getProfile() instanceof OwnProfile &&
30
$migrationContext->getDataSet()::getEntity() === ProductDataSet::getEntity();
31
}
32
​
33
/**
34
* Writes the created mapping
35
*/
36
public function writeMapping(Context $context): void
37
{
38
$this->mappingService->writeMapping($context);
39
}
40
​
41
public function convert(array $data, Context $context, MigrationContextInterface $migrationContext): ConvertStruct
42
{
43
$this->generateChecksum($data);
44
$this->connectionId = $migrationContext->getConnection()->getId();
45
$this->context = $context;
46
​
47
/**
48
* Gets the product uuid out of the mapping table or creates a new one
49
*/
50
$this->mainMapping = $this->mappingService->getOrCreateMapping(
51
$migrationContext->getConnection()->getId(),
52
ProductDataSet::getEntity(),
53
$data['id'],
54
$context,
55
$this->checksum
56
);
57
​
58
$converted['id'] = $this->mainMapping['entityUuid'];
59
$this->convertValue($converted, 'productNumber', $data, 'product_number');
60
$this->convertValue($converted, 'name', $data, 'product_name');
61
$this->convertValue($converted, 'stock', $data, 'stock', self::TYPE_INTEGER);
62
​
63
if (isset($data['tax'])) {
64
$converted['tax'] = $this->getTax($data);
65
$converted['price'] = $this->getPrice($data, $converted['tax']['taxRate']);
66
}
67
​
68
unset(
69
$data['id'],
70
$data['product_number'],
71
$data['product_name'],
72
$data['stock'],
73
$data['tax'],
74
$data['price']
75
);
76
​
77
if (empty($data)) {
78
$data = null;
79
}
80
$this->updateMainMapping($migrationContext, $context);
81
​
82
return new ConvertStruct($converted, $data, $this->mainMapping['id']);
83
}
84
​
85
private function getTax(array $data): array
86
{
87
$taxRate = (float) $data['tax'];
88
​
89
/**
90
* Gets the tax uuid by the given tax rate
91
*/
92
$taxUuid = $this->mappingService->getTaxUuid($this->connectionId, $taxRate, $this->context);
93
​
94
/**
95
* If no tax rate is found, create a new one
96
*/
97
if ($taxUuid === null) {
98
$mapping = $this->mappingService->createMapping(
99
$this->connectionId,
100
DefaultEntities::TAX,
101
$data['id']
102
);
103
$taxUuid = $mapping['entityUuid'];
104
}
105
​
106
return [
107
'id' => $taxUuid,
108
'taxRate' => $taxRate,
109
'name' => 'Own profile tax rate (' . $taxRate . ')',
110
];
111
}
112
​
113
private function getPrice(array $data, float $taxRate): array
114
{
115
$gross = (float) $data['price'] * (1 + $taxRate / 100);
116
​
117
/**
118
* Gets the currency uuid by the given iso code
119
*/
120
$currencyUuid = $this->mappingService->getCurrencyUuid(
121
$this->connectionId,
122
'EUR',
123
$this->context
124
);
125
​
126
if ($currencyUuid === null) {
127
return [];
128
}
129
​
130
$price = [];
131
$price[] = [
132
'currencyId' => $currencyUuid,
133
'gross' => $gross,
134
'net' => (float) $data['price'],
135
'linked' => true,
136
];
137
​
138
return $price;
139
}
140
}
Copied!
If you don't know which properties or requirements your entity has in Shopware 6, you may check the corresponding EntityDefinition. For this example, please take a look at the ProductEntityDefinition to know how to convert the data exactly.
To use this converter, you must register it in the services.xml:
1
<service id="SwagMigrationOwnProfileExample\Profile\OwnProfile\Converter\ProductConverter">
2
<argument type="service" id="SwagMigrationAssistant\Migration\Mapping\MappingService"/>
3
<argument type="service" id="SwagMigrationAssistant\Migration\Logging\LoggingService"/>
4
<tag name="shopware.migration.converter"/>
5
</service>
Copied!
To write new entities, you have to create a new writer class, but for the product entity, you can use the ProductWriter:
1
<?php declare(strict_types=1);
2
​
3
namespace SwagMigrationAssistant\Migration\Writer;
4
​
5
use SwagMigrationAssistant\Migration\DataSelection\DefaultEntities;
6
​
7
class ProductWriter extends AbstractWriter
8
{
9
public function supports(): string
10
{
11
return DefaultEntities::PRODUCT;
12
}
13
}
Copied!
This writer will automatically be called, because the getEntityName method of your ProductDataSet is compared with the return value of the supports method of the writer in the WriterRegistry. These values are identically and so the writer will be used to write your product entities.

Source

There's a GitHub repository available, containing a full example source. Check it out here.
Last modified 1mo ago