Skip to content

Commit e844b50

Browse files
committed
feat: Compatible with migration assistant
1 parent 54b31ad commit e844b50

File tree

6 files changed

+276
-2
lines changed

6 files changed

+276
-2
lines changed

src/DataProvider/Provider/Data/ProductProvider.php

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@
77

88
namespace SwagMigrationAssistant\DataProvider\Provider\Data;
99

10+
use Doctrine\DBAL\Connection;
1011
use Shopware\Core\Content\Product\ProductCollection;
1112
use Shopware\Core\Framework\Context;
1213
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
1314
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
15+
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
16+
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\NotFilter;
1417
use Shopware\Core\Framework\DataAbstractionLayer\Search\Sorting\FieldSorting;
1518
use Shopware\Core\Framework\Log\Package;
1619
use SwagMigrationAssistant\Migration\DataSelection\DefaultEntities;
@@ -20,12 +23,17 @@
2023
#[Package('fundamentals@after-sales')]
2124
class ProductProvider extends AbstractProvider
2225
{
26+
private const BUNDLE_PRODUCT_TYPE = 'grouped_bundle';
27+
28+
private ?bool $hasTypeColumn = null;
29+
2330
/**
2431
* @param EntityRepository<ProductCollection> $productRepo
2532
*/
2633
public function __construct(
2734
private readonly EntityRepository $productRepo,
2835
private readonly RouterInterface $router,
36+
private readonly Connection $connection,
2937
) {
3038
}
3139

@@ -55,6 +63,7 @@ public function getProvidedData(int $limit, int $offset, Context $context): arra
5563
new FieldSorting('parentId'), // get 'NULL' parentIds first
5664
new FieldSorting('id')
5765
);
66+
$this->addBundleExclusionFilter($criteria);
5867
$result = $this->productRepo->search($criteria, $context);
5968

6069
$cleanResult = $this->cleanupSearchResult($result, [
@@ -113,6 +122,34 @@ public function getProvidedData(int $limit, int $offset, Context $context): arra
113122

114123
public function getProvidedTotal(Context $context): int
115124
{
116-
return $this->readTotalFromRepo($this->productRepo, $context);
125+
$criteria = new Criteria();
126+
$this->addBundleExclusionFilter($criteria);
127+
128+
return $this->readTotalFromRepo($this->productRepo, $context, $criteria);
129+
}
130+
131+
private function addBundleExclusionFilter(Criteria $criteria): void
132+
{
133+
if (!$this->hasTypeColumn()) {
134+
return;
135+
}
136+
137+
$criteria->addFilter(
138+
new NotFilter(NotFilter::CONNECTION_AND, [
139+
new EqualsFilter('type', self::BUNDLE_PRODUCT_TYPE),
140+
])
141+
);
142+
}
143+
144+
private function hasTypeColumn(): bool
145+
{
146+
if ($this->hasTypeColumn !== null) {
147+
return $this->hasTypeColumn;
148+
}
149+
150+
$columns = $this->connection->createSchemaManager()->listTableColumns('product');
151+
$this->hasTypeColumn = isset($columns['type']);
152+
153+
return $this->hasTypeColumn;
117154
}
118155
}

src/DependencyInjection/dataProvider.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
<service id="SwagMigrationAssistant\DataProvider\Provider\Data\ProductProvider">
7272
<argument type="service" id="product.repository"/>
7373
<argument type="service" id="router"/>
74+
<argument type="service" id="Doctrine\DBAL\Connection"/>
7475
<tag name="shopware.dataProvider.provider"/>
7576
</service>
7677

src/DependencyInjection/shopware6.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@
115115

116116
<service id="SwagMigrationAssistant\Profile\Shopware6\Converter\ProductConverter"
117117
parent="SwagMigrationAssistant\Profile\Shopware6\Converter\ShopwareMediaConverter">
118+
<argument type="service" id="Doctrine\DBAL\Connection"/>
118119
<tag name="shopware.migration.converter"/>
119120
</service>
120121

src/Profile/Shopware6/Converter/ProductConverter.php

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,16 @@
77

88
namespace SwagMigrationAssistant\Profile\Shopware6\Converter;
99

10+
use Doctrine\DBAL\Connection;
11+
use Shopware\Core\Content\Product\ProductDefinition;
12+
use Shopware\Core\Content\Product\State;
1013
use Shopware\Core\Defaults;
1114
use Shopware\Core\Framework\Log\Package;
1215
use SwagMigrationAssistant\Migration\Converter\ConvertStruct;
1316
use SwagMigrationAssistant\Migration\DataSelection\DefaultEntities;
17+
use SwagMigrationAssistant\Migration\Logging\LoggingServiceInterface;
18+
use SwagMigrationAssistant\Migration\Mapping\MappingServiceInterface;
19+
use SwagMigrationAssistant\Migration\Media\MediaFileServiceInterface;
1420
use SwagMigrationAssistant\Migration\MigrationContextInterface;
1521
use SwagMigrationAssistant\Profile\Shopware6\DataSelection\DataSet\ProductDataSet;
1622
use SwagMigrationAssistant\Profile\Shopware6\Shopware6MajorProfile;
@@ -20,6 +26,17 @@ class ProductConverter extends ShopwareMediaConverter
2026
{
2127
private ?string $sourceDefaultCurrencyUuid;
2228

29+
private ?bool $hasTypeColumn = null;
30+
31+
public function __construct(
32+
MappingServiceInterface $mappingService,
33+
LoggingServiceInterface $loggingService,
34+
MediaFileServiceInterface $mediaFileService,
35+
private readonly Connection $connection,
36+
) {
37+
parent::__construct($mappingService, $loggingService, $mediaFileService);
38+
}
39+
2340
public function supports(MigrationContextInterface $migrationContext): bool
2441
{
2542
return $migrationContext->getProfile()->getName() === Shopware6MajorProfile::PROFILE_NAME
@@ -205,6 +222,8 @@ protected function convertData(array $data): ConvertStruct
205222
);
206223
}
207224

225+
$this->convertStatesToType($converted);
226+
208227
return new ConvertStruct($converted, null, $this->mainMapping['id'] ?? null);
209228
}
210229

@@ -235,4 +254,40 @@ private function checkDefaultCurrency(array &$source, string $key): void
235254
$source[$key][] = $defaultPrice;
236255
}
237256
}
257+
258+
/**
259+
* @param array<string, mixed> $converted
260+
*/
261+
private function convertStatesToType(array &$converted): void
262+
{
263+
if (!$this->hasTypeColumn()) {
264+
return;
265+
}
266+
267+
if (isset($converted['type'])) {
268+
return;
269+
}
270+
271+
if (isset($converted['states']) && \is_array($converted['states'])) {
272+
$converted['type'] = \in_array(State::IS_DOWNLOAD, $converted['states'], true)
273+
? ProductDefinition::TYPE_DIGITAL
274+
: ProductDefinition::TYPE_PHYSICAL;
275+
276+
return;
277+
}
278+
279+
$converted['type'] = ProductDefinition::TYPE_PHYSICAL;
280+
}
281+
282+
private function hasTypeColumn(): bool
283+
{
284+
if ($this->hasTypeColumn !== null) {
285+
return $this->hasTypeColumn;
286+
}
287+
288+
$columns = $this->connection->createSchemaManager()->listTableColumns('product');
289+
$this->hasTypeColumn = isset($columns['type']);
290+
291+
return $this->hasTypeColumn;
292+
}
238293
}

tests/Profile/Shopware6/Converter/ProductConverterTest.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
namespace SwagMigrationAssistant\Test\Profile\Shopware6\Converter;
99

10+
use Doctrine\DBAL\Connection;
1011
use Shopware\Core\Framework\Log\Package;
1112
use SwagMigrationAssistant\Migration\Converter\ConverterInterface;
1213
use SwagMigrationAssistant\Migration\DataSelection\DataSet\DataSet;
@@ -25,7 +26,9 @@ protected function createConverter(
2526
MediaFileServiceInterface $mediaFileService,
2627
?array $mappingArray = [],
2728
): ConverterInterface {
28-
return new ProductConverter($mappingService, $loggingService, $mediaFileService);
29+
$connection = static::getContainer()->get(Connection::class);
30+
31+
return new ProductConverter($mappingService, $loggingService, $mediaFileService, $connection);
2932
}
3033

3134
protected function createDataSet(): DataSet
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
<?php declare(strict_types=1);
2+
/*
3+
* (c) shopware AG <info@shopware.com>
4+
* For the full copyright and license information, please view the LICENSE
5+
* file that was distributed with this source code.
6+
*/
7+
8+
namespace SwagMigrationAssistant\Test\Profile\Shopware6\Converter;
9+
10+
use Doctrine\DBAL\Connection;
11+
use Doctrine\DBAL\Schema\AbstractSchemaManager;
12+
use Doctrine\DBAL\Schema\Column;
13+
use Doctrine\DBAL\Types\StringType;
14+
use PHPUnit\Framework\TestCase;
15+
use Shopware\Core\Content\Product\ProductDefinition;
16+
use Shopware\Core\Framework\Context;
17+
use Shopware\Core\Framework\Log\Package;
18+
use Shopware\Core\Framework\Uuid\Uuid;
19+
use SwagMigrationAssistant\Migration\Connection\SwagMigrationConnectionEntity;
20+
use SwagMigrationAssistant\Migration\MigrationContext;
21+
use SwagMigrationAssistant\Profile\Shopware6\Converter\ProductConverter;
22+
use SwagMigrationAssistant\Profile\Shopware6\DataSelection\DataSet\ProductDataSet;
23+
use SwagMigrationAssistant\Profile\Shopware6\Shopware6MajorProfile;
24+
use SwagMigrationAssistant\Test\Mock\Migration\Logging\DummyLoggingService;
25+
use SwagMigrationAssistant\Test\Mock\Migration\Mapping\Dummy6MappingService;
26+
use SwagMigrationAssistant\Test\Mock\Migration\Media\DummyMediaFileService;
27+
28+
#[Package('fundamentals@after-sales')]
29+
class ProductConverterUnitTest extends TestCase
30+
{
31+
public function testConvertStatesToTypeWithDownloadState(): void
32+
{
33+
$converter = $this->createConverterWithTypeColumn(true);
34+
$context = Context::createDefaultContext();
35+
$migrationContext = $this->createMigrationContext();
36+
37+
$input = [
38+
'id' => Uuid::randomHex(),
39+
'productNumber' => 'TEST-001',
40+
'stock' => 10,
41+
'states' => ['is-download'],
42+
'name' => 'Digital Product',
43+
];
44+
45+
$result = $converter->convert($input, $context, $migrationContext);
46+
$converted = $result->getConverted();
47+
48+
static::assertNotNull($converted);
49+
static::assertArrayHasKey('type', $converted);
50+
static::assertSame(ProductDefinition::TYPE_DIGITAL, $converted['type']);
51+
}
52+
53+
public function testConvertStatesToTypeWithPhysicalState(): void
54+
{
55+
$converter = $this->createConverterWithTypeColumn(true);
56+
$context = Context::createDefaultContext();
57+
$migrationContext = $this->createMigrationContext();
58+
59+
$input = [
60+
'id' => Uuid::randomHex(),
61+
'productNumber' => 'TEST-002',
62+
'stock' => 10,
63+
'states' => ['is-physical'],
64+
'name' => 'Physical Product',
65+
];
66+
67+
$result = $converter->convert($input, $context, $migrationContext);
68+
$converted = $result->getConverted();
69+
70+
static::assertNotNull($converted);
71+
static::assertArrayHasKey('type', $converted);
72+
static::assertSame(ProductDefinition::TYPE_PHYSICAL, $converted['type']);
73+
}
74+
75+
public function testConvertPreservesExistingType(): void
76+
{
77+
$converter = $this->createConverterWithTypeColumn(true);
78+
$context = Context::createDefaultContext();
79+
$migrationContext = $this->createMigrationContext();
80+
81+
$input = [
82+
'id' => Uuid::randomHex(),
83+
'productNumber' => 'TEST-003',
84+
'stock' => 10,
85+
'type' => 'digital',
86+
'name' => 'Product With Type',
87+
];
88+
89+
$result = $converter->convert($input, $context, $migrationContext);
90+
$converted = $result->getConverted();
91+
92+
static::assertNotNull($converted);
93+
static::assertArrayHasKey('type', $converted);
94+
static::assertSame('digital', $converted['type']);
95+
}
96+
97+
public function testConvertDefaultsToPhysicalType(): void
98+
{
99+
$converter = $this->createConverterWithTypeColumn(true);
100+
$context = Context::createDefaultContext();
101+
$migrationContext = $this->createMigrationContext();
102+
103+
$input = [
104+
'id' => Uuid::randomHex(),
105+
'productNumber' => 'TEST-004',
106+
'stock' => 10,
107+
'name' => 'Product Without Type Or States',
108+
];
109+
110+
$result = $converter->convert($input, $context, $migrationContext);
111+
$converted = $result->getConverted();
112+
113+
static::assertNotNull($converted);
114+
static::assertArrayHasKey('type', $converted);
115+
static::assertSame(ProductDefinition::TYPE_PHYSICAL, $converted['type']);
116+
}
117+
118+
public function testConvertDoesNotSetTypeWhenColumnDoesNotExist(): void
119+
{
120+
$converter = $this->createConverterWithTypeColumn(false);
121+
$context = Context::createDefaultContext();
122+
$migrationContext = $this->createMigrationContext();
123+
124+
$input = [
125+
'id' => Uuid::randomHex(),
126+
'productNumber' => 'TEST-005',
127+
'stock' => 10,
128+
'states' => ['is-download'],
129+
'name' => 'Product On Old Database',
130+
];
131+
132+
$result = $converter->convert($input, $context, $migrationContext);
133+
$converted = $result->getConverted();
134+
135+
static::assertNotNull($converted);
136+
static::assertArrayNotHasKey('type', $converted);
137+
}
138+
139+
private function createConverterWithTypeColumn(bool $hasTypeColumn): ProductConverter
140+
{
141+
$schemaManager = $this->createMock(AbstractSchemaManager::class);
142+
143+
if ($hasTypeColumn) {
144+
$schemaManager->method('listTableColumns')->willReturn([
145+
'type' => new Column('type', new StringType()),
146+
]);
147+
} else {
148+
$schemaManager->method('listTableColumns')->willReturn([]);
149+
}
150+
151+
$connection = $this->createMock(Connection::class);
152+
$connection->method('createSchemaManager')->willReturn($schemaManager);
153+
154+
return new ProductConverter(
155+
new Dummy6MappingService(),
156+
new DummyLoggingService(),
157+
new DummyMediaFileService(),
158+
$connection
159+
);
160+
}
161+
162+
private function createMigrationContext(): MigrationContext
163+
{
164+
$connection = new SwagMigrationConnectionEntity();
165+
$connection->setId(Uuid::randomHex());
166+
$connection->setProfileName(Shopware6MajorProfile::PROFILE_NAME);
167+
168+
return new MigrationContext(
169+
new Shopware6MajorProfile('6.5'),
170+
$connection,
171+
Uuid::randomHex(),
172+
new ProductDataSet(),
173+
0,
174+
250
175+
);
176+
}
177+
}

0 commit comments

Comments
 (0)