Skip to content

Commit 42830a6

Browse files
committed
Merge branch 'MP2E-86' of https://github.com/magento-l3/magento2ce into L3-PR-20210908
2 parents 010a884 + 397fbc5 commit 42830a6

File tree

2 files changed

+142
-48
lines changed

2 files changed

+142
-48
lines changed

app/code/Magento/CatalogImportExport/Model/Import/Product.php

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -821,6 +821,7 @@ class Product extends AbstractEntity
821821
* @throws \Magento\Framework\Exception\FileSystemException
822822
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
823823
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
824+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
824825
*/
825826
public function __construct(
826827
\Magento\Framework\Json\Helper\Data $jsonHelper,
@@ -1344,6 +1345,40 @@ protected function _saveProductAttributes(array $attributesData)
13441345
return $this;
13451346
}
13461347

1348+
/**
1349+
* Get data for updating product-category relations
1350+
*
1351+
* @param array $categoriesData
1352+
* @param string $tableName
1353+
* @return array
1354+
*/
1355+
private function getProductCategoriesDataSave(array $categoriesData, string $tableName): array
1356+
{
1357+
$delProductId = [];
1358+
$categoriesIn = [];
1359+
$minCategoryPosition = [];
1360+
foreach ($categoriesData as $delSku => $categories) {
1361+
$productId = $this->skuProcessor->getNewSku($delSku)['entity_id'];
1362+
$delProductId[] = $productId;
1363+
1364+
foreach (array_keys($categories) as $categoryId) {
1365+
//position new products before existing ones
1366+
if (!isset($minCategoryPosition[$categoryId])) {
1367+
$select = $this->_connection->select()
1368+
->from($tableName, ['position' => new \Zend_Db_Expr('MIN(position)')])
1369+
->where('category_id = ?', $categoryId);
1370+
$minCategoryPosition[$categoryId] = (int)$this->_connection->fetchOne($select);
1371+
}
1372+
$categoriesIn[] = [
1373+
'product_id' => $productId,
1374+
'category_id' => $categoryId,
1375+
'position' => --$minCategoryPosition[$categoryId]
1376+
];
1377+
}
1378+
}
1379+
return [$delProductId, $categoriesIn];
1380+
}
1381+
13471382
/**
13481383
* Save product categories.
13491384
*
@@ -1358,17 +1393,8 @@ protected function _saveProductCategories(array $categoriesData)
13581393
$tableName = $this->_resourceFactory->create()->getProductCategoryTable();
13591394
}
13601395
if ($categoriesData) {
1361-
$categoriesIn = [];
1362-
$delProductId = [];
1363-
1364-
foreach ($categoriesData as $delSku => $categories) {
1365-
$productId = $this->skuProcessor->getNewSku($delSku)['entity_id'];
1366-
$delProductId[] = $productId;
1396+
list($delProductId, $categoriesIn) = $this->getProductCategoriesDataSave($categoriesData, $tableName);
13671397

1368-
foreach (array_keys($categories) as $categoryId) {
1369-
$categoriesIn[] = ['product_id' => $productId, 'category_id' => $categoryId, 'position' => 0];
1370-
}
1371-
}
13721398
if (Import::BEHAVIOR_APPEND != $this->getBehavior()) {
13731399
$this->_connection->delete(
13741400
$tableName,
@@ -3268,7 +3294,7 @@ private function findImageByHash(array $images, string $hash): string
32683294
*/
32693295
private function getProductMediaPath(): string
32703296
{
3271-
return $this->joinFilePaths($this->getMediaBasePath(), 'catalog','product');
3297+
return $this->joinFilePaths($this->getMediaBasePath(), 'catalog', 'product');
32723298
}
32733299

32743300
/**
@@ -3289,7 +3315,7 @@ private function getMediaBasePath(): string
32893315
/**
32903316
* Joins two paths and remove redundant directory separator
32913317
*
3292-
* @param string ...$paths
3318+
* @param array $paths
32933319
* @return string
32943320
*/
32953321
private function joinFilePaths(...$paths): string

app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php

Lines changed: 104 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
use Magento\Framework\App\Filesystem\DirectoryList;
3434
use Magento\Framework\App\ResourceConnection;
3535
use Magento\Framework\DB\Adapter\AdapterInterface;
36+
use Magento\Framework\DB\Select;
3637
use Magento\Framework\EntityManager\EntityMetadata;
3738
use Magento\Framework\EntityManager\MetadataPool;
3839
use Magento\Framework\Event\ManagerInterface;
@@ -61,6 +62,7 @@
6162
* @SuppressWarnings(PHPMD.TooManyFields)
6263
* @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
6364
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
65+
* @SuppressWarnings(PHPMD.ExcessivePublicCount)
6466
*/
6567
class ProductTest extends AbstractImportTestCase
6668
{
@@ -299,6 +301,9 @@ class ProductTest extends AbstractImportTestCase
299301
*/
300302
private $driverFile;
301303

304+
/** @var Select|MockObject */
305+
protected $select;
306+
302307
/**
303308
* @inheritDoc
304309
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
@@ -566,6 +571,13 @@ protected function _parentObjectConstructor()
566571
$this->config->expects($this->any())->method('getEntityType')->with(self::ENTITY_TYPE_CODE)->willReturn($type);
567572

568573
$this->_connection = $this->getMockForAbstractClass(AdapterInterface::class);
574+
$this->select = $this->getMockBuilder(Select::class)
575+
->disableOriginalConstructor()
576+
->setMethods(['from', 'where'])
577+
->getMock();
578+
$this->select->expects($this->any())->method('from')->willReturnSelf();
579+
//$this->select->expects($this->any())->method('where')->willReturnSelf();
580+
$this->_connection->expects($this->any())->method('select')->willReturn($this->select);
569581
$this->resource->expects($this->any())->method('getConnection')->willReturn($this->_connection);
570582
return $this;
571583
}
@@ -661,8 +673,8 @@ protected function _initImagesArrayKeys()
661673
}
662674

663675
/**
664-
* @return void
665-
*/
676+
* @return void
677+
*/
666678
public function testSaveProductAttributes(): void
667679
{
668680
$testTable = 'test_table';
@@ -744,8 +756,8 @@ public function testIsAttributeValidAssertAttrInvalid($attrParams, $rowData): vo
744756
}
745757

746758
/**
747-
* @return void
748-
*/
759+
* @return void
760+
*/
749761
public function testGetMultipleValueSeparatorDefault(): void
750762
{
751763
$this->setPropertyValue($this->importProduct, '_parameters', null);
@@ -756,8 +768,8 @@ public function testGetMultipleValueSeparatorDefault(): void
756768
}
757769

758770
/**
759-
* @return void
760-
*/
771+
* @return void
772+
*/
761773
public function testGetMultipleValueSeparatorFromParameters(): void
762774
{
763775
$expectedSeparator = 'value';
@@ -776,8 +788,8 @@ public function testGetMultipleValueSeparatorFromParameters(): void
776788
}
777789

778790
/**
779-
* @return void
780-
*/
791+
* @return void
792+
*/
781793
public function testGetEmptyAttributeValueConstantDefault(): void
782794
{
783795
$this->setPropertyValue($this->importProduct, '_parameters', null);
@@ -788,8 +800,8 @@ public function testGetEmptyAttributeValueConstantDefault(): void
788800
}
789801

790802
/**
791-
* @return void
792-
*/
803+
* @return void
804+
*/
793805
public function testGetEmptyAttributeValueConstantFromParameters(): void
794806
{
795807
$expectedSeparator = '__EMPTY__VALUE__TEST__';
@@ -808,8 +820,8 @@ public function testGetEmptyAttributeValueConstantFromParameters(): void
808820
}
809821

810822
/**
811-
* @return void
812-
*/
823+
* @return void
824+
*/
813825
public function testDeleteProductsForReplacement(): void
814826
{
815827
$importProduct = $this->getMockBuilder(Product::class)
@@ -830,8 +842,8 @@ public function testDeleteProductsForReplacement(): void
830842
}
831843

832844
/**
833-
* @return void
834-
*/
845+
* @return void
846+
*/
835847
public function testGetMediaGalleryAttributeIdIfNotSetYet(): void
836848
{
837849
// reset possible existing id
@@ -903,8 +915,8 @@ public function testValidateRow($rowScope, $oldSku, $expectedResult, $behaviour
903915
}
904916

905917
/**
906-
* @return void
907-
*/
918+
* @return void
919+
*/
908920
public function testValidateRowDeleteBehaviourAddRowErrorCall(): void
909921
{
910922
$importProduct = $this->getMockBuilder(Product::class)
@@ -929,8 +941,8 @@ public function testValidateRowDeleteBehaviourAddRowErrorCall(): void
929941
}
930942

931943
/**
932-
* @return void
933-
*/
944+
* @return void
945+
*/
934946
public function testValidateRowValidatorCheck(): void
935947
{
936948
$messages = ['validator message'];
@@ -1100,8 +1112,8 @@ public function testValidateRowCheckSpecifiedSku($sku, $expectedError): void
11001112
}
11011113

11021114
/**
1103-
* @return void
1104-
*/
1115+
* @return void
1116+
*/
11051117
public function testValidateRowProcessEntityIncrement(): void
11061118
{
11071119
$count = 0;
@@ -1118,8 +1130,8 @@ public function testValidateRowProcessEntityIncrement(): void
11181130
}
11191131

11201132
/**
1121-
* @return void
1122-
*/
1133+
* @return void
1134+
*/
11231135
public function testValidateRowValidateExistingProductTypeAddNewSku(): void
11241136
{
11251137
$importProduct = $this->createModelMockWithErrorAggregator(
@@ -1167,8 +1179,8 @@ public function testValidateRowValidateExistingProductTypeAddNewSku(): void
11671179
}
11681180

11691181
/**
1170-
* @return void
1171-
*/
1182+
* @return void
1183+
*/
11721184
public function testValidateRowValidateExistingProductTypeAddErrorRowCall(): void
11731185
{
11741186
$sku = 'sku';
@@ -1249,8 +1261,8 @@ public function testValidateRowValidateNewProductTypeAddRowErrorCall(
12491261
}
12501262

12511263
/**
1252-
* @return void
1253-
*/
1264+
* @return void
1265+
*/
12541266
public function testValidateRowValidateNewProductTypeGetNewSkuCall(): void
12551267
{
12561268
$sku = 'sku';
@@ -1296,8 +1308,8 @@ public function testValidateRowValidateNewProductTypeGetNewSkuCall(): void
12961308
}
12971309

12981310
/**
1299-
* @return void
1300-
*/
1311+
* @return void
1312+
*/
13011313
public function testValidateDefaultScopeNotValidAttributesResetSku(): void
13021314
{
13031315
$this->validator->expects($this->once())->method('isAttributeValid')->willReturn(false);
@@ -1309,8 +1321,8 @@ public function testValidateDefaultScopeNotValidAttributesResetSku(): void
13091321
}
13101322

13111323
/**
1312-
* @return void
1313-
*/
1324+
* @return void
1325+
*/
13141326
public function testValidateRowSetAttributeSetCodeIntoRowData(): void
13151327
{
13161328
$sku = 'sku';
@@ -1360,8 +1372,8 @@ public function testValidateRowSetAttributeSetCodeIntoRowData(): void
13601372
}
13611373

13621374
/**
1363-
* @return void
1364-
*/
1375+
* @return void
1376+
*/
13651377
public function testValidateValidateOptionEntity(): void
13661378
{
13671379
$sku = 'sku';
@@ -1407,8 +1419,8 @@ public function testGetImagesFromRow($rowData, $expectedResult): void
14071419
}
14081420

14091421
/**
1410-
* @return void
1411-
*/
1422+
* @return void
1423+
*/
14121424
public function testParseAttributesWithoutWrappedValuesWillReturnsLowercasedAttributeCodes(): void
14131425
{
14141426
$attributesData = 'PARAM1=value1,param2=value2';
@@ -1428,8 +1440,8 @@ public function testParseAttributesWithoutWrappedValuesWillReturnsLowercasedAttr
14281440
}
14291441

14301442
/**
1431-
* @return void
1432-
*/
1443+
* @return void
1444+
*/
14331445
public function testParseAttributesWithWrappedValuesWillReturnsLowercasedAttributeCodes(): void
14341446
{
14351447
$attribute1 = $this->getMockBuilder(AbstractAttribute::class)->disableOriginalConstructor()
@@ -1584,6 +1596,62 @@ function ($name) use ($throwException, $exception) {
15841596
);
15851597
}
15861598

1599+
/**
1600+
* Check that getProductCategoriesDataSave method will return array with product-category-position relations
1601+
* where new products positioned before existing
1602+
*
1603+
* @param array $categoriesData
1604+
* @param string $tableName
1605+
* @param array $result
1606+
* @dataProvider productCategoriesDataProvider
1607+
*/
1608+
public function testGetProductCategoriesDataSave(array $categoriesData, string $tableName, array $result)
1609+
{
1610+
$this->_connection->expects($this->at(1))->method('fetchOne')->willReturn('0');
1611+
$this->_connection->expects($this->at(3))->method('fetchOne')->willReturn('-2');
1612+
$this->skuProcessor->expects($this->at(0))->method('getNewSku')->willReturn(['entity_id' => 2]);
1613+
$this->skuProcessor->expects($this->at(1))->method('getNewSku')->willReturn(['entity_id' => 5]);
1614+
$actualResult = $this->invokeMethod(
1615+
$this->importProduct,
1616+
'getProductCategoriesDataSave',
1617+
[$categoriesData, $tableName]
1618+
);
1619+
$this->assertEquals($result, $actualResult);
1620+
}
1621+
1622+
/**
1623+
* Data provider for testGetProductCategoriesDataSave.
1624+
*
1625+
* @return array
1626+
*/
1627+
public function productCategoriesDataProvider()
1628+
{
1629+
return [
1630+
[
1631+
[
1632+
'simple_2' => [3 => true],
1633+
'simple_5' => [5 => true]
1634+
],
1635+
'catalog_category_product',
1636+
[
1637+
[2, 5],
1638+
[
1639+
[
1640+
'product_id' => 2,
1641+
'category_id' => 3,
1642+
'position' => -1
1643+
],
1644+
[
1645+
'product_id' => 5,
1646+
'category_id' => 5,
1647+
'position' => -3
1648+
]
1649+
]
1650+
]
1651+
]
1652+
];
1653+
}
1654+
15871655
/**
15881656
* Data provider for testFillUploaderObject.
15891657
*

0 commit comments

Comments
 (0)