Skip to content

Commit

Permalink
Merge branch '2.4-develop' into ACQE-7152-mainline-functional-tests-d…
Browse files Browse the repository at this point in the history
…eployment
  • Loading branch information
magento-devops-queue-mgr-svc authored Nov 18, 2024
2 parents 68416cb + d50f6b5 commit c8a979a
Show file tree
Hide file tree
Showing 20 changed files with 731 additions and 137 deletions.
27 changes: 12 additions & 15 deletions app/code/Magento/Catalog/Model/Product/Price/PricePersistence.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
* Copyright 2016 Adobe
* All Rights Reserved.
*/

namespace Magento\Catalog\Model\Product\Price;
Expand All @@ -10,6 +10,7 @@
use Magento\Catalog\Api\ProductAttributeRepositoryInterface;
use Magento\Catalog\Model\ProductIdLocatorInterface;
use Magento\Catalog\Model\ResourceModel\Attribute;
use Magento\Catalog\Model\ResourceModel\Product\Price\BasePriceFactory;
use Magento\Framework\App\ObjectManager;
use Magento\Framework\EntityManager\MetadataPool;
use Magento\Framework\Exception\CouldNotDeleteException;
Expand Down Expand Up @@ -88,14 +89,16 @@ class PricePersistence
* @param MetadataPool $metadataPool
* @param string $attributeCode
* @param DateTime|null $dateTime
* @param BasePriceFactory|null $basePriceFactory
*/
public function __construct(
Attribute $attributeResource,
ProductAttributeRepositoryInterface $attributeRepository,
ProductIdLocatorInterface $productIdLocator,
MetadataPool $metadataPool,
$attributeCode = '',
?DateTime $dateTime = null
?DateTime $dateTime = null,
private ?BasePriceFactory $basePriceFactory = null
) {
$this->attributeResource = $attributeResource;
$this->attributeRepository = $attributeRepository;
Expand All @@ -104,6 +107,8 @@ public function __construct(
$this->metadataPool = $metadataPool;
$this->dateTime = $dateTime ?: ObjectManager::getInstance()
->get(DateTime::class);
$this->basePriceFactory = $this->basePriceFactory ?: ObjectManager::getInstance()
->get(BasePriceFactory::class);
}

/**
Expand Down Expand Up @@ -134,21 +139,13 @@ public function get(array $skus)
public function update(array $prices)
{
array_walk($prices, function (&$price) {
return $price['attribute_id'] = $this->getAttributeId();
return $price['attribute_id'] = (int)$this->getAttributeId();
});
$connection = $this->attributeResource->getConnection();
$connection->beginTransaction();

$basePrice = $this->basePriceFactory->create(['attributeId' => (int)$this->getAttributeId()]);
try {
foreach (array_chunk($prices, $this->itemsPerOperation) as $pricesBunch) {
$this->attributeResource->getConnection()->insertOnDuplicate(
$this->attributeResource->getTable($this->table),
$pricesBunch,
['value']
);
}
$connection->commit();
$basePrice->update($prices);
} catch (\Exception $e) {
$connection->rollBack();
throw new CouldNotSaveException(
__('Could not save Prices.'),
$e
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
<?php
/**
* Copyright 2024 Adobe
* All Rights Reserved.
*/
declare(strict_types=1);

namespace Magento\Catalog\Model\ResourceModel\Product\Price;

use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Catalog\Model\ResourceModel\Attribute;
use Magento\Framework\DataObject;
use Magento\Framework\EntityManager\MetadataPool;
use Magento\Framework\Exception\CouldNotSaveException;

class BasePrice
{
/**
* Price storage table.
*
* @var string
*/
private $table = 'catalog_product_entity_decimal';

/**
* @param Attribute $attributeResource
* @param MetadataPool $metadataPool
* @param int $attributeId
*/
public function __construct(
private readonly Attribute $attributeResource,
private readonly MetadataPool $metadataPool,
private readonly int $attributeId = 0,
) {
}

/**
* Get prices that will need update
*
* @param array $priceBunch
* @return array
* @throws \Exception
*/
private function getExistingPrices(array $priceBunch): array
{
$linkField = $this->getEntityLinkField();
$connection = $this->attributeResource->getConnection();

return $connection->fetchAll(
$connection->select()
->from($this->attributeResource->getTable($this->table))
->where('attribute_id = ?', $this->attributeId)
->where('store_id IN (?)', array_unique(array_column($priceBunch, 'store_id')))
->where($linkField . ' IN (?)', array_unique(array_column($priceBunch, $linkField)))
);
}

/**
* Get prices that will need update
*
* @param DataObject $priceBunchesObject
* @param array $existingPrices
* @return array
* @throws \Exception
*/
private function getUpdatablePrices(DataObject $priceBunchesObject, array $existingPrices): array
{
$updateData = [];
$priceBunches = $priceBunchesObject->getPrices();
$linkField = $this->getEntityLinkField();
foreach ($existingPrices as $existingPrice) {
foreach ($priceBunches as $key => $price) {
if ($price[$linkField] == $existingPrice[$linkField] &&
$price['store_id'] == $existingPrice['store_id'] &&
$existingPrice['attribute_id'] == $price['attribute_id']
) {
$priceBunches[$key]['value_id'] = $existingPrice['value_id'];
$uniqueKey = $price[$linkField].'_'.$price['attribute_id'].'_'.$price['store_id'];
$updateData[$uniqueKey] = $priceBunches[$key];
}
}
}

$priceBunchesObject->setPrices($priceBunches);

return $updateData;
}

/**
* Get prices that will need insert
*
* @param DataObject $priceBunchesObject
* @return array
* @throws \Exception
*/
private function getInsertablePrices(DataObject $priceBunchesObject): array
{
$insertData = [];
$priceBunches = $priceBunchesObject->getPrices();
$linkField = $this->getEntityLinkField();
foreach ($priceBunches as $price) {
if (!isset($price['value_id'])) {
$uniqueKey = $price[$linkField].'_'.$price['attribute_id'].'_'.$price['store_id'];
$insertData[$uniqueKey] = $price;
}
}
return $insertData;
}

/**
* Update existing prices
*
* @param array $priceBunches
* @return void
* @throws \Exception
*/
public function update(array $priceBunches): void
{
$priceBunchesObject = new DataObject(['prices' => $priceBunches]);
$existingPrices = $this->getExistingPrices($priceBunches);
$updateData = $this->getUpdatablePrices($priceBunchesObject, $existingPrices);
$insertData = $this->getInsertablePrices($priceBunchesObject);

$connection = $this->attributeResource->getConnection();
$connection->beginTransaction();
try {
if (!empty($updateData)) {
foreach ($updateData as $row) {
$this->attributeResource->getConnection()->update(
$this->attributeResource->getTable($this->table),
['value' => $row['value']],
['value_id = ?' => (int)$row['value_id']]
);
}
}
if (!empty($insertData)) {
$this->attributeResource->getConnection()->insertMultiple(
$this->attributeResource->getTable($this->table),
$insertData
);
}
$connection->commit();
} catch (\Exception $e) {
$connection->rollBack();
throw new CouldNotSaveException(
__('Could not save Prices.'),
$e
);
}
}

/**
* Get link field
*
* @return string
* @throws \Exception
*/
private function getEntityLinkField(): string
{
return $this->metadataPool->getMetadata(ProductInterface::class)
->getLinkField();
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
* Copyright 2016 Adobe
* All Rights Reserved.
*/
declare(strict_types=1);

Expand All @@ -13,13 +13,17 @@
use Magento\Catalog\Model\Product\Type;
use Magento\Catalog\Model\ProductIdLocatorInterface;
use Magento\Catalog\Model\ResourceModel\Attribute;
use Magento\Catalog\Model\ResourceModel\Product\Price\BasePrice;
use Magento\Catalog\Model\ResourceModel\Product\Price\BasePriceFactory;
use Magento\Framework\DB\Adapter\AdapterInterface;
use Magento\Framework\DB\Select;
use Magento\Framework\EntityManager\MetadataPool;
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;

/**
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class PricePersistenceTest extends TestCase
{
/**
Expand Down Expand Up @@ -52,6 +56,11 @@ class PricePersistenceTest extends TestCase
*/
private $metadataPool;

/**
* @var BasePriceFactory|MockObject
*/
private $basePriceFactory;

/**
* @var PricePersistence
*/
Expand Down Expand Up @@ -86,16 +95,18 @@ protected function setUp(): void
$this->productAttribute = $this->getMockBuilder(ProductAttributeInterface::class)
->disableOriginalConstructor()
->getMockForAbstractClass();
$this->basePriceFactory = $this->getMockBuilder(BasePriceFactory::class)
->disableOriginalConstructor()
->getMock();

$objectManager = new ObjectManager($this);
$this->model = $objectManager->getObject(
PricePersistence::class,
[
'attributeResource' => $this->attributeResource,
'attributeRepository' => $this->attributeRepository,
'productIdLocator' => $this->productIdLocator,
'metadataPool' => $this->metadataPool,
]
$this->model = new PricePersistence(
$this->attributeResource,
$this->attributeRepository,
$this->productIdLocator,
$this->metadataPool,
'price',
null,
$this->basePriceFactory
);
}

Expand Down Expand Up @@ -161,34 +172,21 @@ public function testUpdate()
'store_id' => 1,
'row_id' => 1,
'value' => 15
],
[
'store_id' => 0,
'row_id' => 2,
'value' => 20
]
];
$basePrice = $this->createMock(BasePrice::class);
$basePrice->expects($this->once())
->method('update');
$this->attributeRepository->expects($this->once())->method('get')->willReturn($this->productAttribute);
$this->productAttribute->expects($this->once())->method('getAttributeId')->willReturn($attributeId);
$this->attributeResource->expects($this->atLeastOnce())->method('getConnection')->willReturn($this->connection);
$this->connection->expects($this->once())->method('beginTransaction')->willReturnSelf();
$this->attributeResource
->expects($this->once())
->method('getTable')
->with('catalog_product_entity_decimal')
->willReturn('catalog_product_entity_decimal');
$this->connection
->expects($this->once())
->method('insertOnDuplicate')
->with(
'catalog_product_entity_decimal',
[
[
'store_id' => 1,
'row_id' => 1,
'value' => 15,
'attribute_id' => 5,
]
],
['value']
)
->willReturnSelf();
$this->connection->expects($this->once())->method('commit')->willReturnSelf();
$this->basePriceFactory->expects($this->once())
->method('create')
->willReturn($basePrice);
$this->model->update($prices);
}

Expand All @@ -207,33 +205,15 @@ public function testUpdateWithException()
'value' => 15
]
];
$basePrice = $this->createMock(BasePrice::class);
$basePrice->expects($this->once())
->method('update')
->willThrowException(new \Exception());
$this->attributeRepository->expects($this->once())->method('get')->willReturn($this->productAttribute);
$this->productAttribute->expects($this->once())->method('getAttributeId')->willReturn($attributeId);
$this->attributeResource->expects($this->atLeastOnce())->method('getConnection')->willReturn($this->connection);
$this->connection->expects($this->once())->method('beginTransaction')->willReturnSelf();
$this->attributeResource
->expects($this->once())
->method('getTable')
->with('catalog_product_entity_decimal')
->willReturn('catalog_product_entity_decimal');
$this->connection
->expects($this->once())
->method('insertOnDuplicate')
->with(
'catalog_product_entity_decimal',
[
[
'store_id' => 1,
'row_id' => 1,
'value' => 15,
'attribute_id' => 5,
]
],
['value']
)
->willReturnSelf();
$this->connection->expects($this->once())->method('commit')->willThrowException(new \Exception());
$this->connection->expects($this->once())->method('rollback')->willReturnSelf();
$this->basePriceFactory->expects($this->once())
->method('create')
->willReturn($basePrice);
$this->model->update($prices);
}

Expand Down
Loading

0 comments on commit c8a979a

Please sign in to comment.