Skip to content

Commit 57430a4

Browse files
committed
ACP2E-3376: split insert into duplicate into inserts and updates.
1 parent 6acfd6a commit 57430a4

File tree

3 files changed

+145
-48
lines changed

3 files changed

+145
-48
lines changed

app/code/Magento/Catalog/Model/Product/Price/PricePersistence.php

Lines changed: 86 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?php
22
/**
3-
* Copyright © Magento, Inc. All rights reserved.
4-
* See COPYING.txt for license details.
3+
* Copyright 2016 Adobe
4+
* All Rights Reserved.
55
*/
66

77
namespace Magento\Catalog\Model\Product\Price;
@@ -134,17 +134,16 @@ public function get(array $skus)
134134
public function update(array $prices)
135135
{
136136
array_walk($prices, function (&$price) {
137-
return $price['attribute_id'] = $this->getAttributeId();
137+
return $price['attribute_id'] = (int)$this->getAttributeId();
138138
});
139+
139140
$connection = $this->attributeResource->getConnection();
140141
$connection->beginTransaction();
141142
try {
142143
foreach (array_chunk($prices, $this->itemsPerOperation) as $pricesBunch) {
143-
$this->attributeResource->getConnection()->insertOnDuplicate(
144-
$this->attributeResource->getTable($this->table),
145-
$pricesBunch,
146-
['value']
147-
);
144+
$existingPrices = $this->getExistingPrices($pricesBunch);
145+
$pricesBunch = $this->doUpdate($pricesBunch, $existingPrices);
146+
$this->doInsert($pricesBunch);
148147
}
149148
$connection->commit();
150149
} catch (\Exception $e) {
@@ -156,6 +155,85 @@ public function update(array $prices)
156155
}
157156
}
158157

158+
/**
159+
* Get prices that will need update
160+
*
161+
* @param array $priceBunch
162+
* @return array
163+
*/
164+
private function getExistingPrices(array $priceBunch): array
165+
{
166+
$linkField = $this->getEntityLinkField();
167+
$connection = $this->attributeResource->getConnection();
168+
169+
return $connection->fetchAll(
170+
$connection->select()
171+
->from($this->attributeResource->getTable($this->table))
172+
->where('attribute_id = ?', $this->getAttributeId())
173+
->where('store_id IN (?)', array_unique(array_column($priceBunch, 'store_id')))
174+
->where($linkField . ' IN (?)', array_unique(array_column($priceBunch, $linkField)))
175+
);
176+
}
177+
178+
/**
179+
* Update existing prices
180+
*
181+
* @param array $priceBunches
182+
* @param array $existingPrices
183+
* @return array
184+
*/
185+
private function doUpdate(array $priceBunches, array $existingPrices): array
186+
{
187+
$updateData = [];
188+
$linkField = $this->getEntityLinkField();
189+
foreach ($existingPrices as $existingPrice) {
190+
foreach ($priceBunches as $key => $price) {
191+
if ($price[$linkField] == $existingPrice[$linkField] &&
192+
$price['store_id'] == $existingPrice['store_id'] &&
193+
$existingPrice['attribute_id'] == $price['attribute_id']
194+
) {
195+
$priceBunches[$key]['value_id'] = $existingPrice['value_id'];
196+
$uniqueKey = $price[$linkField].$price['attribute_id'].$price['store_id'];
197+
$updateData[$uniqueKey] = $priceBunches[$key];
198+
}
199+
}
200+
}
201+
if (!empty($updateData)) {
202+
foreach ($updateData as $row) {
203+
$this->attributeResource->getConnection()->update(
204+
$this->attributeResource->getTable($this->table),
205+
['value' => $row['value']],
206+
['value_id = ?' => (int)$row['value_id']]
207+
);
208+
}
209+
}
210+
return $priceBunches;
211+
}
212+
213+
/**
214+
* Insert new prices
215+
*
216+
* @param array $priceBunches
217+
* @return void
218+
*/
219+
private function doInsert(array $priceBunches): void
220+
{
221+
$insertData = [];
222+
$linkField = $this->getEntityLinkField();
223+
foreach ($priceBunches as $price) {
224+
if (!isset($price['value_id'])) {
225+
$uniqueKey = $price[$linkField].$price['attribute_id'].$price['store_id'];
226+
$insertData[$uniqueKey] = $price;
227+
}
228+
}
229+
if (!empty($insertData)) {
230+
$this->attributeResource->getConnection()->insertMultiple(
231+
$this->attributeResource->getTable($this->table),
232+
$insertData
233+
);
234+
}
235+
}
236+
159237
/**
160238
* Delete product attribute by SKU.
161239
*

app/code/Magento/Catalog/Test/Unit/Model/Product/Price/PricePersistenceTest.php

Lines changed: 56 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,30 @@
11
<?php
22
/**
3-
* Copyright © Magento, Inc. All rights reserved.
4-
* See COPYING.txt for license details.
3+
* Copyright 2016 Adobe
4+
* All Rights Reserved.
55
*/
66
declare(strict_types=1);
77

88
namespace Magento\Catalog\Test\Unit\Model\Product\Price;
99

1010
use Magento\Catalog\Api\Data\ProductAttributeInterface;
11+
use Magento\Catalog\Api\Data\ProductInterface;
1112
use Magento\Catalog\Api\ProductAttributeRepositoryInterface;
1213
use Magento\Catalog\Model\Product\Price\PricePersistence;
1314
use Magento\Catalog\Model\Product\Type;
1415
use Magento\Catalog\Model\ProductIdLocatorInterface;
1516
use Magento\Catalog\Model\ResourceModel\Attribute;
1617
use Magento\Framework\DB\Adapter\AdapterInterface;
1718
use Magento\Framework\DB\Select;
19+
use Magento\Framework\EntityManager\EntityMetadataInterface;
1820
use Magento\Framework\EntityManager\MetadataPool;
1921
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
2022
use PHPUnit\Framework\MockObject\MockObject;
2123
use PHPUnit\Framework\TestCase;
2224

25+
/**
26+
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
27+
*/
2328
class PricePersistenceTest extends TestCase
2429
{
2530
/**
@@ -155,39 +160,50 @@ public function testGet()
155160
*/
156161
public function testUpdate()
157162
{
158-
$attributeId = 5;
163+
$attributeId = 77;
159164
$prices = [
160165
[
161166
'store_id' => 1,
162167
'row_id' => 1,
163168
'value' => 15
169+
],
170+
[
171+
'store_id' => 0,
172+
'row_id' => 2,
173+
'value' => 20
164174
]
165175
];
176+
177+
$metadataEntity = $this->createMock(EntityMetadataInterface::class);
178+
$select = $this->createMock(Select::class);
179+
180+
$metadataEntity->expects($this->atLeastOnce())->method('getLinkField')->willReturn('row_id');
181+
$select->expects($this->once())->method('from')->with('catalog_product_entity_decimal')->willReturnSelf();
182+
$select->expects($this->atLeastOnce())->method('where')->willReturnSelf();
183+
184+
$this->metadataPool->expects($this->atLeastOnce())
185+
->method('getMetadata')
186+
->with(ProductInterface::class)
187+
->willReturn($metadataEntity);
166188
$this->attributeRepository->expects($this->once())->method('get')->willReturn($this->productAttribute);
167189
$this->productAttribute->expects($this->once())->method('getAttributeId')->willReturn($attributeId);
168190
$this->attributeResource->expects($this->atLeastOnce())->method('getConnection')->willReturn($this->connection);
169191
$this->connection->expects($this->once())->method('beginTransaction')->willReturnSelf();
192+
$this->connection->expects($this->atLeastOnce())->method('select')->willReturn($select);
193+
$this->connection->expects($this->atLeastOnce())
194+
->method('fetchAll')
195+
->willReturn(
196+
[
197+
['value_id' => 1, 'row_id' => 1, 'attribute_id' => 77, 'store_id' => 1, 'value' => 10]
198+
]
199+
);
200+
$this->connection->expects($this->once())->method('update')->willReturn(1);
201+
$this->connection->expects($this->once())->method('insertMultiple')->willReturn(1);
170202
$this->attributeResource
171-
->expects($this->once())
203+
->expects($this->atLeastOnce())
172204
->method('getTable')
173205
->with('catalog_product_entity_decimal')
174206
->willReturn('catalog_product_entity_decimal');
175-
$this->connection
176-
->expects($this->once())
177-
->method('insertOnDuplicate')
178-
->with(
179-
'catalog_product_entity_decimal',
180-
[
181-
[
182-
'store_id' => 1,
183-
'row_id' => 1,
184-
'value' => 15,
185-
'attribute_id' => 5,
186-
]
187-
],
188-
['value']
189-
)
190-
->willReturnSelf();
191207
$this->connection->expects($this->once())->method('commit')->willReturnSelf();
192208
$this->model->update($prices);
193209
}
@@ -207,31 +223,34 @@ public function testUpdateWithException()
207223
'value' => 15
208224
]
209225
];
226+
$metadataEntity = $this->createMock(EntityMetadataInterface::class);
227+
$select = $this->createMock(Select::class);
228+
229+
$metadataEntity->expects($this->atLeastOnce())->method('getLinkField')->willReturn('row_id');
230+
$select->expects($this->once())->method('from')->with('catalog_product_entity_decimal')->willReturnSelf();
231+
$select->expects($this->atLeastOnce())->method('where')->willReturnSelf();
232+
233+
$this->metadataPool->expects($this->atLeastOnce())
234+
->method('getMetadata')
235+
->with(ProductInterface::class)
236+
->willReturn($metadataEntity);
210237
$this->attributeRepository->expects($this->once())->method('get')->willReturn($this->productAttribute);
211238
$this->productAttribute->expects($this->once())->method('getAttributeId')->willReturn($attributeId);
212239
$this->attributeResource->expects($this->atLeastOnce())->method('getConnection')->willReturn($this->connection);
213240
$this->connection->expects($this->once())->method('beginTransaction')->willReturnSelf();
241+
$this->connection->expects($this->atLeastOnce())->method('select')->willReturn($select);
242+
$this->connection->expects($this->atLeastOnce())
243+
->method('fetchAll')
244+
->willReturn(
245+
[
246+
['value_id' => 1, 'row_id' => 1, 'attribute_id' => 77, 'store_id' => 1, 'value' => 10]
247+
]
248+
);
214249
$this->attributeResource
215-
->expects($this->once())
250+
->expects($this->exactly(2))
216251
->method('getTable')
217252
->with('catalog_product_entity_decimal')
218253
->willReturn('catalog_product_entity_decimal');
219-
$this->connection
220-
->expects($this->once())
221-
->method('insertOnDuplicate')
222-
->with(
223-
'catalog_product_entity_decimal',
224-
[
225-
[
226-
'store_id' => 1,
227-
'row_id' => 1,
228-
'value' => 15,
229-
'attribute_id' => 5,
230-
]
231-
],
232-
['value']
233-
)
234-
->willReturnSelf();
235254
$this->connection->expects($this->once())->method('commit')->willThrowException(new \Exception());
236255
$this->connection->expects($this->once())->method('rollback')->willReturnSelf();
237256
$this->model->update($prices);

app/code/Magento/Catalog/etc/db_schema.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<?xml version="1.0"?>
22
<!--
33
/**
4-
* Copyright © Magento, Inc. All rights reserved.
5-
* See COPYING.txt for license details.
4+
* Copyright 2018 Adobe
5+
* All Rights Reserved.
66
*/
77
-->
88
<schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
@@ -69,7 +69,7 @@
6969
</table>
7070
<table name="catalog_product_entity_decimal" resource="default" engine="innodb"
7171
comment="Catalog Product Decimal Attribute Backend Table">
72-
<column xsi:type="int" name="value_id" unsigned="false" nullable="false" identity="true"
72+
<column xsi:type="int" name="value_id" unsigned="true" nullable="false" identity="true"
7373
comment="Value ID"/>
7474
<column xsi:type="smallint" name="attribute_id" unsigned="true" nullable="false" identity="false"
7575
default="0" comment="Attribute ID"/>

0 commit comments

Comments
 (0)