diff --git a/app/code/Magento/Bundle/etc/di.xml b/app/code/Magento/Bundle/etc/di.xml index 9842b28efaa1c..287a6c8bfdbc0 100644 --- a/app/code/Magento/Bundle/etc/di.xml +++ b/app/code/Magento/Bundle/etc/di.xml @@ -160,6 +160,9 @@ Magento\Catalog\Model\Indexer\Price\CompositeProductBatchSizeManagement + + Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\CompositeProductBatchSizeAdjuster + diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Price/Action/Full.php b/app/code/Magento/Catalog/Model/Indexer/Product/Price/Action/Full.php index 88dba03def001..eb15833a7d0b2 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Price/Action/Full.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Price/Action/Full.php @@ -106,6 +106,9 @@ public function execute($ids = null) $this->_defaultIndexerResource->getMainTable() ); + // Prepare replica table for indexation. + $this->_defaultIndexerResource->getConnection()->truncateTable($replicaTable); + /** @var \Magento\Catalog\Model\ResourceModel\Product\Indexer\AbstractIndexer $indexer */ foreach ($this->getTypeIndexers() as $indexer) { $indexer->getTableStrategy()->setUseIdxTable(false); diff --git a/app/code/Magento/Catalog/Model/ProductRepository.php b/app/code/Magento/Catalog/Model/ProductRepository.php index aac9ec4807cbf..4a72539e982f2 100644 --- a/app/code/Magento/Catalog/Model/ProductRepository.php +++ b/app/code/Magento/Catalog/Model/ProductRepository.php @@ -641,7 +641,7 @@ public function save(\Magento\Catalog\Api\Data\ProductInterface $product, $saveO } unset($this->instances[$product->getSku()]); unset($this->instancesById[$product->getId()]); - return $this->get($product->getSku()); + return $this->get($product->getSku(), false, $product->getStoreId()); } /** diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/BatchSizeCalculator.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/BatchSizeCalculator.php index b31147af2342f..47c23b101a3b2 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/BatchSizeCalculator.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/BatchSizeCalculator.php @@ -21,15 +21,24 @@ class BatchSizeCalculator */ private $estimators; + /** + * @var \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\CompositeProductBatchSizeAdjusterInterface[] + * @since 2.2.0 + */ + private $batchSizeAdjusters; + /** * BatchSizeCalculator constructor. * @param array $batchRowsCount * @param array $estimators + * @param array $batchSizeAdjusters + * @since 2.2.0 */ - public function __construct(array $batchRowsCount, array $estimators) + public function __construct(array $batchRowsCount, array $estimators, array $batchSizeAdjusters) { $this->batchRowsCount = $batchRowsCount; $this->estimators = $estimators; + $this->batchSizeAdjusters = $batchSizeAdjusters; } /** @@ -52,6 +61,10 @@ public function estimateBatchSize(\Magento\Framework\DB\Adapter\AdapterInterface ? $this->estimators[$indexerTypeId] : $this->estimators['default']; + $batchRowsCount = isset($this->batchSizeAdjusters[$indexerTypeId]) + ? $this->batchSizeAdjusters[$indexerTypeId]->adjust($batchRowsCount) + : $batchRowsCount; + $calculator->ensureBatchSize($connection, $batchRowsCount); return $batchRowsCount; diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/CompositeProductBatchSizeAdjuster.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/CompositeProductBatchSizeAdjuster.php new file mode 100644 index 0000000000000..0054167c5386f --- /dev/null +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/CompositeProductBatchSizeAdjuster.php @@ -0,0 +1,35 @@ +compositeProductRelationsCalculator = $compositeProductRelationsCalculator; + } + + /** + * {@inheritdoc} + */ + public function adjust($batchSize) + { + $maxRelationsCount = $this->compositeProductRelationsCalculator->getMaxRelationsCount(); + return $maxRelationsCount > 0 ? ceil($batchSize / $maxRelationsCount) : $batchSize; + } +} diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/CompositeProductBatchSizeAdjusterInterface.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/CompositeProductBatchSizeAdjusterInterface.php new file mode 100644 index 0000000000000..5472527a85919 --- /dev/null +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/CompositeProductBatchSizeAdjusterInterface.php @@ -0,0 +1,22 @@ +indexerResource = $indexerResource; + } + + /** + * Returns maximum number of composite related products. + * + * @return int + */ + public function getMaxRelationsCount() + { + $connection = $this->indexerResource->getConnection(); + $relationSelect = $connection->select(); + $relationSelect->from( + ['relation' => $this->indexerResource->getTable('catalog_product_relation')], + ['count' => new \Zend_Db_Expr('count(relation.child_id)')] + ); + $relationSelect->group('parent_id'); + + $maxSelect = $connection->select(); + $maxSelect->from( + ['max_value' => $relationSelect], + ['count' => new \Zend_Db_Expr('MAX(count)')] + ); + return $connection->fetchOne($maxSelect); + } +} diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/CompositeProductRowSizeEstimator.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/CompositeProductRowSizeEstimator.php index a3425bfc9d729..94a94c720b026 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/CompositeProductRowSizeEstimator.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/CompositeProductRowSizeEstimator.php @@ -20,11 +20,6 @@ class CompositeProductRowSizeEstimator implements IndexTableRowSizeEstimatorInte */ const MEMORY_SIZE_FOR_ONE_ROW = 200; - /** - * @var DefaultPrice - */ - private $indexerResource; - /** * @var WebsiteManagementInterface */ @@ -36,18 +31,25 @@ class CompositeProductRowSizeEstimator implements IndexTableRowSizeEstimatorInte private $collectionFactory; /** - * @param DefaultPrice $indexerResource + * @var CompositeProductRelationsCalculator + * @since 2.2.0 + */ + private $compositeProductRelationsCalculator; + + /** * @param WebsiteManagementInterface $websiteManagement * @param CollectionFactory $collectionFactory + * @param CompositeProductRelationsCalculator $compositeProductRelationsCalculator + * @since 2.2.0 */ public function __construct( - DefaultPrice $indexerResource, WebsiteManagementInterface $websiteManagement, - CollectionFactory $collectionFactory + CollectionFactory $collectionFactory, + CompositeProductRelationsCalculator $compositeProductRelationsCalculator ) { - $this->indexerResource = $indexerResource; $this->websiteManagement = $websiteManagement; $this->collectionFactory = $collectionFactory; + $this->compositeProductRelationsCalculator = $compositeProductRelationsCalculator; } /** @@ -59,21 +61,7 @@ public function estimateRowSize() { $websitesCount = $this->websiteManagement->getCount(); $customerGroupCount = $this->collectionFactory->create()->getSize(); - - $connection = $this->indexerResource->getConnection(); - $relationSelect = $connection->select(); - $relationSelect->from( - ['relation' => $this->indexerResource->getTable('catalog_product_relation')], - ['count' => new \Zend_Db_Expr('count(relation.child_id)')] - ); - $relationSelect->group('parent_id'); - - $maxSelect = $connection->select(); - $maxSelect->from( - ['max_value' => $relationSelect], - ['count' => new \Zend_Db_Expr('MAX(count)')] - ); - $maxRelatedProductCount = $connection->fetchOne($maxSelect); + $maxRelatedProductCount = $this->compositeProductRelationsCalculator->getMaxRelationsCount(); /** * Calculate memory size for largest composite product in database. diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php index 44d38adc5c15c..b537e2c406cab 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php @@ -193,7 +193,8 @@ protected function setUp() 'validate', 'save', 'getMediaGalleryEntries', - 'setData' + 'setData', + 'getStoreId' ]); $this->initializedProductMock->expects($this->any()) ->method('hasGalleryAttribute') diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Indexer/Price/BatchSizeCalculatorTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Indexer/Price/BatchSizeCalculatorTest.php index 74bce577f53a1..9508782cb4a99 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Indexer/Price/BatchSizeCalculatorTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Indexer/Price/BatchSizeCalculatorTest.php @@ -29,7 +29,8 @@ protected function setUp() $this->batchRowsCount = 200; $this->model = new \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\BatchSizeCalculator( ['default' => $this->batchRowsCount], - ['default' => $this->estimatorMock] + ['default' => $this->estimatorMock], + [] ); } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Indexer/Price/CompositeProductBatchSizeAdjusterTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Indexer/Price/CompositeProductBatchSizeAdjusterTest.php new file mode 100644 index 0000000000000..69def72af1669 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Indexer/Price/CompositeProductBatchSizeAdjusterTest.php @@ -0,0 +1,39 @@ +relationsCalculatorMock = $this->getMockBuilder(CompositeProductRelationsCalculator::class) + ->disableOriginalConstructor() + ->getMock(); + $this->model = new CompositeProductBatchSizeAdjuster($this->relationsCalculatorMock); + } + + public function testAdjust() + { + $this->relationsCalculatorMock->expects($this->once()) + ->method('getMaxRelationsCount') + ->willReturn(200); + $this->assertEquals(25, $this->model->adjust(5000)); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Indexer/Price/CompositeProductRelationsCalculatorTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Indexer/Price/CompositeProductRelationsCalculatorTest.php new file mode 100644 index 0000000000000..3eda8616275a1 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Indexer/Price/CompositeProductRelationsCalculatorTest.php @@ -0,0 +1,71 @@ +defaultPriceMock = $this->getMockBuilder(DefaultPrice::class)->disableOriginalConstructor()->getMock(); + $this->model = new CompositeProductRelationsCalculator($this->defaultPriceMock); + } + + public function testGetMaxRelationsCount() + { + $tableName = 'catalog_product_relation'; + $maxRelatedProductCount = 200; + + $connectionMock = $this->getMockBuilder(\Magento\Framework\DB\Adapter\AdapterInterface::class)->getMock(); + $this->defaultPriceMock->expects($this->once())->method('getConnection')->willReturn($connectionMock); + $this->defaultPriceMock->expects($this->once())->method('getTable')->with($tableName)->willReturn($tableName); + + $relationSelectMock = $this->getMockBuilder(\Magento\Framework\DB\Select::class) + ->disableOriginalConstructor() + ->getMock(); + $relationSelectMock->expects($this->once()) + ->method('from') + ->with( + ['relation' => $tableName], + ['count' => 'count(relation.child_id)'] + ) + ->willReturnSelf(); + $relationSelectMock->expects($this->once())->method('group')->with('parent_id')->willReturnSelf(); + $connectionMock->expects($this->at(0))->method('select')->willReturn($relationSelectMock); + + $maxSelectMock = $this->getMockBuilder(\Magento\Framework\DB\Select::class) + ->disableOriginalConstructor() + ->getMock(); + $maxSelectMock->expects($this->once()) + ->method('from') + ->with( + ['max_value' => $relationSelectMock], + ['count' => 'MAX(count)'] + ) + ->willReturnSelf(); + $connectionMock->expects($this->at(1))->method('select')->willReturn($maxSelectMock); + + $connectionMock->expects($this->at(2)) + ->method('fetchOne') + ->with($maxSelectMock) + ->willReturn($maxRelatedProductCount); + + $this->assertEquals($maxRelatedProductCount, $this->model->getMaxRelationsCount()); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Indexer/Price/CompositeProductRowSizeEstimatorTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Indexer/Price/CompositeProductRowSizeEstimatorTest.php index 2eefb4e715ad4..728044b89cafe 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Indexer/Price/CompositeProductRowSizeEstimatorTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Indexer/Price/CompositeProductRowSizeEstimatorTest.php @@ -23,7 +23,7 @@ class CompositeProductRowSizeEstimatorTest extends \PHPUnit\Framework\TestCase /** * @var \PHPUnit_Framework_MockObject_MockObject */ - private $defaultPriceMock; + private $relationsCalculatorMock; /** * @var \PHPUnit_Framework_MockObject_MockObject @@ -37,55 +37,27 @@ protected function setUp() \Magento\Customer\Model\ResourceModel\Group\CollectionFactory::class, ['create'] ); - $this->defaultPriceMock = $this->createMock( - \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\DefaultPrice::class + $this->relationsCalculatorMock = $this->createMock( + \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\CompositeProductRelationsCalculator::class ); $this->model = new CompositeProductRowSizeEstimator( - $this->defaultPriceMock, $this->websiteManagementMock, - $this->collectionFactoryMock + $this->collectionFactoryMock, + $this->relationsCalculatorMock ); } public function testEstimateRowSize() { $expectedResult = 40000000; - $tableName = 'catalog_product_relation'; $maxRelatedProductCount = 10; $this->websiteManagementMock->expects($this->once())->method('getCount')->willReturn(100); $collectionMock = $this->createMock(\Magento\Customer\Model\ResourceModel\Group\Collection::class); $this->collectionFactoryMock->expects($this->once())->method('create')->willReturn($collectionMock); $collectionMock->expects($this->once())->method('getSize')->willReturn(200); - - $connectionMock = $this->createMock(\Magento\Framework\DB\Adapter\AdapterInterface::class); - $this->defaultPriceMock->expects($this->once())->method('getConnection')->willReturn($connectionMock); - $this->defaultPriceMock->expects($this->once())->method('getTable')->with($tableName)->willReturn($tableName); - - $relationSelectMock = $this->createMock(\Magento\Framework\DB\Select::class); - $relationSelectMock->expects($this->once()) - ->method('from') - ->with( - ['relation' => $tableName], - ['count' => 'count(relation.child_id)'] - ) - ->willReturnSelf(); - $relationSelectMock->expects($this->once())->method('group')->with('parent_id')->willReturnSelf(); - $connectionMock->expects($this->at(0))->method('select')->willReturn($relationSelectMock); - - $maxSelectMock = $this->createMock(\Magento\Framework\DB\Select::class); - $maxSelectMock->expects($this->once()) - ->method('from') - ->with( - ['max_value' => $relationSelectMock], - ['count' => 'MAX(count)'] - ) - ->willReturnSelf(); - $connectionMock->expects($this->at(1))->method('select')->willReturn($maxSelectMock); - - $connectionMock->expects($this->at(2)) - ->method('fetchOne') - ->with($maxSelectMock) + $this->relationsCalculatorMock->expects($this->once()) + ->method('getMaxRelationsCount') ->willReturn($maxRelatedProductCount); $this->assertEquals( diff --git a/app/code/Magento/CatalogInventory/Model/Plugin/AroundProductRepositorySave.php b/app/code/Magento/CatalogInventory/Model/Plugin/AroundProductRepositorySave.php deleted file mode 100644 index d8378556ec3c2..0000000000000 --- a/app/code/Magento/CatalogInventory/Model/Plugin/AroundProductRepositorySave.php +++ /dev/null @@ -1,44 +0,0 @@ -get($product->getSku(), false, $product->getStoreId()); - } -} diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Model/Plugin/AroundProductRepositorySaveTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Model/Plugin/AroundProductRepositorySaveTest.php deleted file mode 100644 index 049313a9a3108..0000000000000 --- a/app/code/Magento/CatalogInventory/Test/Unit/Model/Plugin/AroundProductRepositorySaveTest.php +++ /dev/null @@ -1,351 +0,0 @@ -markTestSkipped('Test needs to be refactored.'); - $this->stockRegistry = $this->getMockBuilder(StockRegistryInterface::class) - ->setMethods(['getStockItem', 'updateStockItemBySku']) - ->getMockForAbstractClass(); - $this->storeManager = $this->getMockBuilder(StoreManagerInterface::class) - ->getMockForAbstractClass(); - $this->stockConfiguration = $this->getMockBuilder(StockConfigurationInterface::class) - ->setMethods(['getDefaultScopeId']) - ->getMockForAbstractClass(); - - $this->plugin = new AroundProductRepositorySave( - $this->stockRegistry, - $this->storeManager, - $this->stockConfiguration - ); - - $this->savedProduct = $this->getMockBuilder(ProductInterface::class) - ->setMethods(['getExtensionAttributes', 'getStoreId']) - ->getMockForAbstractClass(); - - $this->productRepository = $this->getMockBuilder(ProductRepositoryInterface::class) - ->setMethods(['get']) - ->getMockForAbstractClass(); - $this->product = $this->getMockBuilder(ProductInterface::class) - ->setMethods(['getExtensionAttributes', 'getStoreId']) - ->getMockForAbstractClass(); - $this->productExtension = $this->getMockForAbstractClass( - ProductExtensionInterface::class, - [], - '', - false, - false, - true, - ['getStockItem'] - ); - $this->stockItem = $this->getMockBuilder(StockItemInterface::class) - ->setMethods(['setWebsiteId', 'getWebsiteId', 'getStockId']) - ->getMockForAbstractClass(); - $this->defaultStock = $this->getMockBuilder(StockInterface::class) - ->setMethods(['getStockId']) - ->getMockForAbstractClass(); - } - - public function testAfterSaveWhenProductHasNoStockItemNeedingToBeUpdated() - { - // pretend we have no extension attributes at all - $this->product->expects($this->once()) - ->method('getExtensionAttributes') - ->willReturn(null); - $this->productExtension->expects($this->never())->method('getStockItem'); - - // pretend that the product already has existing stock item information - $this->stockRegistry->expects($this->once())->method('getStockItem')->willReturn($this->stockItem); - $this->stockItem->expects($this->once())->method('getItemId')->willReturn(1); - $this->stockItem->expects($this->never())->method('setProductId'); - $this->stockItem->expects($this->never())->method('setWebsiteId'); - - // expect that there are no changes to the existing stock item information - $result = $this->plugin->afterSave($this->productRepository, $this->savedProduct, $this->product); - $this->assertEquals( - $this->savedProduct, - $result - ); - } - - public function testAfterSaveWhenProductHasNoPersistentStockItemInfo() - { - // pretend we do have extension attributes, but none for 'stock_item' - $this->product->expects($this->once()) - ->method('getExtensionAttributes') - ->willReturn($this->productExtension); - $this->productExtension->expects($this->once()) - ->method('getStockItem') - ->willReturn(null); - - $this->stockConfiguration->expects($this->once())->method('getDefaultScopeId')->willReturn(1); - $this->stockRegistry->expects($this->once())->method('getStockItem')->willReturn($this->stockItem); - $this->stockRegistry->expects($this->once())->method('updateStockItemBySku'); - - $this->stockItem->expects($this->once())->method('getItemId')->willReturn(null); - $this->stockItem->expects($this->once())->method('setProductId'); - $this->stockItem->expects($this->once())->method('setWebsiteId'); - $this->product->expects(($this->atLeastOnce()))->method('getStoreId')->willReturn(20); - - $newProductMock = $this->getMockBuilder(\Magento\Catalog\Api\Data\ProductInterface::class) - ->disableOriginalConstructor()->getMock(); - $this->productRepository->expects($this->once())->method('get')->willReturn($newProductMock); - - $this->assertEquals( - $newProductMock, - $this->plugin->afterSave($this->productRepository, $this->savedProduct, $this->product) - ); - } - - public function testAfterSave() - { - $productId = 5494; - $storeId = 2; - $sku = 'my product that needs saving'; - $defaultScopeId = 100; - $this->stockConfiguration->expects($this->exactly(2)) - ->method('getDefaultScopeId') - ->willReturn($defaultScopeId); - $this->stockRegistry->expects($this->once()) - ->method('getStock') - ->with($defaultScopeId) - ->willReturn($this->defaultStock); - - $this->product->expects($this->once()) - ->method('getExtensionAttributes') - ->willReturn($this->productExtension); - $this->productExtension->expects($this->once()) - ->method('getStockItem') - ->willReturn($this->stockItem); - - $storedStockItem = $this->getMockBuilder(StockItemInterface::class) - ->setMethods(['getItemId']) - ->getMockForAbstractClass(); - $storedStockItem->expects($this->once()) - ->method('getItemId') - ->willReturn(500); - $this->stockRegistry->expects($this->once()) - ->method('getStockItem') - ->willReturn($storedStockItem); - - $this->product->expects(($this->exactly(2)))->method('getId')->willReturn($productId); - $this->product->expects(($this->atLeastOnce()))->method('getStoreId')->willReturn($storeId); - $this->product->expects($this->atLeastOnce())->method('getSku')->willReturn($sku); - - $this->stockItem->expects($this->once())->method('setProductId')->with($productId); - $this->stockItem->expects($this->once())->method('setWebsiteId')->with($defaultScopeId); - - $this->stockRegistry->expects($this->once()) - ->method('updateStockItemBySku') - ->with($sku, $this->stockItem); - - $newProductMock = $this->getMockBuilder(\Magento\Catalog\Api\Data\ProductInterface::class) - ->disableOriginalConstructor()->getMock(); - $this->productRepository->expects($this->once()) - ->method('get') - ->with($sku, false, $storeId, true) - ->willReturn($newProductMock); - - $this->assertEquals( - $newProductMock, - $this->plugin->afterSave($this->productRepository, $this->savedProduct, $this->product) - ); - } - - /** - * @expectedException \Magento\Framework\Exception\LocalizedException - * @expectedExceptionMessage Invalid stock id: 100500. Only default stock with id 50 allowed - */ - public function testAfterSaveWithInvalidStockId() - { - $stockId = 100500; - $defaultScopeId = 100; - $defaultStockId = 50; - - $this->stockItem->expects($this->once()) - ->method('getStockId') - ->willReturn($stockId); - $this->stockRegistry->expects($this->once()) - ->method('getStock') - ->with($defaultScopeId) - ->willReturn($this->defaultStock); - $this->stockConfiguration->expects($this->once()) - ->method('getDefaultScopeId') - ->willReturn($defaultScopeId); - $this->defaultStock->expects($this->once()) - ->method('getStockId') - ->willReturn($defaultStockId); - - $this->product->expects($this->once()) - ->method('getExtensionAttributes') - ->willReturn($this->productExtension); - $this->productExtension->expects($this->once()) - ->method('getStockItem') - ->willReturn($this->stockItem); - - $this->plugin->afterSave($this->productRepository, $this->savedProduct, $this->product); - } - - /** - * @expectedException \Magento\Framework\Exception\LocalizedException - * @expectedExceptionMessage Invalid stock item id: 0. Should be null or numeric value greater than 0 - */ - public function testAfterSaveWithInvalidStockItemId() - { - $stockId = 80; - $stockItemId = 0; - $defaultScopeId = 100; - $defaultStockId = 80; - - $this->stockItem->expects($this->once()) - ->method('getStockId') - ->willReturn($stockId); - $this->stockRegistry->expects($this->once()) - ->method('getStock') - ->with($defaultScopeId) - ->willReturn($this->defaultStock); - $this->stockConfiguration->expects($this->once()) - ->method('getDefaultScopeId') - ->willReturn($defaultScopeId); - $this->defaultStock->expects($this->once()) - ->method('getStockId') - ->willReturn($defaultStockId); - - $this->product->expects($this->once()) - ->method('getExtensionAttributes') - ->willReturn($this->productExtension); - $this->productExtension->expects($this->once()) - ->method('getStockItem') - ->willReturn($this->stockItem); - - $this->stockItem->expects($this->once()) - ->method('getItemId') - ->willReturn($stockItemId); - - $this->plugin->afterSave($this->productRepository, $this->savedProduct, $this->product); - } - - /** - * @expectedException \Magento\Framework\Exception\LocalizedException - * @expectedExceptionMessage Invalid stock item id: 35. Assigned stock item id is 40 - */ - public function testAfterSaveWithNotAssignedStockItemId() - { - $stockId = 80; - $stockItemId = 35; - $defaultScopeId = 100; - $defaultStockId = 80; - $storedStockitemId = 40; - - $this->stockItem->expects($this->once()) - ->method('getStockId') - ->willReturn($stockId); - $this->stockRegistry->expects($this->once()) - ->method('getStock') - ->with($defaultScopeId) - ->willReturn($this->defaultStock); - $this->stockConfiguration->expects($this->once()) - ->method('getDefaultScopeId') - ->willReturn($defaultScopeId); - $this->defaultStock->expects($this->once()) - ->method('getStockId') - ->willReturn($defaultStockId); - - $this->product->expects($this->once()) - ->method('getExtensionAttributes') - ->willReturn($this->productExtension); - $this->productExtension->expects($this->once()) - ->method('getStockItem') - ->willReturn($this->stockItem); - - $this->stockItem->expects($this->once()) - ->method('getItemId') - ->willReturn($stockItemId); - - $storedStockItem = $this->getMockBuilder(StockItemInterface::class) - ->setMethods(['getItemId']) - ->getMockForAbstractClass(); - $storedStockItem->expects($this->once()) - ->method('getItemId') - ->willReturn($storedStockitemId); - $this->stockRegistry->expects($this->once()) - ->method('getStockItem') - ->willReturn($storedStockItem); - - $this->plugin->afterSave($this->productRepository, $this->savedProduct, $this->product); - } -} diff --git a/app/code/Magento/CatalogInventory/etc/di.xml b/app/code/Magento/CatalogInventory/etc/di.xml index e62470b3ae0fb..2f64d71595c86 100644 --- a/app/code/Magento/CatalogInventory/etc/di.xml +++ b/app/code/Magento/CatalogInventory/etc/di.xml @@ -71,9 +71,6 @@ - - - Magento\CatalogInventory\Model\Indexer\Stock\Processor diff --git a/app/code/Magento/ConfigurableProduct/Model/Plugin/ProductRepositorySave.php b/app/code/Magento/ConfigurableProduct/Model/Plugin/ProductRepositorySave.php index 075b07c38eb9f..54b6e42ff7678 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Plugin/ProductRepositorySave.php +++ b/app/code/Magento/ConfigurableProduct/Model/Plugin/ProductRepositorySave.php @@ -81,9 +81,9 @@ public function afterSave( $attributeCodes[] = $attributeCode; } $this->validateProductLinks($attributeCodes, $configurableLinks); - $product->getTypeInstance()->resetConfigurableAttributes($product); + $result->getTypeInstance()->resetConfigurableAttributes($product); - return $product; + return $result; } /** diff --git a/app/code/Magento/ConfigurableProduct/etc/di.xml b/app/code/Magento/ConfigurableProduct/etc/di.xml index 1ec382bf37b29..30fd8737c3820 100644 --- a/app/code/Magento/ConfigurableProduct/etc/di.xml +++ b/app/code/Magento/ConfigurableProduct/etc/di.xml @@ -180,6 +180,9 @@ Magento\Catalog\Model\Indexer\Price\CompositeProductBatchSizeManagement + + Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\CompositeProductBatchSizeAdjuster + diff --git a/app/code/Magento/GroupedProduct/etc/di.xml b/app/code/Magento/GroupedProduct/etc/di.xml index ff2cac0bd3186..f39bcfa01453c 100644 --- a/app/code/Magento/GroupedProduct/etc/di.xml +++ b/app/code/Magento/GroupedProduct/etc/di.xml @@ -94,6 +94,9 @@ Magento\Catalog\Model\Indexer\Price\CompositeProductBatchSizeManagement + + Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\CompositeProductBatchSizeAdjuster + diff --git a/app/code/Magento/ProductVideo/view/frontend/web/js/fotorama-add-video-events.js b/app/code/Magento/ProductVideo/view/frontend/web/js/fotorama-add-video-events.js index f78e424d741eb..03ce42bf25c4a 100644 --- a/app/code/Magento/ProductVideo/view/frontend/web/js/fotorama-add-video-events.js +++ b/app/code/Magento/ProductVideo/view/frontend/web/js/fotorama-add-video-events.js @@ -617,6 +617,7 @@ define([ var videoSettings; videoSettings = this.options.videoSettings[0]; + $image.find('.' + this.PV).remove(); $image.append( '
+ catalog_category_product + catalog_product_category + @@ -45,6 +49,10 @@ 4 catalogProductSimple::default_in_custom_website catalogProductSimple::default + + catalog_category_product + catalog_product_category + @@ -61,6 +69,10 @@ 7 7 catalogProductSimple::default_in_custom_website + + catalog_category_product + catalog_product_category + diff --git a/dev/tests/functional/tests/app/Magento/CatalogImportExport/Test/etc/testcase.xml b/dev/tests/functional/tests/app/Magento/CatalogImportExport/Test/etc/testcase.xml index 3438787d668f7..be8bf8b89dd16 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogImportExport/Test/etc/testcase.xml +++ b/dev/tests/functional/tests/app/Magento/CatalogImportExport/Test/etc/testcase.xml @@ -11,6 +11,7 @@ - + + diff --git a/dev/tests/functional/tests/app/Magento/Indexer/Test/TestStep/ReindexStep.php b/dev/tests/functional/tests/app/Magento/Indexer/Test/TestStep/ReindexStep.php new file mode 100644 index 0000000000000..1a3cb4a034765 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Indexer/Test/TestStep/ReindexStep.php @@ -0,0 +1,50 @@ +indexer = $indexer; + $this->indexerType = $indexerType; + } + + /** + * Run reindex process. + * All indexers will be refreshed in a case of empty $indexerType array. + * + * @return void + */ + public function run() + { + $this->indexer->reindex($this->indexerType); + } +} diff --git a/lib/internal/Magento/Framework/EntityManager/OperationPool.php b/lib/internal/Magento/Framework/EntityManager/OperationPool.php index f79df58f89a24..97d580e328b1b 100644 --- a/lib/internal/Magento/Framework/EntityManager/OperationPool.php +++ b/lib/internal/Magento/Framework/EntityManager/OperationPool.php @@ -8,12 +8,28 @@ use Magento\Framework\ObjectManagerInterface as ObjectManager; use Magento\Framework\EntityManager\OperationInterface; +use Magento\Framework\EntityManager\Operation\CheckIfExists; +use Magento\Framework\EntityManager\Operation\Read; +use Magento\Framework\EntityManager\Operation\Create; +use Magento\Framework\EntityManager\Operation\Update; +use Magento\Framework\EntityManager\Operation\Delete; /** * Class OperationPool */ class OperationPool { + /** + * @var array + */ + private $defaultOperations = [ + 'checkIfExists' => CheckIfExists::class, + 'read' => Read::class, + 'create' => Create::class, + 'update' => Update::class, + 'delete' => Delete::class, + ]; + /** * @var array */ @@ -31,10 +47,13 @@ class OperationPool */ public function __construct( ObjectManager $objectManager, - $operations + $operations = [] ) { $this->objectManager = $objectManager; - $this->operations = $operations; + $this->operations = array_replace_recursive( + ['default' => $this->defaultOperations], + $operations + ); } /** diff --git a/lib/internal/Magento/Framework/EntityManager/Test/Unit/OperationPoolTest.php b/lib/internal/Magento/Framework/EntityManager/Test/Unit/OperationPoolTest.php new file mode 100644 index 0000000000000..cd44a3088122f --- /dev/null +++ b/lib/internal/Magento/Framework/EntityManager/Test/Unit/OperationPoolTest.php @@ -0,0 +1,47 @@ +createMock(ObjectManagerInterface::class); + $operationPool = new OperationPool( + $objectManagerMock, + [] + ); + + $objectManagerMock->expects($this->once()) + ->method('get') + ->with(\Magento\Framework\EntityManager\Operation\Read::class); + $operationPool->getOperation('entity_type', 'read'); + } + + public function testGetOperationUsesOverriddenDefaultValueForEntityThatDoesNotProvideCustomMapping() + { + $customReadOperation = 'CustomReadOperation'; + $objectManagerMock = $this->createMock(ObjectManagerInterface::class); + $operationPool = new OperationPool( + $objectManagerMock, + [ + 'default' => [ + 'read' => $customReadOperation, + 'new' => 'CustomNewOperation', + ], + ] + ); + + $objectManagerMock->expects($this->once()) + ->method('get') + ->with($customReadOperation); + $operationPool->getOperation('entity_type', 'read'); + } +}