From efb7c618a35cb59b392a09572595c26d73488521 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Thu, 1 Aug 2019 12:11:44 -0500 Subject: [PATCH 01/17] MC-18833: Tier price API doesn't take into account indexers mode --- .../Catalog/Model/Indexer/Product/Price.php | 60 ++--- .../Model/Product/Price/TierPriceStorage.php | 133 ++++------ .../Product/Price/TierPriceStorageTest.php | 231 +++++++++--------- .../Indexer/Model/Indexer/CacheCleaner.php | 100 ++++++++ app/code/Magento/Indexer/etc/di.xml | 3 + 5 files changed, 294 insertions(+), 233 deletions(-) create mode 100644 app/code/Magento/Indexer/Model/Indexer/CacheCleaner.php diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Price.php b/app/code/Magento/Catalog/Model/Indexer/Product/Price.php index c9936f7e6c691..b703ba82a4052 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Price.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Price.php @@ -5,43 +5,56 @@ */ namespace Magento\Catalog\Model\Indexer\Product; +use Magento\Catalog\Model\Category as CategoryModel; +use Magento\Catalog\Model\Indexer\Product\Price\Action\Full as FullAction; +use Magento\Catalog\Model\Indexer\Product\Price\Action\Row as RowAction; +use Magento\Catalog\Model\Indexer\Product\Price\Action\Rows as RowsAction; +use Magento\Catalog\Model\Product as ProductModel; +use Magento\Framework\Indexer\ActionInterface as IndexerActionInterface; use Magento\Framework\Indexer\CacheContext; +use Magento\Framework\Mview\ActionInterface as MviewActionInterface; -class Price implements \Magento\Framework\Indexer\ActionInterface, \Magento\Framework\Mview\ActionInterface +/** + * Price indexer + */ +class Price implements IndexerActionInterface, MviewActionInterface { /** - * @var \Magento\Catalog\Model\Indexer\Product\Price\Action\Row + * @var RowAction */ protected $_productPriceIndexerRow; /** - * @var \Magento\Catalog\Model\Indexer\Product\Price\Action\Rows + * @var RowsAction */ protected $_productPriceIndexerRows; /** - * @var \Magento\Catalog\Model\Indexer\Product\Price\Action\Full + * @var FullAction */ protected $_productPriceIndexerFull; /** - * @var \Magento\Framework\Indexer\CacheContext + * @var CacheContext */ private $cacheContext; /** - * @param Price\Action\Row $productPriceIndexerRow - * @param Price\Action\Rows $productPriceIndexerRows - * @param Price\Action\Full $productPriceIndexerFull + * @param RowAction $productPriceIndexerRow + * @param RowsAction $productPriceIndexerRows + * @param FullAction $productPriceIndexerFull + * @param CacheContext $cacheContext */ public function __construct( - \Magento\Catalog\Model\Indexer\Product\Price\Action\Row $productPriceIndexerRow, - \Magento\Catalog\Model\Indexer\Product\Price\Action\Rows $productPriceIndexerRows, - \Magento\Catalog\Model\Indexer\Product\Price\Action\Full $productPriceIndexerFull + RowAction $productPriceIndexerRow, + RowsAction $productPriceIndexerRows, + FullAction $productPriceIndexerFull, + CacheContext $cacheContext ) { $this->_productPriceIndexerRow = $productPriceIndexerRow; $this->_productPriceIndexerRows = $productPriceIndexerRows; $this->_productPriceIndexerFull = $productPriceIndexerFull; + $this->cacheContext = $cacheContext; } /** @@ -53,7 +66,7 @@ public function __construct( public function execute($ids) { $this->_productPriceIndexerRows->execute($ids); - $this->getCacheContext()->registerEntities(\Magento\Catalog\Model\Product::CACHE_TAG, $ids); + $this->cacheContext->registerEntities(ProductModel::CACHE_TAG, $ids); } /** @@ -64,10 +77,10 @@ public function execute($ids) public function executeFull() { $this->_productPriceIndexerFull->execute(); - $this->getCacheContext()->registerTags( + $this->cacheContext->registerTags( [ - \Magento\Catalog\Model\Category::CACHE_TAG, - \Magento\Catalog\Model\Product::CACHE_TAG + CategoryModel::CACHE_TAG, + ProductModel::CACHE_TAG ] ); } @@ -81,6 +94,7 @@ public function executeFull() public function executeList(array $ids) { $this->_productPriceIndexerRows->execute($ids); + $this->cacheContext->registerEntities(ProductModel::CACHE_TAG, $ids); } /** @@ -92,20 +106,6 @@ public function executeList(array $ids) public function executeRow($id) { $this->_productPriceIndexerRow->execute($id); - } - - /** - * Get cache context - * - * @return \Magento\Framework\Indexer\CacheContext - * @deprecated 100.0.11 - */ - protected function getCacheContext() - { - if (!($this->cacheContext instanceof CacheContext)) { - return \Magento\Framework\App\ObjectManager::getInstance()->get(CacheContext::class); - } else { - return $this->cacheContext; - } + $this->cacheContext->registerEntities(ProductModel::CACHE_TAG, [$id]); } } diff --git a/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php b/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php index 3ee064670a460..6efff27fa2287 100644 --- a/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php +++ b/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php @@ -7,11 +7,15 @@ namespace Magento\Catalog\Model\Product\Price; use Magento\Catalog\Api\Data\TierPriceInterface; +use Magento\Catalog\Api\TierPriceStorageInterface; +use Magento\Catalog\Model\Indexer\Product\Price\Processor as PriceIndexerProcessor; +use Magento\Catalog\Model\Product\Price\Validation\TierPriceValidator; +use Magento\Catalog\Model\ProductIdLocatorInterface; /** * Tier price storage. */ -class TierPriceStorage implements \Magento\Catalog\Api\TierPriceStorageInterface +class TierPriceStorage implements TierPriceStorageInterface { /** * Tier price resource model. @@ -23,7 +27,7 @@ class TierPriceStorage implements \Magento\Catalog\Api\TierPriceStorageInterface /** * Tier price validator. * - * @var \Magento\Catalog\Model\Product\Price\Validation\TierPriceValidator + * @var TierPriceValidator */ private $tierPriceValidator; @@ -35,65 +39,38 @@ class TierPriceStorage implements \Magento\Catalog\Api\TierPriceStorageInterface private $tierPriceFactory; /** - * Price indexer. + * Price index processor. * - * @var \Magento\Catalog\Model\Indexer\Product\Price + * @var PriceIndexerProcessor */ - private $priceIndexer; + private $priceIndexProcessor; /** * Product ID locator. * - * @var \Magento\Catalog\Model\ProductIdLocatorInterface + * @var ProductIdLocatorInterface */ private $productIdLocator; - /** - * Page cache config. - * - * @var \Magento\PageCache\Model\Config - */ - private $config; - - /** - * Cache type list. - * - * @var \Magento\Framework\App\Cache\TypeListInterface - */ - private $typeList; - - /** - * Indexer chunk value. - * - * @var int - */ - private $indexerChunkValue = 500; - /** * @param TierPricePersistence $tierPricePersistence - * @param \Magento\Catalog\Model\Product\Price\Validation\TierPriceValidator $tierPriceValidator + * @param TierPriceValidator $tierPriceValidator * @param TierPriceFactory $tierPriceFactory - * @param \Magento\Catalog\Model\Indexer\Product\Price $priceIndexer - * @param \Magento\Catalog\Model\ProductIdLocatorInterface $productIdLocator - * @param \Magento\PageCache\Model\Config $config - * @param \Magento\Framework\App\Cache\TypeListInterface $typeList + * @param PriceIndexerProcessor $priceIndexProcessor + * @param ProductIdLocatorInterface $productIdLocator */ public function __construct( TierPricePersistence $tierPricePersistence, - \Magento\Catalog\Model\Product\Price\Validation\TierPriceValidator $tierPriceValidator, + TierPriceValidator $tierPriceValidator, TierPriceFactory $tierPriceFactory, - \Magento\Catalog\Model\Indexer\Product\Price $priceIndexer, - \Magento\Catalog\Model\ProductIdLocatorInterface $productIdLocator, - \Magento\PageCache\Model\Config $config, - \Magento\Framework\App\Cache\TypeListInterface $typeList + PriceIndexerProcessor $priceIndexProcessor, + ProductIdLocatorInterface $productIdLocator ) { $this->tierPricePersistence = $tierPricePersistence; $this->tierPriceValidator = $tierPriceValidator; $this->tierPriceFactory = $tierPriceFactory; - $this->priceIndexer = $priceIndexer; + $this->priceIndexProcessor = $priceIndexProcessor; $this->productIdLocator = $productIdLocator; - $this->config = $config; - $this->typeList = $typeList; } /** @@ -102,8 +79,10 @@ public function __construct( public function get(array $skus) { $skus = $this->tierPriceValidator->validateSkus($skus); + $skuByIdLookup = $this->buildSkuByIdLookup($skus); + $prices = $this->getExistingPrices($skuByIdLookup); - return $this->getExistingPrices($skus); + return $prices; } /** @@ -111,18 +90,21 @@ public function get(array $skus) */ public function update(array $prices) { - $affectedIds = $this->retrieveAffectedProductIdsForPrices($prices); $skus = array_unique( - array_map(function ($price) { - return $price->getSku(); - }, $prices) + array_map( + function (TierPriceInterface $price) { + return $price->getSku(); + }, + $prices + ) ); - $result = $this->tierPriceValidator->retrieveValidationResult($prices, $this->getExistingPrices($skus, true)); + $skuByIdLookup = $this->buildSkuByIdLookup($skus); + $existingPrices = $this->getExistingPrices($skuByIdLookup, true); + $result = $this->tierPriceValidator->retrieveValidationResult($prices, $existingPrices); $prices = $this->removeIncorrectPrices($prices, $result->getFailedRowIds()); $formattedPrices = $this->retrieveFormattedPrices($prices); $this->tierPricePersistence->update($formattedPrices); - $this->reindexPrices($affectedIds); - $this->invalidateFullPageCache(); + $this->reindexPrices(array_keys($skuByIdLookup)); return $result->getFailedItems(); } @@ -138,7 +120,6 @@ public function replace(array $prices) $formattedPrices = $this->retrieveFormattedPrices($prices); $this->tierPricePersistence->replace($formattedPrices, $affectedIds); $this->reindexPrices($affectedIds); - $this->invalidateFullPageCache(); return $result->getFailedItems(); } @@ -154,7 +135,6 @@ public function delete(array $prices) $priceIds = $this->retrieveAffectedPriceIds($prices); $this->tierPricePersistence->delete($priceIds); $this->reindexPrices($affectedIds); - $this->invalidateFullPageCache(); return $result->getFailedItems(); } @@ -162,18 +142,16 @@ public function delete(array $prices) /** * Get existing prices by SKUs. * - * @param array $skus + * @param array $skuByIdLookup * @param bool $groupBySku [optional] * @return array */ - private function getExistingPrices(array $skus, $groupBySku = false) + private function getExistingPrices(array $skuByIdLookup, bool $groupBySku = false): array { - $ids = $this->retrieveAffectedIds($skus); - $rawPrices = $this->tierPricePersistence->get($ids); + $rawPrices = $this->tierPricePersistence->get(array_keys($skuByIdLookup)); $prices = []; if ($rawPrices) { $linkField = $this->tierPricePersistence->getEntityLinkField(); - $skuByIdLookup = $this->buildSkuByIdLookup($skus); foreach ($rawPrices as $rawPrice) { $sku = $skuByIdLookup[$rawPrice[$linkField]]; $price = $this->tierPriceFactory->create($rawPrice, $sku); @@ -194,7 +172,7 @@ private function getExistingPrices(array $skus, $groupBySku = false) * @param array $prices * @return array */ - private function retrieveFormattedPrices(array $prices) + private function retrieveFormattedPrices(array $prices): array { $formattedPrices = []; @@ -215,12 +193,15 @@ private function retrieveFormattedPrices(array $prices) * @param TierPriceInterface[] $prices * @return array */ - private function retrieveAffectedProductIdsForPrices(array $prices) + private function retrieveAffectedProductIdsForPrices(array $prices): array { $skus = array_unique( - array_map(function ($price) { - return $price->getSku(); - }, $prices) + array_map( + function (TierPriceInterface $price) { + return $price->getSku(); + }, + $prices + ) ); return $this->retrieveAffectedIds($skus); @@ -232,7 +213,7 @@ private function retrieveAffectedProductIdsForPrices(array $prices) * @param array $skus * @return array */ - private function retrieveAffectedIds(array $skus) + private function retrieveAffectedIds(array $skus): array { $affectedIds = []; @@ -249,7 +230,7 @@ private function retrieveAffectedIds(array $skus) * @param array $prices * @return array */ - private function retrieveAffectedPriceIds(array $prices) + private function retrieveAffectedPriceIds(array $prices): array { $affectedIds = $this->retrieveAffectedProductIdsForPrices($prices); $formattedPrices = $this->retrieveFormattedPrices($prices); @@ -270,7 +251,7 @@ private function retrieveAffectedPriceIds(array $prices) * @param array $existingPrices * @return int|null */ - private function retrievePriceId(array $price, array $existingPrices) + private function retrievePriceId(array $price, array $existingPrices): ?int { $linkField = $this->tierPricePersistence->getEntityLinkField(); @@ -281,7 +262,7 @@ private function retrievePriceId(array $price, array $existingPrices) && $this->isCorrectPriceValue($existingPrice, $price) && $existingPrice[$linkField] == $price[$linkField] ) { - return $existingPrice['value_id']; + return (int) $existingPrice['value_id']; } } @@ -295,7 +276,7 @@ private function retrievePriceId(array $price, array $existingPrices) * @param array $price * @return bool */ - private function isCorrectPriceValue(array $existingPrice, array $price) + private function isCorrectPriceValue(array $existingPrice, array $price): bool { return ($existingPrice['value'] != 0 && $existingPrice['value'] == $price['value']) || ($existingPrice['percentage_value'] !== null @@ -308,7 +289,7 @@ private function isCorrectPriceValue(array $existingPrice, array $price) * @param array $skus * @return array */ - private function buildSkuByIdLookup($skus) + private function buildSkuByIdLookup(array $skus): array { $lookup = []; foreach ($this->productIdLocator->retrieveProductIdsBySkus($skus) as $sku => $ids) { @@ -320,28 +301,16 @@ private function buildSkuByIdLookup($skus) return $lookup; } - /** - * Invalidate full page cache. - * - * @return void - */ - private function invalidateFullPageCache() - { - if ($this->config->isEnabled()) { - $this->typeList->invalidate('full_page'); - } - } - /** * Reindex prices. * * @param array $ids * @return void */ - private function reindexPrices(array $ids) + private function reindexPrices(array $ids): void { - foreach (array_chunk($ids, $this->indexerChunkValue) as $affectedIds) { - $this->priceIndexer->execute($affectedIds); + if (!empty($ids)) { + $this->priceIndexProcessor->reindexList($ids); } } @@ -352,7 +321,7 @@ private function reindexPrices(array $ids) * @param array $ids * @return array */ - private function removeIncorrectPrices(array $prices, array $ids) + private function removeIncorrectPrices(array $prices, array $ids): array { foreach ($ids as $id) { unset($prices[$id]); diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/TierPriceStorageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/TierPriceStorageTest.php index a97f2281125a6..34f43b725da57 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/TierPriceStorageTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/TierPriceStorageTest.php @@ -6,46 +6,44 @@ namespace Magento\Catalog\Test\Unit\Model\Product\Price; +use Magento\Catalog\Api\Data\TierPriceInterface; +use Magento\Catalog\Model\Indexer\Product\Price\Processor as PriceIndexerProcessor; +use Magento\Catalog\Model\Product\Price\TierPriceFactory; +use Magento\Catalog\Model\Product\Price\TierPricePersistence; +use Magento\Catalog\Model\Product\Price\Validation\Result as PriceValidationResult; +use Magento\Catalog\Model\Product\Price\Validation\TierPriceValidator; +use Magento\Catalog\Model\ProductIdLocatorInterface; + /** * TierPriceStorage test. */ class TierPriceStorageTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Catalog\Model\Product\Price\TierPricePersistence|\PHPUnit_Framework_MockObject_MockObject + * @var TierPricePersistence|\PHPUnit_Framework_MockObject_MockObject */ private $tierPricePersistence; /** - * @var \Magento\Catalog\Model\Product\Price\Validation\TierPriceValidator|\PHPUnit_Framework_MockObject_MockObject + * @var TierPriceValidator|\PHPUnit_Framework_MockObject_MockObject */ private $tierPriceValidator; /** - * @var \Magento\Catalog\Model\Product\Price\TierPriceFactory|\PHPUnit_Framework_MockObject_MockObject + * @var TierPriceFactory|\PHPUnit_Framework_MockObject_MockObject */ private $tierPriceFactory; /** - * @var \Magento\Catalog\Model\Indexer\Product\Price|\PHPUnit_Framework_MockObject_MockObject + * @var PriceIndexerProcessor|\PHPUnit_Framework_MockObject_MockObject */ - private $priceIndexer; + private $priceIndexProcessor; /** - * @var \Magento\Catalog\Model\ProductIdLocatorInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ProductIdLocatorInterface|\PHPUnit_Framework_MockObject_MockObject */ private $productIdLocator; - /** - * @var \Magento\PageCache\Model\Config|\PHPUnit_Framework_MockObject_MockObject - */ - private $config; - - /** - * @var \Magento\Framework\App\Cache\TypeListInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $typeList; - /** * @var \Magento\Catalog\Model\Product\Price\TierPriceStorage */ @@ -56,36 +54,13 @@ class TierPriceStorageTest extends \PHPUnit\Framework\TestCase */ protected function setUp() { - $this->tierPricePersistence = $this->getMockBuilder( - \Magento\Catalog\Model\Product\Price\TierPricePersistence::class - ) - ->disableOriginalConstructor() - ->getMock(); - $this->tierPricePersistence->expects($this->any()) - ->method('getEntityLinkField') - ->willReturn('row_id'); - $this->tierPriceValidator = $this->getMockBuilder( - \Magento\Catalog\Model\Product\Price\Validation\TierPriceValidator::class - ) - ->disableOriginalConstructor() - ->getMock(); - $this->tierPriceFactory = $this->getMockBuilder( - \Magento\Catalog\Model\Product\Price\TierPriceFactory::class - ) - ->disableOriginalConstructor() - ->getMock(); - $this->priceIndexer = $this->getMockBuilder(\Magento\Catalog\Model\Indexer\Product\Price::class) - ->disableOriginalConstructor() - ->getMock(); - $this->productIdLocator = $this->getMockBuilder(\Magento\Catalog\Model\ProductIdLocatorInterface::class) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - $this->config = $this->getMockBuilder(\Magento\PageCache\Model\Config::class) - ->disableOriginalConstructor() - ->getMock(); - $this->typeList = $this->getMockBuilder(\Magento\Framework\App\Cache\TypeListInterface::class) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); + $this->tierPricePersistence = $this->createMock(TierPricePersistence::class); + $this->tierPricePersistence->method('getEntityLinkField') + ->willReturn('entity_id'); + $this->tierPriceValidator = $this->createMock(TierPriceValidator::class); + $this->tierPriceFactory = $this->createMock(TierPriceFactory::class); + $this->priceIndexProcessor = $this->createMock(PriceIndexerProcessor::class); + $this->productIdLocator = $this->createMock(ProductIdLocatorInterface::class); $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->tierPriceStorage = $objectManager->getObject( @@ -94,10 +69,8 @@ protected function setUp() 'tierPricePersistence' => $this->tierPricePersistence, 'tierPriceValidator' => $this->tierPriceValidator, 'tierPriceFactory' => $this->tierPriceFactory, - 'priceIndexer' => $this->priceIndexer, + 'priceIndexProcessor' => $this->priceIndexProcessor, 'productIdLocator' => $this->productIdLocator, - 'config' => $this->config, - 'typeList' => $this->typeList, ] ); } @@ -125,7 +98,7 @@ public function testGet() [ [ 'value_id' => 1, - 'row_id' => 2, + 'entity_id' => 2, 'all_groups' => 1, 'customer_group_id' => 0, 'qty' => 2.0000, @@ -135,7 +108,7 @@ public function testGet() ], [ 'value_id' => 2, - 'row_id' => 3, + 'entity_id' => 3, 'all_groups' => 1, 'customer_group_id' => 0, 'qty' => 3.0000, @@ -145,7 +118,7 @@ public function testGet() ] ] ); - $price = $this->getMockBuilder(\Magento\Catalog\Api\Data\TierPriceInterface::class)->getMockForAbstractClass(); + $price = $this->getMockBuilder(TierPriceInterface::class)->getMockForAbstractClass(); $this->tierPriceFactory->expects($this->atLeastOnce())->method('create')->willReturn($price); $prices = $this->tierPriceStorage->get($skus); $this->assertNotEmpty($prices); @@ -183,36 +156,37 @@ public function testGetWithoutTierPrices() */ public function testUpdate() { - $price = $this->getMockBuilder(\Magento\Catalog\Api\Data\TierPriceInterface::class)->getMockForAbstractClass(); - $result = $this->getMockBuilder(\Magento\Catalog\Model\Product\Price\Validation\Result::class) - ->disableOriginalConstructor() - ->getMock(); - $result->expects($this->atLeastOnce())->method('getFailedRowIds')->willReturn([]); + $price = $this->createMock(TierPriceInterface::class); + $result = $this->createMock(PriceValidationResult::class); + $result->expects($this->once()) + ->method('getFailedRowIds') + ->willReturn([]); $this->productIdLocator->expects($this->atLeastOnce()) ->method('retrieveProductIdsBySkus') ->willReturn(['simple' => ['2' => 'simple'], 'virtual' => ['3' => 'virtual']]); - $this->tierPriceValidator - ->expects($this->atLeastOnce()) + $this->tierPriceValidator->expects($this->once()) ->method('retrieveValidationResult') ->willReturn($result); - $this->tierPriceFactory->expects($this->atLeastOnce())->method('createSkeleton')->willReturn( - [ - 'row_id' => 2, - 'all_groups' => 1, - 'customer_group_id' => 0, - 'qty' => 2, - 'value' => 3, - 'percentage_value' => null, - 'website_id' => 0 - ] - ); + $this->tierPriceFactory->expects($this->once()) + ->method('createSkeleton') + ->willReturn( + [ + 'entity_id' => 2, + 'all_groups' => 1, + 'customer_group_id' => 0, + 'qty' => 2, + 'value' => 3, + 'percentage_value' => null, + 'website_id' => 0 + ] + ); $this->tierPricePersistence->expects($this->once()) ->method('get') ->willReturn( [ [ 'value_id' => 1, - 'row_id' => 2, + 'entity_id' => 2, 'all_groups' => 1, 'customer_group_id' => 0, 'qty' => 2.0000, @@ -222,11 +196,15 @@ public function testUpdate() ] ] ); - $this->tierPricePersistence->expects($this->atLeastOnce())->method('update'); - $this->priceIndexer->expects($this->atLeastOnce())->method('execute'); - $this->config->expects($this->atLeastOnce())->method('isEnabled')->willReturn(true); - $this->typeList->expects($this->atLeastOnce())->method('invalidate'); - $price->expects($this->atLeastOnce())->method('getSku')->willReturn('simple'); + $this->tierPricePersistence->expects($this->once()) + ->method('update'); + $this->priceIndexProcessor->expects($this->once()) + ->method('reindexList') + ->with([2, 3]); + $price->expects($this->atLeastOnce()) + ->method('getSku') + ->willReturn('simple'); + $this->assertEmpty($this->tierPriceStorage->update([$price])); } @@ -237,35 +215,41 @@ public function testUpdate() */ public function testReplace() { - $price = $this->getMockBuilder(\Magento\Catalog\Api\Data\TierPriceInterface::class)->getMockForAbstractClass(); - $price->expects($this->atLeastOnce())->method('getSku')->willReturn('virtual'); - $result = $this->getMockBuilder(\Magento\Catalog\Model\Product\Price\Validation\Result::class) - ->disableOriginalConstructor() - ->getMock(); - $result->expects($this->atLeastOnce())->method('getFailedRowIds')->willReturn([]); + $price = $this->createMock(TierPriceInterface::class); + $price->expects($this->atLeastOnce()) + ->method('getSku') + ->willReturn('virtual'); + $result = $this->createMock(PriceValidationResult::class); + $result->expects($this->once()) + ->method('getFailedRowIds') + ->willReturn([]); $this->productIdLocator->expects($this->atLeastOnce()) ->method('retrieveProductIdsBySkus') ->willReturn(['simple' => ['2' => 'simple'], 'virtual' => ['3' => 'virtual']]); $this->tierPriceValidator - ->expects($this->atLeastOnce()) + ->expects($this->once()) ->method('retrieveValidationResult') ->willReturn($result); - $this->tierPriceFactory->expects($this->atLeastOnce())->method('createSkeleton')->willReturn( - [ - 'row_id' => 3, - 'all_groups' => 1, - 'customer_group_id' => 0, - 'qty' => 3, - 'value' => 7, - 'percentage_value' => null, - 'website_id' => 0 - ] - ); - $this->tierPricePersistence->expects($this->atLeastOnce())->method('replace'); - $this->priceIndexer->expects($this->atLeastOnce())->method('execute'); - $this->config->expects($this->atLeastOnce())->method('isEnabled')->willReturn(true); - $this->typeList->expects($this->atLeastOnce())->method('invalidate'); + $this->tierPriceFactory->expects($this->once()) + ->method('createSkeleton') + ->willReturn( + [ + 'entity_id' => 3, + 'all_groups' => 1, + 'customer_group_id' => 0, + 'qty' => 3, + 'value' => 7, + 'percentage_value' => null, + 'website_id' => 0 + ] + ); + $this->tierPricePersistence->expects($this->once()) + ->method('replace'); + $this->priceIndexProcessor->expects($this->once()) + ->method('reindexList') + ->with([2, 3]); + $this->assertEmpty($this->tierPriceStorage->replace([$price])); } @@ -276,13 +260,15 @@ public function testReplace() */ public function testDelete() { - $price = $this->getMockBuilder(\Magento\Catalog\Api\Data\TierPriceInterface::class)->getMockForAbstractClass(); - $price->expects($this->atLeastOnce())->method('getSku')->willReturn('simple'); - $result = $this->getMockBuilder(\Magento\Catalog\Model\Product\Price\Validation\Result::class) - ->disableOriginalConstructor() - ->getMock(); - $result->expects($this->atLeastOnce())->method('getFailedRowIds')->willReturn([]); - $this->tierPriceValidator->expects($this->atLeastOnce()) + $price = $this->createMock(TierPriceInterface::class); + $price->expects($this->atLeastOnce()) + ->method('getSku') + ->willReturn('simple'); + $result = $this->createMock(PriceValidationResult::class); + $result->expects($this->once()) + ->method('getFailedRowIds') + ->willReturn([]); + $this->tierPriceValidator->expects($this->once()) ->method('retrieveValidationResult') ->willReturn($result); $this->productIdLocator->expects($this->atLeastOnce()) @@ -294,7 +280,7 @@ public function testDelete() [ [ 'value_id' => 7, - 'row_id' => 7, + 'entity_id' => 7, 'all_groups' => 1, 'customer_group_id' => 0, 'qty' => 5.0000, @@ -304,21 +290,24 @@ public function testDelete() ] ] ); - $this->tierPriceFactory->expects($this->atLeastOnce())->method('createSkeleton')->willReturn( - [ - 'row_id' => 3, - 'all_groups' => 1, - 'customer_group_id' => 0, - 'qty' => 3, - 'value' => 7, - 'percentage_value' => null, - 'website_id' => 0 - ] - ); - $this->tierPricePersistence->expects($this->atLeastOnce())->method('delete'); - $this->priceIndexer->expects($this->atLeastOnce())->method('execute'); - $this->config->expects($this->atLeastOnce())->method('isEnabled')->willReturn(true); - $this->typeList->expects($this->atLeastOnce())->method('invalidate'); + $this->tierPriceFactory->expects($this->once()) + ->method('createSkeleton')->willReturn( + [ + 'entity_id' => 3, + 'all_groups' => 1, + 'customer_group_id' => 0, + 'qty' => 3, + 'value' => 7, + 'percentage_value' => null, + 'website_id' => 0 + ] + ); + $this->tierPricePersistence->expects($this->once()) + ->method('delete'); + $this->priceIndexProcessor->expects($this->once()) + ->method('reindexList') + ->with([2]); + $this->assertEmpty($this->tierPriceStorage->delete([$price])); } } diff --git a/app/code/Magento/Indexer/Model/Indexer/CacheCleaner.php b/app/code/Magento/Indexer/Model/Indexer/CacheCleaner.php new file mode 100644 index 0000000000000..c75a3541ba9c3 --- /dev/null +++ b/app/code/Magento/Indexer/Model/Indexer/CacheCleaner.php @@ -0,0 +1,100 @@ +eventManager = $eventManager; + $this->cacheContext = $cacheContext; + $this->appCache = $appCache; + } + + /** + * Clean cache after full reindex. + * + * @param ActionInterface $subject + * @return void + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterExecuteFull(ActionInterface $subject) + { + $this->cleanCache(); + } + + /** + * Clean cache after reindexed list. + * + * @param ActionInterface $subject + * @return void + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterExecuteList(ActionInterface $subject) + { + $this->cleanCache(); + } + + /** + * Clean cache after reindexed row. + * + * @param ActionInterface $subject + * @return void + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterExecuteRow(ActionInterface $subject) + { + $this->cleanCache(); + } + + /** + * Clean cache. + * + * @return void + */ + private function cleanCache() + { + $this->eventManager->dispatch('clean_cache_by_tags', ['object' => $this->cacheContext]); + + $identities = $this->cacheContext->getIdentities(); + if (!empty($identities)) { + $this->appCache->clean($identities); + } + } +} diff --git a/app/code/Magento/Indexer/etc/di.xml b/app/code/Magento/Indexer/etc/di.xml index 76e7e7a46224b..9496f29cb1d87 100644 --- a/app/code/Magento/Indexer/etc/di.xml +++ b/app/code/Magento/Indexer/etc/di.xml @@ -67,4 +67,7 @@ Magento\Indexer\Model\Indexer + + + From a4f09b45dbaa3e42420922ef6befffeff9bdccb1 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Thu, 1 Aug 2019 13:56:01 -0500 Subject: [PATCH 02/17] MC-18833: Tier price API doesn't take into account indexers mode --- .../Magento/Catalog/Model/Product/Price/TierPriceStorage.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php b/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php index 6efff27fa2287..0c371a48fde96 100644 --- a/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php +++ b/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php @@ -218,10 +218,10 @@ private function retrieveAffectedIds(array $skus): array $affectedIds = []; foreach ($this->productIdLocator->retrieveProductIdsBySkus($skus) as $productId) { - $affectedIds = array_merge($affectedIds, array_keys($productId)); + $affectedIds[] = array_keys($productId); } - return array_unique($affectedIds); + return array_unique(array_merge(...$affectedIds)); } /** From 814c68a48d433ff8f73c63352618b8dcb0e38c33 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Fri, 2 Aug 2019 02:49:10 -0500 Subject: [PATCH 03/17] MC-18833: Tier price API doesn't take into account indexers mode --- .../Magento/Catalog/Model/Product/Price/TierPriceStorage.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php b/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php index 0c371a48fde96..b9e1a27e46e41 100644 --- a/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php +++ b/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php @@ -221,7 +221,7 @@ private function retrieveAffectedIds(array $skus): array $affectedIds[] = array_keys($productId); } - return array_unique(array_merge(...$affectedIds)); + return $affectedIds ? array_unique(array_merge(...$affectedIds)) : []; } /** From 98272e90d737e2bb9dd54ca3e5f81737bf848bd6 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Fri, 2 Aug 2019 15:03:51 -0500 Subject: [PATCH 04/17] MC-18833: Tier price API doesn't take into account indexers mode --- .../Model/Product/Price/TierPriceStorage.php | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php b/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php index b9e1a27e46e41..36ef1826462b0 100644 --- a/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php +++ b/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php @@ -79,10 +79,8 @@ public function __construct( public function get(array $skus) { $skus = $this->tierPriceValidator->validateSkus($skus); - $skuByIdLookup = $this->buildSkuByIdLookup($skus); - $prices = $this->getExistingPrices($skuByIdLookup); - return $prices; + return $this->getExistingPrices($skus); } /** @@ -90,6 +88,7 @@ public function get(array $skus) */ public function update(array $prices) { + $affectedIds = $this->retrieveAffectedProductIdsForPrices($prices); $skus = array_unique( array_map( function (TierPriceInterface $price) { @@ -98,13 +97,11 @@ function (TierPriceInterface $price) { $prices ) ); - $skuByIdLookup = $this->buildSkuByIdLookup($skus); - $existingPrices = $this->getExistingPrices($skuByIdLookup, true); - $result = $this->tierPriceValidator->retrieveValidationResult($prices, $existingPrices); + $result = $this->tierPriceValidator->retrieveValidationResult($prices, $this->getExistingPrices($skus, true)); $prices = $this->removeIncorrectPrices($prices, $result->getFailedRowIds()); $formattedPrices = $this->retrieveFormattedPrices($prices); $this->tierPricePersistence->update($formattedPrices); - $this->reindexPrices(array_keys($skuByIdLookup)); + $this->reindexPrices($affectedIds); return $result->getFailedItems(); } @@ -142,16 +139,18 @@ public function delete(array $prices) /** * Get existing prices by SKUs. * - * @param array $skuByIdLookup + * @param array $skus * @param bool $groupBySku [optional] * @return array */ - private function getExistingPrices(array $skuByIdLookup, bool $groupBySku = false): array + private function getExistingPrices(array $skus, bool $groupBySku = false): array { - $rawPrices = $this->tierPricePersistence->get(array_keys($skuByIdLookup)); + $ids = $this->retrieveAffectedIds($skus); + $rawPrices = $this->tierPricePersistence->get($ids); $prices = []; if ($rawPrices) { $linkField = $this->tierPricePersistence->getEntityLinkField(); + $skuByIdLookup = $this->buildSkuByIdLookup($skus); foreach ($rawPrices as $rawPrice) { $sku = $skuByIdLookup[$rawPrice[$linkField]]; $price = $this->tierPriceFactory->create($rawPrice, $sku); From 417dd7756fd27f2b63020426bf5fe9a69eb173e0 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Mon, 5 Aug 2019 13:01:30 -0500 Subject: [PATCH 05/17] MC-18833: Tier price API doesn't take into account indexers mode --- app/code/Magento/Indexer/Model/Indexer/CacheCleaner.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/code/Magento/Indexer/Model/Indexer/CacheCleaner.php b/app/code/Magento/Indexer/Model/Indexer/CacheCleaner.php index c75a3541ba9c3..25b4ae286e332 100644 --- a/app/code/Magento/Indexer/Model/Indexer/CacheCleaner.php +++ b/app/code/Magento/Indexer/Model/Indexer/CacheCleaner.php @@ -91,10 +91,5 @@ public function afterExecuteRow(ActionInterface $subject) private function cleanCache() { $this->eventManager->dispatch('clean_cache_by_tags', ['object' => $this->cacheContext]); - - $identities = $this->cacheContext->getIdentities(); - if (!empty($identities)) { - $this->appCache->clean($identities); - } } } From 3121dfe97a5d4a18ae0d0ef27a9d32fd62e8da1f Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Mon, 5 Aug 2019 13:02:11 -0500 Subject: [PATCH 06/17] MC-18833: Tier price API doesn't take into account indexers mode --- app/code/Magento/Indexer/Model/Indexer/CacheCleaner.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Indexer/Model/Indexer/CacheCleaner.php b/app/code/Magento/Indexer/Model/Indexer/CacheCleaner.php index 25b4ae286e332..4a5ea36a68a61 100644 --- a/app/code/Magento/Indexer/Model/Indexer/CacheCleaner.php +++ b/app/code/Magento/Indexer/Model/Indexer/CacheCleaner.php @@ -90,6 +90,9 @@ public function afterExecuteRow(ActionInterface $subject) */ private function cleanCache() { - $this->eventManager->dispatch('clean_cache_by_tags', ['object' => $this->cacheContext]); + $identities = $this->cacheContext->getIdentities(); + if (!empty($identities)) { + $this->appCache->clean($identities); + } } } From 7d3d37cb3b011f4eddb123ed50eef08520fba167 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Fri, 9 Aug 2019 14:22:59 -0500 Subject: [PATCH 07/17] MC-18833: Tier price API doesn't take into account indexers mode --- app/code/Magento/Indexer/Model/Indexer/CacheCleaner.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Indexer/Model/Indexer/CacheCleaner.php b/app/code/Magento/Indexer/Model/Indexer/CacheCleaner.php index 4a5ea36a68a61..c75a3541ba9c3 100644 --- a/app/code/Magento/Indexer/Model/Indexer/CacheCleaner.php +++ b/app/code/Magento/Indexer/Model/Indexer/CacheCleaner.php @@ -90,6 +90,8 @@ public function afterExecuteRow(ActionInterface $subject) */ private function cleanCache() { + $this->eventManager->dispatch('clean_cache_by_tags', ['object' => $this->cacheContext]); + $identities = $this->cacheContext->getIdentities(); if (!empty($identities)) { $this->appCache->clean($identities); From 0b47e429e64e36c048bcfd2df2b4c70fd59c4803 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Mon, 12 Aug 2019 12:56:47 -0500 Subject: [PATCH 08/17] MC-19090: Category Image from Gallery is not saved --- .../Catalog/Model/Category/Attribute/Backend/Image.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Image.php b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Image.php index 6a035a4681a54..073b1fa44a07e 100644 --- a/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Image.php +++ b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Image.php @@ -125,7 +125,9 @@ public function beforeSave($object) } if ($imageName = $this->getUploadedImageName($value)) { - $imageName = $this->checkUniqueImageName($imageName); + if (!$this->fileResidesOutsideCategoryDir($object->getData($attributeName))) { + $imageName = $this->checkUniqueImageName($imageName); + } $object->setData($this->additionalData . $attributeName, $value); $object->setData($attributeName, $imageName); } elseif (!is_string($value)) { From 5b2f43e3d95a2c6d4371ed4fd46c51a86907e8e4 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Mon, 12 Aug 2019 15:34:39 -0500 Subject: [PATCH 09/17] MC-19090: Category Image from Gallery is not saved --- .../Category/Attribute/Backend/ImageTest.php | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Category/Attribute/Backend/ImageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Category/Attribute/Backend/ImageTest.php index dc74cdfc642e3..a76ae5244076f 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Category/Attribute/Backend/ImageTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Category/Attribute/Backend/ImageTest.php @@ -179,27 +179,19 @@ public function testBeforeSaveAttributeFileNameOutsideOfCategoryDir() { $model = $this->setUpModelForAfterSave(); $model->setAttribute($this->attribute); - - $mediaDirectoryMock = $this->createMock(WriteInterface::class); - $this->filesystem->expects($this->once()) - ->method('getDirectoryWrite') - ->with(DirectoryList::MEDIA) - ->willReturn($mediaDirectoryMock); + $imagePath = '/pub/media/wysiwyg/test123.jpg'; $this->filesystem - ->expects($this->once()) + ->expects($this->exactly(2)) ->method('getUri') ->with(DirectoryList::MEDIA) ->willReturn('pub/media'); - $mediaDirectoryMock->expects($this->once()) - ->method('getAbsolutePath') - ->willReturn('/pub/media/wysiwyg/test123.jpg'); $object = new \Magento\Framework\DataObject( [ 'test_attribute' => [ [ 'name' => 'test123.jpg', - 'url' => '/pub/media/wysiwyg/test123.jpg', + 'url' => $imagePath, ], ], ] @@ -207,9 +199,9 @@ public function testBeforeSaveAttributeFileNameOutsideOfCategoryDir() $model->beforeSave($object); - $this->assertEquals('test123.jpg', $object->getTestAttribute()); + $this->assertEquals($imagePath, $object->getTestAttribute()); $this->assertEquals( - [['name' => '/pub/media/wysiwyg/test123.jpg', 'url' => '/pub/media/wysiwyg/test123.jpg']], + [['name' => $imagePath, 'url' => $imagePath]], $object->getData('_additional_data_test_attribute') ); } From b19440d0539e2dbe95ba92b8ffd743f2893997cb Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Thu, 15 Aug 2019 10:20:19 -0500 Subject: [PATCH 10/17] MC-19090: Category Image from Gallery is not saved --- .../Catalog/Model/Category/Attribute/Backend/Image.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Image.php b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Image.php index 073b1fa44a07e..07fd1a50edc97 100644 --- a/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Image.php +++ b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Image.php @@ -125,7 +125,7 @@ public function beforeSave($object) } if ($imageName = $this->getUploadedImageName($value)) { - if (!$this->fileResidesOutsideCategoryDir($object->getData($attributeName))) { + if (!$this->fileResidesOutsideCategoryDir($value)) { $imageName = $this->checkUniqueImageName($imageName); } $object->setData($this->additionalData . $attributeName, $value); @@ -178,13 +178,15 @@ private function fileResidesOutsideCategoryDir($value) } $fileUrl = ltrim($value[0]['url'], '/'); - $baseMediaDir = $this->_filesystem->getUri(DirectoryList::MEDIA); + $imageUploader = $this->getImageUploader(); + $baseMediaDir = $this->_filesystem->getUri(DirectoryList::MEDIA) + . DIRECTORY_SEPARATOR . $imageUploader->getBasePath(); if (!$baseMediaDir) { return false; } - return strpos($fileUrl, $baseMediaDir) === 0; + return strpos($fileUrl, $baseMediaDir) === false; } /** From c7293b05ec6224d2525c9c9d55e8d2138a53cbcf Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Thu, 15 Aug 2019 17:48:36 -0500 Subject: [PATCH 11/17] MC-19090: Category Image from Gallery is not saved --- .../Catalog/Model/Category/Attribute/Backend/Image.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Image.php b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Image.php index 07fd1a50edc97..32b5d066193ca 100644 --- a/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Image.php +++ b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Image.php @@ -178,15 +178,13 @@ private function fileResidesOutsideCategoryDir($value) } $fileUrl = ltrim($value[0]['url'], '/'); - $imageUploader = $this->getImageUploader(); - $baseMediaDir = $this->_filesystem->getUri(DirectoryList::MEDIA) - . DIRECTORY_SEPARATOR . $imageUploader->getBasePath(); + $baseMediaDir = $this->_filesystem->getUri(DirectoryList::MEDIA); if (!$baseMediaDir) { return false; } - return strpos($fileUrl, $baseMediaDir) === false; + return strpos($fileUrl, $baseMediaDir) !== false; } /** From 60c78c1489ed1fb92000af44ed19a02e20f28203 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Thu, 15 Aug 2019 17:50:07 -0500 Subject: [PATCH 12/17] MC-19090: Category Image from Gallery is not saved --- .../Catalog/Model/Category/FileInfo.php | 41 +++++++++++++++++-- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Category/FileInfo.php b/app/code/Magento/Catalog/Model/Category/FileInfo.php index d77f472c6be90..0973db756df87 100644 --- a/app/code/Magento/Catalog/Model/Category/FileInfo.php +++ b/app/code/Magento/Catalog/Model/Category/FileInfo.php @@ -10,6 +10,8 @@ use Magento\Framework\Filesystem; use Magento\Framework\Filesystem\Directory\WriteInterface; use Magento\Framework\Filesystem\Directory\ReadInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Store\Model\StoreManagerInterface; /** * Class FileInfo @@ -48,16 +50,26 @@ class FileInfo */ private $pubDirectory; + /** + * Store manager + * + * @var \Magento\Store\Model\StoreManagerInterface + */ + private $storeManager; + /** * @param Filesystem $filesystem * @param Mime $mime + * @param StoreManagerInterface $storeManager */ public function __construct( Filesystem $filesystem, - Mime $mime + Mime $mime, + StoreManagerInterface $storeManager ) { $this->filesystem = $filesystem; $this->mime = $mime; + $this->storeManager = $storeManager; } /** @@ -152,7 +164,8 @@ public function isExist($fileName) */ private function getFilePath($fileName) { - $filePath = ltrim($fileName, '/'); + $filePath = $this->removeStorePath($fileName); + $filePath = ltrim($filePath, '/'); $mediaDirectoryRelativeSubpath = $this->getMediaDirectoryPathRelativeToBaseDirectoryPath($filePath); $isFileNameBeginsWithMediaDirectoryPath = $this->isBeginsWithMediaDirectoryPath($fileName); @@ -177,7 +190,8 @@ private function getFilePath($fileName) */ public function isBeginsWithMediaDirectoryPath($fileName) { - $filePath = ltrim($fileName, '/'); + $filePath = $this->removeStorePath($fileName); + $filePath = ltrim($filePath, '/'); $mediaDirectoryRelativeSubpath = $this->getMediaDirectoryPathRelativeToBaseDirectoryPath($filePath); $isFileNameBeginsWithMediaDirectoryPath = strpos($filePath, (string) $mediaDirectoryRelativeSubpath) === 0; @@ -185,6 +199,27 @@ public function isBeginsWithMediaDirectoryPath($fileName) return $isFileNameBeginsWithMediaDirectoryPath; } + /** + * Clean store path in case if it's exists + * + * @param string $path + * @return string + */ + private function removeStorePath(string $path): string + { + $result = $path; + try { + $storeUrl = $this->storeManager->getStore()->getUrl(); + } catch (NoSuchEntityException $e) { + return $result; + } + $path = parse_url($path, PHP_URL_PATH); + $storePath = parse_url($storeUrl, PHP_URL_PATH); + $result = ltrim($path, $storePath); + + return $result; + } + /** * Get media directory subpath relative to base directory path * From 43e454de018e7c0b54773f0d88cf8412fd4a5356 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Fri, 16 Aug 2019 10:28:13 -0500 Subject: [PATCH 13/17] MC-19090: Category Image from Gallery is not saved --- .../Category/Attribute/Backend/Image.php | 1 + .../Test/Unit/Model/Category/FileInfoTest.php | 25 ++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Image.php b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Image.php index 32b5d066193ca..4af2df37dea0d 100644 --- a/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Image.php +++ b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Image.php @@ -121,6 +121,7 @@ public function beforeSave($object) if ($this->fileResidesOutsideCategoryDir($value)) { // use relative path for image attribute so we know it's outside of category dir when we fetch it + $value[0]['url'] = parse_url($value[0]['url'], PHP_URL_PATH); $value[0]['name'] = $value[0]['url']; } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Category/FileInfoTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Category/FileInfoTest.php index 6c6a69ec39c85..71f5ca33d1303 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Category/FileInfoTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Category/FileInfoTest.php @@ -13,6 +13,8 @@ use Magento\Framework\Filesystem\Directory\WriteInterface; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; +use Magento\Store\Model\StoreManagerInterface; +use Magento\Store\Model\Store; /** * Test for Magento\Catalog\Model\Category\FileInfo class. @@ -44,6 +46,16 @@ class FileInfoTest extends TestCase */ private $pubDirectory; + /** + * @var StoreManagerInterface|MockObject + */ + private $storeManager; + + /** + * @var Store|MockObject + */ + private $store; + /** * @var FileInfo */ @@ -60,6 +72,16 @@ protected function setUp() $this->pubDirectory = $pubDirectory = $this->getMockBuilder(ReadInterface::class) ->getMockForAbstractClass(); + $this->store = $this->getMockBuilder(Store::class) + ->disableOriginalConstructor() + ->getMock(); + $this->storeManager = $this->getMockBuilder(StoreManagerInterface::class) + ->setMethods(['getStore']) + ->getMockForAbstractClass(); + $this->storeManager->expects($this->any()) + ->method('getStore') + ->willReturn($this->store); + $this->filesystem = $this->getMockBuilder(Filesystem::class) ->disableOriginalConstructor() ->getMock(); @@ -94,7 +116,8 @@ function ($arg) use ($baseDirectory, $pubDirectory) { $this->model = new FileInfo( $this->filesystem, - $this->mime + $this->mime, + $this->storeManager ); } From 8072515c720734c42071c1b78b44c007a0731238 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Fri, 16 Aug 2019 10:51:39 -0500 Subject: [PATCH 14/17] MC-19090: Category Image from Gallery is not saved --- .../Magento/Catalog/Model/Category/Attribute/Backend/Image.php | 1 + app/code/Magento/Catalog/Model/Category/FileInfo.php | 2 ++ 2 files changed, 3 insertions(+) diff --git a/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Image.php b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Image.php index 4af2df37dea0d..4880214e5c6a6 100644 --- a/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Image.php +++ b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Image.php @@ -121,6 +121,7 @@ public function beforeSave($object) if ($this->fileResidesOutsideCategoryDir($value)) { // use relative path for image attribute so we know it's outside of category dir when we fetch it + // phpcs:ignore Magento2.Functions.DiscouragedFunction $value[0]['url'] = parse_url($value[0]['url'], PHP_URL_PATH); $value[0]['name'] = $value[0]['url']; } diff --git a/app/code/Magento/Catalog/Model/Category/FileInfo.php b/app/code/Magento/Catalog/Model/Category/FileInfo.php index 0973db756df87..66a251f7ed448 100644 --- a/app/code/Magento/Catalog/Model/Category/FileInfo.php +++ b/app/code/Magento/Catalog/Model/Category/FileInfo.php @@ -213,7 +213,9 @@ private function removeStorePath(string $path): string } catch (NoSuchEntityException $e) { return $result; } + // phpcs:ignore Magento2.Functions.DiscouragedFunction $path = parse_url($path, PHP_URL_PATH); + // phpcs:ignore Magento2.Functions.DiscouragedFunction $storePath = parse_url($storeUrl, PHP_URL_PATH); $result = ltrim($path, $storePath); From 053123c290fac941009bfcfa0aade416439177a5 Mon Sep 17 00:00:00 2001 From: Roman Lytvynenko Date: Fri, 16 Aug 2019 16:59:50 -0500 Subject: [PATCH 15/17] MC-19238: Subscribe to Order Status RSS gives error page --- .../Magento/Sales/Model/Rss/OrderStatus.php | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Sales/Model/Rss/OrderStatus.php b/app/code/Magento/Sales/Model/Rss/OrderStatus.php index c3c4456c6b7ca..e03cbbee1b50c 100644 --- a/app/code/Magento/Sales/Model/Rss/OrderStatus.php +++ b/app/code/Magento/Sales/Model/Rss/OrderStatus.php @@ -217,11 +217,12 @@ protected function getEntries() if ($type && $type != 'order') { $urlAppend = $type; } - $type = __(ucwords($type)); - $title = __('Details for %1 #%2', $type, $result['increment_id']); - $description = '

' . __('Notified Date: %1', $this->localeDate->formatDate($result['created_at'])) + $type = __(ucwords($type))->render(); + $title = __('Details for %1 #%2', $type, $result['increment_id'])->render(); + $description = '

' + . __('Notified Date: %1', $this->localeDate->formatDate($result['created_at']))->render() . '
' - . __('Comment: %1
', $result['comment']) . '

'; + . __('Comment: %1
', $result['comment'])->render() . '

'; $url = $this->urlBuilder->getUrl( 'sales/order/' . $urlAppend, ['order_id' => $this->order->getId()] @@ -233,10 +234,10 @@ protected function getEntries() 'Order #%1 created at %2', $this->order->getIncrementId(), $this->localeDate->formatDate($this->order->getCreatedAt()) - ); + )->render(); $url = $this->urlBuilder->getUrl('sales/order/view', ['order_id' => $this->order->getId()]); - $description = '

' . __('Current Status: %1
', $this->order->getStatusLabel()) . - __('Total: %1
', $this->order->formatPrice($this->order->getGrandTotal())) . '

'; + $description = '

' . __('Current Status: %1
', $this->order->getStatusLabel())->render() . + __('Total: %1
', $this->order->formatPrice($this->order->getGrandTotal()))->render() . '

'; $entries[] = ['title' => $title, 'link' => $url, 'description' => $description]; @@ -250,7 +251,7 @@ protected function getEntries() */ protected function getHeader() { - $title = __('Order # %1 Notification(s)', $this->order->getIncrementId()); + $title = __('Order # %1 Notification(s)', $this->order->getIncrementId())->render(); $newUrl = $this->urlBuilder->getUrl('sales/order/view', ['order_id' => $this->order->getId()]); return ['title' => $title, 'description' => $title, 'link' => $newUrl, 'charset' => 'UTF-8']; From eec9c26a44f3a62ef426f9ac326ff36269a77d7b Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Mon, 19 Aug 2019 08:21:01 +0000 Subject: [PATCH 16/17] MC-19407: Number of the rows increase very fast in changelog table --- lib/internal/Magento/Framework/Mview/View/Subscription.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Mview/View/Subscription.php b/lib/internal/Magento/Framework/Mview/View/Subscription.php index 67dff1a2cc5db..ddfa39f0a089f 100644 --- a/lib/internal/Magento/Framework/Mview/View/Subscription.php +++ b/lib/internal/Magento/Framework/Mview/View/Subscription.php @@ -214,7 +214,7 @@ protected function buildStatement($event, $changelog) $columns = []; foreach ($columnNames as $columnName) { $columns[] = sprintf( - 'NEW.%1$s <=> OLD.%1$s', + 'NOT(NEW.%1$s <=> OLD.%1$s)', $this->connection->quoteIdentifier($columnName) ); } From 629ccde3e6cea99b79e53b9acd8459d758ce27be Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Wed, 21 Aug 2019 11:32:38 -0500 Subject: [PATCH 17/17] MC-19090: Category Image from Gallery is not saved --- app/code/Magento/Catalog/Model/Category/FileInfo.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Category/FileInfo.php b/app/code/Magento/Catalog/Model/Category/FileInfo.php index 66a251f7ed448..76b6a2e75d0ea 100644 --- a/app/code/Magento/Catalog/Model/Category/FileInfo.php +++ b/app/code/Magento/Catalog/Model/Category/FileInfo.php @@ -209,7 +209,7 @@ private function removeStorePath(string $path): string { $result = $path; try { - $storeUrl = $this->storeManager->getStore()->getUrl(); + $storeUrl = $this->storeManager->getStore()->getBaseUrl(); } catch (NoSuchEntityException $e) { return $result; } @@ -217,8 +217,9 @@ private function removeStorePath(string $path): string $path = parse_url($path, PHP_URL_PATH); // phpcs:ignore Magento2.Functions.DiscouragedFunction $storePath = parse_url($storeUrl, PHP_URL_PATH); - $result = ltrim($path, $storePath); + $storePath = rtrim($storePath, '/'); + $result = preg_replace('/^' . preg_quote($storePath, '/') . '/', '', $path); return $result; }