Skip to content

Commit 375f889

Browse files
committed
Merge branch 'ACP2E-1391' of https://github.com/magento-l3/magento2ce into PR-06022023
2 parents 292d1ca + bbd575a commit 375f889

File tree

3 files changed

+194
-0
lines changed

3 files changed

+194
-0
lines changed

app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ class Configurable extends \Magento\CatalogImportExport\Model\Import\Product\Typ
157157
*
158158
* @var \Magento\Framework\DB\Adapter\AdapterInterface
159159
* @deprecated 100.2.0
160+
* @see No longer used
160161
*/
161162
protected $_connection;
162163

@@ -563,6 +564,18 @@ protected function _parseVariations($rowData)
563564

564565
$fieldAndValuePairs = [];
565566
foreach ($fieldAndValuePairsText as $nameAndValue) {
567+
// If field value contains comma. For example: sku=C100-10,2cm,size=10,2cm
568+
// then this results in $fieldAndValuePairsText = ["sku=C100-10", "2cm", "size=10", "2cm"]
569+
// This code block makes sure that the array element that do not contain the equal sign "="
570+
// will be appended to the preceding element value.
571+
// As a result $fieldAndValuePairs = ["sku" => "C100-10,2cm", "size" => "10,2cm"]
572+
if (strpos($nameAndValue, ImportProduct::PAIR_NAME_VALUE_SEPARATOR) === false
573+
&& isset($fieldName)
574+
&& isset($fieldAndValuePairs[$fieldName])
575+
) {
576+
$fieldAndValuePairs[$fieldName] .= $this->_entityModel->getMultipleValueSeparator() . $nameAndValue;
577+
continue;
578+
}
566579
$nameAndValue = explode(ImportProduct::PAIR_NAME_VALUE_SEPARATOR, $nameAndValue, 2);
567580
if ($nameAndValue) {
568581
$value = isset($nameAndValue[1]) ? trim($nameAndValue[1]) : '';
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\ImportExport\Test\Fixture;
9+
10+
use Magento\Framework\App\Filesystem\DirectoryList;
11+
use Magento\Framework\DataObject;
12+
use Magento\Framework\DataObjectFactory;
13+
use Magento\Framework\Filesystem;
14+
use Magento\TestFramework\Fixture\Data\ProcessorInterface;
15+
use Magento\TestFramework\Fixture\RevertibleDataFixtureInterface;
16+
17+
class CsvFile implements RevertibleDataFixtureInterface
18+
{
19+
private const DEFAULT_DATA = [
20+
'directory' => DirectoryList::TMP,
21+
'path' => 'import/%uniqid%.csv',
22+
'rows' => [],
23+
];
24+
25+
/**
26+
* @var Filesystem
27+
*/
28+
private Filesystem $filesystem;
29+
30+
/**
31+
* @var ProcessorInterface
32+
*/
33+
private ProcessorInterface $dataProcessor;
34+
35+
/**
36+
* @var DataObjectFactory
37+
*/
38+
private DataObjectFactory $dataObjectFactory;
39+
40+
/**
41+
* @param Filesystem $filesystem
42+
* @param ProcessorInterface $dataProcessor
43+
* @param DataObjectFactory $dataObjectFactory
44+
*/
45+
public function __construct(
46+
Filesystem $filesystem,
47+
ProcessorInterface $dataProcessor,
48+
DataObjectFactory $dataObjectFactory
49+
) {
50+
$this->filesystem = $filesystem;
51+
$this->dataProcessor = $dataProcessor;
52+
$this->dataObjectFactory = $dataObjectFactory;
53+
}
54+
55+
/**
56+
* {@inheritdoc}
57+
* @param array $data Parameters. Same format as CsvFile::DEFAULT_DATA.
58+
* Additional fields:
59+
* - $data['rows']: CSV data to be written into the file in the following format:
60+
* - headers are listed in the first array and the following array
61+
* [
62+
* ['col1', 'col2'],
63+
* ['row1col1', 'row1col2'],
64+
* ]
65+
* - headers are listed as array keys
66+
* [
67+
* ['col1' => 'row1col1', 'col2' => 'row1col2'],
68+
* ['col1' => 'row2col1', 'col2' => 'row2col2'],
69+
* [
70+
*
71+
* @see CsvFile::DEFAULT_DATA
72+
*/
73+
public function apply(array $data = []): ?DataObject
74+
{
75+
$data = $this->dataProcessor->process($this, array_merge(self::DEFAULT_DATA, $data));
76+
$rows = $data['rows'];
77+
$row = reset($rows);
78+
79+
if (array_is_list($row)) {
80+
$cols = $row;
81+
$colsCount = count($cols);
82+
foreach ($rows as $row) {
83+
if ($colsCount !== count($row)) {
84+
throw new \InvalidArgumentException('Arrays in "rows" must be the same size');
85+
}
86+
}
87+
} else {
88+
$cols = array_keys($row);
89+
$lines[] = $cols;
90+
foreach ($rows as $row) {
91+
$line = [];
92+
if (array_diff($cols, array_keys($row))) {
93+
throw new \InvalidArgumentException('Arrays in "rows" must have same keys');
94+
}
95+
foreach ($cols as $field) {
96+
$line[] = $row[$field];
97+
}
98+
$lines[] = $line;
99+
}
100+
$rows = $lines;
101+
}
102+
$directory = $this->filesystem->getDirectoryWrite($data['directory']);
103+
$file = $directory->openFile($data['path'], 'w+');
104+
foreach ($rows as $row) {
105+
$file->writeCsv($row);
106+
}
107+
$file->close();
108+
$data['absolute_path'] = $directory->getAbsolutePath($data['path']);
109+
110+
return $this->dataObjectFactory->create(['data' => $data]);
111+
}
112+
113+
/**
114+
* @inheritDoc
115+
*/
116+
public function revert(DataObject $data): void
117+
{
118+
$directory = $this->filesystem->getDirectoryWrite($data['directory']);
119+
$directory->delete($data['path']);
120+
}
121+
}

dev/tests/integration/testsuite/Magento/ConfigurableImportExport/Model/Import/Product/Type/ConfigurableTest.php

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@
88
use Magento\Catalog\Api\Data\ProductInterface;
99
use Magento\Catalog\Api\ProductRepositoryInterface;
1010
use Magento\Catalog\Model\Product;
11+
use Magento\Catalog\Test\Fixture\Product as ProductFixture;
1112
use Magento\CatalogInventory\Api\Data\StockItemInterface;
1213
use Magento\CatalogInventory\Api\StockConfigurationInterface;
1314
use Magento\CatalogInventory\Api\StockItemRepositoryInterface;
15+
use Magento\ConfigurableProduct\Test\Fixture\Attribute as AttributeFixture;
16+
use Magento\ConfigurableProduct\Test\Fixture\Product as ConfigurableProductFixture;
1417
use Magento\Framework\App\Filesystem\DirectoryList;
1518
use Magento\Framework\EntityManager\EntityMetadata;
1619
use Magento\Framework\EntityManager\MetadataPool;
@@ -20,7 +23,10 @@
2023
use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregatorInterface;
2124
use Magento\ImportExport\Model\Import\Adapter as ImportAdapter;
2225
use Magento\CatalogInventory\Api\StockItemCriteriaInterfaceFactory;
26+
use Magento\ImportExport\Test\Fixture\CsvFile as CsvFileFixture;
2327
use Magento\Store\Model\Store;
28+
use Magento\TestFramework\Fixture\DataFixture;
29+
use Magento\TestFramework\Fixture\DataFixtureStorageManager;
2430
use Magento\TestFramework\Helper\Bootstrap;
2531
use PHPUnit\Framework\TestCase;
2632

@@ -252,6 +258,60 @@ private function getStockItem(int $productId): ?StockItemInterface
252258
return reset($stockItems);
253259
}
254260

261+
#[
262+
DataFixture(ProductFixture::class, ['sku' => 'cp1-10,2cm'], as: 'p1'),
263+
DataFixture(ProductFixture::class, ['sku' => 'cp1-15,5cm'], as: 'p2'),
264+
DataFixture(
265+
AttributeFixture::class,
266+
[
267+
'attribute_code' => 'size',
268+
'options' => [['label' => '10,2cm'], ['label' => '15,5cm']],
269+
],
270+
as: 'attr'
271+
),
272+
DataFixture(
273+
ConfigurableProductFixture::class,
274+
['_options' => ['$attr$'], '_links' => ['$p1$', '$p2$']],
275+
'cp1'
276+
),
277+
DataFixture(
278+
CsvFileFixture::class,
279+
[
280+
'rows' => [
281+
['sku', 'configurable_variations'],
282+
['$cp1.sku$', 'sku=cp1-10,2cm,size=10,2cm|sku=cp1-15,5cm,size=15,5cm'],
283+
]
284+
],
285+
'file'
286+
)
287+
]
288+
public function testSpecialCharactersInConfigurableVariations(): void
289+
{
290+
$fixtures = DataFixtureStorageManager::getStorage();
291+
$attrId = $fixtures->get('attr')->getId();
292+
$sku = $fixtures->get('cp1')->getSku();
293+
$p1Id = $fixtures->get('p1')->getId();
294+
$p2Id = $fixtures->get('p2')->getId();
295+
$pathToFile = $fixtures->get('file')->getAbsolutePath();
296+
$errors = $this->doImport($pathToFile, Import::BEHAVIOR_APPEND);
297+
$this->assertEquals(
298+
0,
299+
$errors->getErrorsCount(),
300+
implode(PHP_EOL, array_map(fn ($error) => $error->getErrorMessage(), $errors->getAllErrors()))
301+
);
302+
/** @var ProductRepositoryInterface $productRepository */
303+
$productRepository = $this->objectManager->get(ProductRepositoryInterface::class);
304+
/** @var ProductInterface $product */
305+
$product = $productRepository->get($sku, forceReload: true);
306+
$options = $product->getExtensionAttributes()->getConfigurableProductOptions();
307+
$this->assertCount(1, $options);
308+
$this->assertEquals($attrId, reset($options)->getAttributeId());
309+
$childIds = $product->getExtensionAttributes()->getConfigurableProductLinks();
310+
$this->assertCount(2, $childIds);
311+
$this->assertContains($p1Id, $childIds);
312+
$this->assertContains($p2Id, $childIds);
313+
}
314+
255315
/**
256316
* @param string $file
257317
* @param string $behavior

0 commit comments

Comments
 (0)