From 9504113e65f0ad0e4d584fb1d31d6c2753135005 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Tue, 28 Mar 2023 14:32:25 -0500 Subject: [PATCH 01/24] ACP2E-1716: Site down after upgrade --- .../SelectionProductsDisabledRequired.php | 164 ------------------ .../DisabledProductOptionPriceModifier.php | 148 ---------------- .../Bundle/Plugin/Catalog/Helper/Product.php | 82 --------- app/code/Magento/Bundle/etc/di.xml | 7 - app/code/Magento/Bundle/etc/frontend/di.xml | 3 - .../DisabledProductOptionPostProcessor.php | 70 -------- app/code/Magento/BundleGraphQl/composer.json | 1 - app/code/Magento/BundleGraphQl/etc/di.xml | 7 - .../Model/ResourceModel/Stock/Status.php | 6 +- .../GraphQl/Bundle/BundleProductViewTest.php | 32 +++- 10 files changed, 28 insertions(+), 492 deletions(-) delete mode 100644 app/code/Magento/Bundle/Model/Product/SelectionProductsDisabledRequired.php delete mode 100644 app/code/Magento/Bundle/Model/ResourceModel/Indexer/Price/DisabledProductOptionPriceModifier.php delete mode 100644 app/code/Magento/Bundle/Plugin/Catalog/Helper/Product.php delete mode 100644 app/code/Magento/BundleGraphQl/Model/Resolver/Products/DataProvider/Product/DisabledProductOptionPostProcessor.php diff --git a/app/code/Magento/Bundle/Model/Product/SelectionProductsDisabledRequired.php b/app/code/Magento/Bundle/Model/Product/SelectionProductsDisabledRequired.php deleted file mode 100644 index d3f1c2f1c999..000000000000 --- a/app/code/Magento/Bundle/Model/Product/SelectionProductsDisabledRequired.php +++ /dev/null @@ -1,164 +0,0 @@ -bundleSelection = $bundleSelection; - $this->storeManager = $storeManager; - $this->catalogProductStatus = $catalogProductStatus; - $this->productCollectionFactory = $productCollectionFactory; - $this->metadataPool = $metadataPool; - } - - /** - * Return ids of options and child products when all products in required option are disabled in bundle product - * - * @param int $bundleId - * @param int|null $websiteId - * @return array - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - */ - public function getChildProductIds(int $bundleId, ?int $websiteId = null): array - { - if (!$websiteId) { - $websiteId = (int)$this->storeManager->getStore()->getWebsiteId(); - } - $cacheKey = $this->getCacheKey($bundleId, $websiteId); - if (isset($this->productsDisabledRequired[$cacheKey])) { - return $this->productsDisabledRequired[$cacheKey]; - } - $selectionProductIds = $this->bundleSelection->getChildrenIds($bundleId); - /** for cases when no required products found */ - if (count($selectionProductIds) === 1 && isset($selectionProductIds[0])) { - $this->productsDisabledRequired[$cacheKey] = []; - return $this->productsDisabledRequired[$cacheKey]; - } - $products = $this->getProducts($selectionProductIds, $websiteId); - if (!$products) { - $this->productsDisabledRequired[$cacheKey] = []; - return $this->productsDisabledRequired[$cacheKey]; - } - foreach ($selectionProductIds as $optionId => $optionProductIds) { - foreach ($optionProductIds as $productId) { - if (isset($products[$productId])) { - /** @var Product $product */ - $product = $products[$productId]; - if (in_array($product->getStatus(), $this->catalogProductStatus->getVisibleStatusIds())) { - unset($selectionProductIds[$optionId]); - } - } - } - } - $this->productsDisabledRequired[$cacheKey] = $selectionProductIds; - return $this->productsDisabledRequired[$cacheKey]; - } - - /** - * Get products objects - * - * @param array $selectionProductIds - * @param int $websiteId - * @return ProductInterface[] - */ - private function getProducts(array $selectionProductIds, int $websiteId): array - { - $productIds = []; - $defaultStore = $this->storeManager->getWebsite($websiteId)->getDefaultStore(); - $defaultStoreId = $defaultStore ? $defaultStore->getId() : null; - foreach ($selectionProductIds as $optionProductIds) { - $productIds[] = $optionProductIds; - } - $productIds = array_merge([], ...$productIds); - $productCollection = $this->productCollectionFactory->create(); - $productCollection->joinAttribute( - ProductInterface::STATUS, - Product::ENTITY . '/' . ProductInterface::STATUS, - $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField(), - null, - 'inner', - $defaultStoreId - ); - $productCollection->addIdFilter($productIds); - $productCollection->addStoreFilter($defaultStoreId); - $productCollection->setFlag($this->hasStockStatusFilter, true); - return $productCollection->getItems(); - } - - /** - * Get cache key - * - * @param int $bundleId - * @param int $websiteId - * @return string - */ - private function getCacheKey(int $bundleId, int $websiteId): string - { - return $bundleId . '-' . $websiteId; - } -} diff --git a/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Price/DisabledProductOptionPriceModifier.php b/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Price/DisabledProductOptionPriceModifier.php deleted file mode 100644 index b3c3e74e1fa6..000000000000 --- a/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Price/DisabledProductOptionPriceModifier.php +++ /dev/null @@ -1,148 +0,0 @@ -resourceConnection = $resourceConnection; - $this->config = $config; - $this->metadataPool = $metadataPool; - $this->bundleSelection = $bundleSelection; - $this->selectionProductsDisabledRequired = $selectionProductsDisabledRequired; - } - - /** - * Remove bundle product from price index when all products in required option are disabled - * - * @param IndexTableStructure $priceTable - * @param array $entityIds - * @return void - * @throws \Magento\Framework\Exception\LocalizedException - */ - public function modifyPrice(IndexTableStructure $priceTable, array $entityIds = []) : void - { - foreach ($this->getBundleIds($entityIds) as $entityId) { - $entityId = (int) $entityId; - foreach ($this->getWebsiteIdsOfProduct($entityId) as $websiteId) { - $websiteId = (int) $websiteId; - $productIdsDisabledRequired = $this->selectionProductsDisabledRequired - ->getChildProductIds($entityId, $websiteId); - if ($productIdsDisabledRequired) { - $connection = $this->resourceConnection->getConnection('indexer'); - $select = $connection->select(); - $select->from(['price_index' => $priceTable->getTableName()], []); - $priceEntityField = $priceTable->getEntityField(); - $select->where('price_index.website_id = ?', $websiteId); - $select->where("price_index.{$priceEntityField} = ?", $entityId); - $query = $select->deleteFromSelect('price_index'); - $connection->query($query); - } - } - } - } - - /** - * Get all website ids of product - * - * @param int $entityId - * @return array - */ - private function getWebsiteIdsOfProduct(int $entityId): array - { - if (isset($this->websiteIdsOfProduct[$entityId])) { - return $this->websiteIdsOfProduct[$entityId]; - } - $connection = $this->resourceConnection->getConnection('indexer'); - $select = $connection->select(); - $select->from( - ['product_in_websites' => $this->resourceConnection->getTableName('catalog_product_website')], - ['website_id'] - )->where('product_in_websites.product_id = ?', $entityId); - $this->websiteIdsOfProduct[$entityId] = $connection->fetchCol($select); - - return $this->websiteIdsOfProduct[$entityId]; - } - - /** - * Get Bundle Ids - * - * @param array $entityIds - * @return \Traversable - */ - private function getBundleIds(array $entityIds): \Traversable - { - $connection = $this->resourceConnection->getConnection('indexer'); - $select = $connection->select(); - $select->from( - ['cpe' => $this->resourceConnection->getTableName('catalog_product_entity')], - ['entity_id'] - )->where('cpe.entity_id in ( ? )', !empty($entityIds) ? $entityIds : [0], \Zend_Db::INT_TYPE) - ->where('type_id = ?', Type::TYPE_BUNDLE); - - $statement = $select->query(); - while ($id = $statement->fetchColumn()) { - yield $id; - } - } -} diff --git a/app/code/Magento/Bundle/Plugin/Catalog/Helper/Product.php b/app/code/Magento/Bundle/Plugin/Catalog/Helper/Product.php deleted file mode 100644 index 0b090b2cbad7..000000000000 --- a/app/code/Magento/Bundle/Plugin/Catalog/Helper/Product.php +++ /dev/null @@ -1,82 +0,0 @@ -selectionProductsDisabledRequired = $selectionProductsDisabledRequired; - $this->scopeConfig = $scopeConfig; - $this->productRepository = $productRepository; - } - - /** - * Do not show bundle product when all products in required option are disabled - * - * @param Subject $subject - * @param bool $result - * @param ProductModel|int $product - * @return bool - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function afterCanShow(Subject $subject, $result, $product) - { - if (is_int($product)) { - $product = $this->productRepository->getById($product); - } - $productId = (int)$product->getEntityId(); - if ($result == false || $product->getTypeId() !== Type::TYPE_BUNDLE) { - return $result; - } - $isShowOutOfStock = $this->scopeConfig->getValue( - Configuration::XML_PATH_SHOW_OUT_OF_STOCK, - ScopeInterface::SCOPE_STORE - ); - if ($isShowOutOfStock) { - return $result; - } - $productIdsDisabledRequired = $this->selectionProductsDisabledRequired->getChildProductIds($productId); - return $productIdsDisabledRequired ? false : $result; - } -} diff --git a/app/code/Magento/Bundle/etc/di.xml b/app/code/Magento/Bundle/etc/di.xml index c5c4a491234e..47329a0afdcd 100644 --- a/app/code/Magento/Bundle/etc/di.xml +++ b/app/code/Magento/Bundle/etc/di.xml @@ -276,13 +276,6 @@ - - - - Magento\Bundle\Model\ResourceModel\Indexer\Price\DisabledProductOptionPriceModifier - - - diff --git a/app/code/Magento/Bundle/etc/frontend/di.xml b/app/code/Magento/Bundle/etc/frontend/di.xml index 411cf91cbc8b..54f5ff0a1f48 100644 --- a/app/code/Magento/Bundle/etc/frontend/di.xml +++ b/app/code/Magento/Bundle/etc/frontend/di.xml @@ -22,7 +22,4 @@ - - - diff --git a/app/code/Magento/BundleGraphQl/Model/Resolver/Products/DataProvider/Product/DisabledProductOptionPostProcessor.php b/app/code/Magento/BundleGraphQl/Model/Resolver/Products/DataProvider/Product/DisabledProductOptionPostProcessor.php deleted file mode 100644 index 8887fa14fd8c..000000000000 --- a/app/code/Magento/BundleGraphQl/Model/Resolver/Products/DataProvider/Product/DisabledProductOptionPostProcessor.php +++ /dev/null @@ -1,70 +0,0 @@ -selectionProductsDisabledRequired = $selectionProductsDisabledRequired; - } - - /** - * Remove bundle product from collection when all products in required option are disabled - * - * @param Collection $collection - * @param array $attributeNames - * @param ContextInterface|null $context - * @return Collection - * @throws \Magento\Framework\Exception\LocalizedException - * @throws \Magento\Framework\Exception\NoSuchEntityException - */ - public function process( - Collection $collection, - array $attributeNames, - ?ContextInterface $context = null - ): Collection { - if (!$collection->isLoaded()) { - $collection->load(); - } - /** @var Product $product */ - foreach ($collection as $key => $product) { - if ($product->getTypeId() !== Product\Type::TYPE_BUNDLE || $context === null) { - continue; - } - $productId = (int)$product->getEntityId(); - $websiteId = (int)$context->getExtensionAttributes()->getStore()->getWebsiteId(); - $productIdsDisabledRequired = $this->selectionProductsDisabledRequired->getChildProductIds( - $productId, - $websiteId - ); - if ($productIdsDisabledRequired) { - $collection->removeItemByKey($key); - } - } - return $collection; - } -} diff --git a/app/code/Magento/BundleGraphQl/composer.json b/app/code/Magento/BundleGraphQl/composer.json index 7d29641125a3..8863ff700a57 100644 --- a/app/code/Magento/BundleGraphQl/composer.json +++ b/app/code/Magento/BundleGraphQl/composer.json @@ -6,7 +6,6 @@ "php": "~8.1.0||~8.2.0", "magento/module-catalog": "*", "magento/module-bundle": "*", - "magento/module-graph-ql": "*", "magento/module-catalog-graph-ql": "*", "magento/module-quote": "*", "magento/module-quote-graph-ql": "*", diff --git a/app/code/Magento/BundleGraphQl/etc/di.xml b/app/code/Magento/BundleGraphQl/etc/di.xml index 879359839a64..15acad7c6bf0 100644 --- a/app/code/Magento/BundleGraphQl/etc/di.xml +++ b/app/code/Magento/BundleGraphQl/etc/di.xml @@ -23,11 +23,4 @@ - - - - Magento\BundleGraphQl\Model\Resolver\Products\DataProvider\Product\DisabledProductOptionPostProcessor - - - diff --git a/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Status.php b/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Status.php index bb38c03f5854..007ecff49a2f 100644 --- a/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Status.php +++ b/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Status.php @@ -23,7 +23,8 @@ * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @api * - * @deprecated 100.3.0 Replaced with Multi Source Inventory + * @deprecated 100.3.0 + * @see Replaced with Multi Source Inventory * @link https://developer.adobe.com/commerce/webapi/rest/inventory/index.html * @link https://developer.adobe.com/commerce/webapi/rest/inventory/inventory-api-reference.html * @since 100.0.2 @@ -35,6 +36,7 @@ class Status extends AbstractDb * * @var StoreManagerInterface * @deprecated 100.1.0 + * @see Not used anymore */ protected $_storeManager; @@ -227,7 +229,7 @@ public function getProductCollection($lastEntityId = 0, $limit = 1000) */ public function addStockStatusToSelect(Select $select, Website $website) { - $websiteId = $this->getWebsiteId($website->getId()); + $websiteId = $this->getWebsiteId(); $select->joinLeft( ['stock_status' => $this->getMainTable()], 'e.entity_id = stock_status.product_id AND stock_status.website_id=' . $websiteId, diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/BundleProductViewTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/BundleProductViewTest.php index 3207d2458308..3d389473264c 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/BundleProductViewTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/BundleProductViewTest.php @@ -14,13 +14,15 @@ use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Test\Fixture\Product as ProductFixture; use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Store\Test\Fixture\Group as GroupFixture; +use Magento\Store\Test\Fixture\Store as StoreFixture; +use Magento\Store\Test\Fixture\Website as WebsiteFixture; use Magento\TestFramework\Fixture\DataFixture; +use Magento\TestFramework\Fixture\DbIsolation; use Magento\TestFramework\ObjectManager; use Magento\TestFramework\TestCase\GraphQlAbstract; use Magento\Store\Model\StoreManagerInterface; use Magento\Catalog\Model\Product\Attribute\Source\Status as ProductStatus; -use Magento\TestFramework\Fixture\DataFixtureStorage; -use Magento\TestFramework\Fixture\DataFixtureStorageManager; /** * Test querying Bundle products @@ -279,7 +281,7 @@ private function assertBundleProductOptions($product, $actualResponse, $isChildV 'quantity' => (int)$bundleProductLink->getQty(), 'position' => $bundleProductLink->getPosition(), 'is_default' => (bool)$bundleProductLink->getIsDefault(), - 'price_type' => self::KEY_PRICE_TYPE_FIXED, + 'price_type' => self::KEY_PRICE_TYPE_FIXED, 'can_change_quantity' => $bundleProductLink->getCanChangeQuantity() ] ); @@ -448,9 +450,20 @@ public function testNonExistentFieldQtyExceptionOnBundleProduct() $this->graphQlQuery($query); } - /** - * @magentoApiDataFixture Magento/Bundle/_files/product_1.php - */ + #[ + DbIsolation(false), + DataFixture(WebsiteFixture::class, as: 'website2'), + DataFixture(GroupFixture::class, ['website_id' => '$website2.id$'], 'group2'), + DataFixture(StoreFixture::class, ['store_group_id' => '$group2.id$', 'code' => 'store2'], 'store2'), + DataFixture(ProductFixture::class, ['sku' => 'p1', 'website_ids' => [1, '$website2.id$']], 'p1'), + DataFixture(ProductFixture::class, ['sku' => 'p2', 'website_ids' => [1, '$website2.id$']], 'p2'), + DataFixture(BundleOptionFixture::class, ['product_links' => ['$p1$']], 'opt1'), + DataFixture(BundleOptionFixture::class, ['product_links' => ['$p2$']], 'opt2'), + DataFixture( + BundleProductFixture::class, + ['sku' => 'bundle-product', '_options' => ['$opt1$', '$opt2$'], 'website_ids' => [1, '$website2.id$']] + ), + ] public function testBundleProductWithDisabledProductOption() { /** @var StoreManagerInterface $storeManager */ @@ -458,7 +471,7 @@ public function testBundleProductWithDisabledProductOption() $storeIdDefault = $storeManager->getDefaultStoreView()->getId(); /** @var ProductRepositoryInterface $productRepository */ $productRepository = ObjectManager::getInstance()->get(ProductRepositoryInterface::class); - $simpleProduct = $productRepository->get('simple', false, $storeIdDefault, true); + $simpleProduct = $productRepository->get('p1', true, $storeIdDefault, true); $simpleProduct->setStatus(ProductStatus::STATUS_DISABLED); $simpleProduct->setStoreIds([$storeIdDefault]); $productRepository->save($simpleProduct); @@ -513,8 +526,11 @@ public function testBundleProductWithDisabledProductOption() } QUERY; - $response = $this->graphQlQuery($query); + $response = $this->graphQlQuery($query, [], '', ['Store' => 'default']); $this->assertEmpty($response['products']['items']); + $response = $this->graphQlQuery($query, [], '', ['Store' => 'store2']); + $this->assertNotEmpty($response['products']['items']); + $this->assertEquals($productSku, $response['products']['items'][0]['sku']); } #[ From f87e8f5251ffc54ed5f22f623ecfa47c1b5b5ec5 Mon Sep 17 00:00:00 2001 From: aplapana Date: Wed, 15 Nov 2023 14:02:00 +0200 Subject: [PATCH 02/24] ACP2E-2494: Performance issue when loading product attributes in cart rules - implemented sub-query solution --- .../Magento/SalesRule/Model/ResourceModel/Rule.php | 11 ++++++----- .../SalesRule/Model/ResourceModel/RuleTest.php | 1 - 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/SalesRule/Model/ResourceModel/Rule.php b/app/code/Magento/SalesRule/Model/ResourceModel/Rule.php index 125e2194b45e..d04a2e3d9e49 100644 --- a/app/code/Magento/SalesRule/Model/ResourceModel/Rule.php +++ b/app/code/Magento/SalesRule/Model/ResourceModel/Rule.php @@ -305,18 +305,19 @@ public function getStoreLabel($ruleId, $storeId) public function getActiveAttributes() { $connection = $this->getConnection(); + $subSelect = $connection->select(); + $subSelect->reset(); + $subSelect->from($this->getTable('salesrule_product_attribute')) + ->group('attribute_id'); $select = $connection->select()->from( - ['a' => $this->getTable('salesrule_product_attribute')], + ['a' => $subSelect], new \Zend_Db_Expr('DISTINCT ea.attribute_code') )->joinInner( ['ea' => $this->getTable('eav_attribute')], 'ea.attribute_id = a.attribute_id', [] - )->joinInner( - ['sr' => $this->getTable('salesrule')], - 'a.' . $this->getLinkField() . ' = sr.' . $this->getLinkField() . ' AND sr.is_active = 1', - [] ); + return $connection->fetchAll($select); } diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/Model/ResourceModel/RuleTest.php b/dev/tests/integration/testsuite/Magento/SalesRule/Model/ResourceModel/RuleTest.php index 1299c5fca09f..8cf450a92bd4 100644 --- a/dev/tests/integration/testsuite/Magento/SalesRule/Model/ResourceModel/RuleTest.php +++ b/dev/tests/integration/testsuite/Magento/SalesRule/Model/ResourceModel/RuleTest.php @@ -73,7 +73,6 @@ public function testGetActiveAttributes() { $rule = $this->fixtures->get('rule1'); $items = $this->resource->getActiveAttributes(); - $this->assertEquals([], $items); $rule->setIsActive(1); $rule->save(); $items = $this->resource->getActiveAttributes(); From bd8920d96ddb51fe17a28c6e6d22fe94dfb0a8c4 Mon Sep 17 00:00:00 2001 From: aplapana Date: Thu, 16 Nov 2023 11:42:11 +0200 Subject: [PATCH 03/24] ACP2E-2494: Performance issue when loading product attributes in cart rules - implemented CR --- app/code/Magento/SalesRule/Model/ResourceModel/Rule.php | 2 +- .../Magento/SalesRule/Model/ResourceModel/RuleTest.php | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/code/Magento/SalesRule/Model/ResourceModel/Rule.php b/app/code/Magento/SalesRule/Model/ResourceModel/Rule.php index d04a2e3d9e49..200029d5e27b 100644 --- a/app/code/Magento/SalesRule/Model/ResourceModel/Rule.php +++ b/app/code/Magento/SalesRule/Model/ResourceModel/Rule.php @@ -311,7 +311,7 @@ public function getActiveAttributes() ->group('attribute_id'); $select = $connection->select()->from( ['a' => $subSelect], - new \Zend_Db_Expr('DISTINCT ea.attribute_code') + new \Zend_Db_Expr('ea.attribute_code') )->joinInner( ['ea' => $this->getTable('eav_attribute')], 'ea.attribute_id = a.attribute_id', diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/Model/ResourceModel/RuleTest.php b/dev/tests/integration/testsuite/Magento/SalesRule/Model/ResourceModel/RuleTest.php index 8cf450a92bd4..f2ec3bbb5449 100644 --- a/dev/tests/integration/testsuite/Magento/SalesRule/Model/ResourceModel/RuleTest.php +++ b/dev/tests/integration/testsuite/Magento/SalesRule/Model/ResourceModel/RuleTest.php @@ -72,7 +72,6 @@ public function testAfterSave() public function testGetActiveAttributes() { $rule = $this->fixtures->get('rule1'); - $items = $this->resource->getActiveAttributes(); $rule->setIsActive(1); $rule->save(); $items = $this->resource->getActiveAttributes(); From 7d5c6c414a97f1d84b15c9188161d12fd5ad9178 Mon Sep 17 00:00:00 2001 From: aplapana Date: Mon, 11 Dec 2023 09:25:43 +0200 Subject: [PATCH 04/24] ACP2E-2494: Performance issue when loading product attributes in cart rules - testing PAT --- .../Magento/SalesRule/Model/ResourceModel/Rule.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/SalesRule/Model/ResourceModel/Rule.php b/app/code/Magento/SalesRule/Model/ResourceModel/Rule.php index 200029d5e27b..f1a80e5b8e55 100644 --- a/app/code/Magento/SalesRule/Model/ResourceModel/Rule.php +++ b/app/code/Magento/SalesRule/Model/ResourceModel/Rule.php @@ -305,17 +305,17 @@ public function getStoreLabel($ruleId, $storeId) public function getActiveAttributes() { $connection = $this->getConnection(); - $subSelect = $connection->select(); - $subSelect->reset(); - $subSelect->from($this->getTable('salesrule_product_attribute')) - ->group('attribute_id'); $select = $connection->select()->from( - ['a' => $subSelect], - new \Zend_Db_Expr('ea.attribute_code') + ['a' => $this->getTable('salesrule_product_attribute')], + new \Zend_Db_Expr('DISTINCT ea.attribute_code') )->joinInner( ['ea' => $this->getTable('eav_attribute')], 'ea.attribute_id = a.attribute_id', [] + )->joinInner( + ['sr' => $this->getTable('salesrule')], + 'a.' . $this->getLinkField() . ' = sr.' . $this->getLinkField() . ' AND sr.is_active = 1', + [] ); return $connection->fetchAll($select); From 6c73a6cc22c1e5ad457f3e2cbea812c2bd2dac04 Mon Sep 17 00:00:00 2001 From: aplapana Date: Mon, 11 Dec 2023 11:46:49 +0200 Subject: [PATCH 05/24] ACP2E-2494: Performance issue when loading product attributes in cart rules - testing PAT --- .../Magento/SalesRule/Model/ResourceModel/Rule.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/SalesRule/Model/ResourceModel/Rule.php b/app/code/Magento/SalesRule/Model/ResourceModel/Rule.php index f1a80e5b8e55..4ed17d7ef815 100644 --- a/app/code/Magento/SalesRule/Model/ResourceModel/Rule.php +++ b/app/code/Magento/SalesRule/Model/ResourceModel/Rule.php @@ -305,17 +305,17 @@ public function getStoreLabel($ruleId, $storeId) public function getActiveAttributes() { $connection = $this->getConnection(); + $subSelect = $connection->select(); + $subSelect->reset(); + $subSelect->from($this->getTable('salesrule_product_attribute')) + ->group('attribute_id'); $select = $connection->select()->from( - ['a' => $this->getTable('salesrule_product_attribute')], - new \Zend_Db_Expr('DISTINCT ea.attribute_code') + ['a' => $subSelect], + new \Zend_Db_Expr('ea.attribute_code') )->joinInner( ['ea' => $this->getTable('eav_attribute')], 'ea.attribute_id = a.attribute_id', [] - )->joinInner( - ['sr' => $this->getTable('salesrule')], - 'a.' . $this->getLinkField() . ' = sr.' . $this->getLinkField() . ' AND sr.is_active = 1', - [] ); return $connection->fetchAll($select); From 6f20e7cd784f85c7501c5f733d42ec0709f9856a Mon Sep 17 00:00:00 2001 From: soumah Date: Wed, 20 Dec 2023 15:09:21 -0600 Subject: [PATCH 06/24] ACP2E-2642: Fastly cache not cleared for content staging update --- app/code/Magento/Cms/Test/Fixture/Block.php | 69 +++++++++ .../GraphQlCache/Model/Plugin/View/Layout.php | 57 +++++++ .../Magento/GraphQlCache/etc/graphql/di.xml | 3 + .../GraphQl/PageCache/CacheTagTest.php | 144 ++++++++++++++++++ 4 files changed, 273 insertions(+) create mode 100644 app/code/Magento/Cms/Test/Fixture/Block.php create mode 100644 app/code/Magento/GraphQlCache/Model/Plugin/View/Layout.php diff --git a/app/code/Magento/Cms/Test/Fixture/Block.php b/app/code/Magento/Cms/Test/Fixture/Block.php new file mode 100644 index 000000000000..2a3c4079504e --- /dev/null +++ b/app/code/Magento/Cms/Test/Fixture/Block.php @@ -0,0 +1,69 @@ + 'block%uniqid%', + BlockInterface::TITLE => 'Block%uniqid%', + BlockInterface::CONTENT => 'BlockContent%uniqid%', + BlockInterface::CREATION_TIME => null, + BlockInterface::UPDATE_TIME => null, + 'active' => true + ]; + + /** + * @param ProcessorInterface $dataProcessor + * @param ServiceFactory $serviceFactory + */ + public function __construct( + private readonly ProcessorInterface $dataProcessor, + private readonly ServiceFactory $serviceFactory + ) { + } + + /** + * {@inheritdoc} + * @param array $data Parameters. Same format as Block::DEFAULT_DATA. + */ + public function apply(array $data = []): ?DataObject + { + $data = $this->dataProcessor->process($this, array_merge(self::DEFAULT_DATA, $data)); + $service = $this->serviceFactory->create(BlockRepositoryInterface::class, 'save'); + + return $service->execute(['block' => $data]); + } + + /** + * @inheritdoc + */ + public function revert(DataObject $data): void + { + $service = $this->serviceFactory->create(BlockRepositoryInterface::class, 'deleteById'); + $service->execute(['blockId' => $data->getId()]); + } +} diff --git a/app/code/Magento/GraphQlCache/Model/Plugin/View/Layout.php b/app/code/Magento/GraphQlCache/Model/Plugin/View/Layout.php new file mode 100644 index 000000000000..c2668a5d4d40 --- /dev/null +++ b/app/code/Magento/GraphQlCache/Model/Plugin/View/Layout.php @@ -0,0 +1,57 @@ +cacheableQuery->addCacheTags($block->getIdentities()); + } + return $result; + } +} diff --git a/app/code/Magento/GraphQlCache/etc/graphql/di.xml b/app/code/Magento/GraphQlCache/etc/graphql/di.xml index 1a85f02b5be9..f1a63cfb7be7 100644 --- a/app/code/Magento/GraphQlCache/etc/graphql/di.xml +++ b/app/code/Magento/GraphQlCache/etc/graphql/di.xml @@ -29,4 +29,7 @@ + + + diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/PageCache/CacheTagTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/PageCache/CacheTagTest.php index 93e6caebe2ba..46fa6de59467 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/PageCache/CacheTagTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/PageCache/CacheTagTest.php @@ -7,9 +7,19 @@ namespace Magento\GraphQl\PageCache; +use Magento\Catalog\Api\CategoryRepositoryInterface; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Model\Product; +use Magento\Catalog\Test\Fixture\Category as CategoryFixture; +use Magento\Cms\Model\BlockRepository; +use Magento\Cms\Test\Fixture\Block as BlockFixture; use Magento\GraphQlCache\Model\CacheId\CacheIdCalculator; +use Magento\PageCache\Model\Config; +use Magento\Store\Model\Store; +use Magento\TestFramework\Fixture\Config as ConfigFixture; +use Magento\TestFramework\Fixture\DataFixture; +use Magento\TestFramework\Fixture\DataFixtureStorageManager; +use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\ObjectManager; /** @@ -133,6 +143,140 @@ public function testCacheInvalidationForCategoriesWithProduct() ); } + #[ + ConfigFixture(Config::XML_PAGECACHE_TYPE, Config::VARNISH), + DataFixture(BlockFixture::class, ['content' => 'Original Block content'], as: 'block'), + DataFixture(CategoryFixture::class, as: 'category1'), + DataFixture(CategoryFixture::class, ['description' => 'Original Category description'], as: 'category2'), + ] + public function testCacheInvalidationForCategoriesWithWidget(): void + { + $fixtures = DataFixtureStorageManager::getStorage(); + $block = $fixtures->get('block'); + $category1 = $fixtures->get('category1'); + $category2 = $fixtures->get('category2'); + $queryForCategory1 = $this->getCategoriesQuery((int) $category1->getId()); + $queryForCategory2 = $this->getCategoriesQuery((int) $category2->getId()); + + $this->updateCategoryDescription((int) $category1->getId(), $this->getBlockWidget((int) $block->getId())); + + $responseCacheIdForCategory1 = $this->getQueryResponseCacheKey($queryForCategory1); + // Verify we get MISS for category1 query in the first request + $responseMissForCategory1 = $this->assertCacheMissAndReturnResponse( + $queryForCategory1, + [CacheIdCalculator::CACHE_ID_HEADER => $responseCacheIdForCategory1] + ); + + // Verify we get HIT for category 1 query in the second request + $responseHitForCategory1 = $this->assertCacheHitAndReturnResponse( + $queryForCategory1, + [CacheIdCalculator::CACHE_ID_HEADER => $responseCacheIdForCategory1] + ); + + $this->assertEquals($responseMissForCategory1['body'], $responseHitForCategory1['body']); + + // Verify category1 description contains block content + $this->assertCategoryDescription('Original Block content', $responseHitForCategory1); + + $responseCacheIdForCategory2 = $this->getQueryResponseCacheKey($queryForCategory2); + // Verify we get MISS for category2 query in the first request + $responseMissForCategory2 = $this->assertCacheMissAndReturnResponse( + $queryForCategory2, + [CacheIdCalculator::CACHE_ID_HEADER => $responseCacheIdForCategory2] + ); + + // Verify we get HIT for category 2 query in the second request + $responseHitForCategory2 = $this->assertCacheHitAndReturnResponse( + $queryForCategory2, + [CacheIdCalculator::CACHE_ID_HEADER => $responseCacheIdForCategory2] + ); + + $this->assertEquals($responseMissForCategory2['body'], $responseHitForCategory2['body']); + + // Verify category2 description is the same as created + $this->assertCategoryDescription('Original Category description', $responseHitForCategory2); + + // Update block content + $newBlockContent = 'New block content!!!'; + $this->updateBlockContent((int) $block->getId(), $newBlockContent); + + // Verify we get MISS for category1 query after block is updated + $this->assertCacheMissAndReturnResponse( + $queryForCategory1, + [CacheIdCalculator::CACHE_ID_HEADER => $responseCacheIdForCategory1] + ); + + // Verify we get HIT for category1 query in the second request after block is updated + $responseHitForCategory1 = $this->assertCacheHitAndReturnResponse( + $queryForCategory1, + [CacheIdCalculator::CACHE_ID_HEADER => $responseCacheIdForCategory1] + ); + + // Verify we get HIT for category2 query after block is updated + $responseHitForCategory2 = $this->assertCacheHitAndReturnResponse( + $queryForCategory2, + [CacheIdCalculator::CACHE_ID_HEADER => $responseCacheIdForCategory2] + ); + + // Verify the updated block data is returned in category1 query response + $this->assertCategoryDescription($newBlockContent, $responseHitForCategory1); + + // Verify category2 description is the same as created + $this->assertCategoryDescription('Original Category description', $responseHitForCategory2); + } + + private function assertCategoryDescription(string $expected, array $response): void + { + $responseBody = $response['body']; + $this->assertIsArray($responseBody); + $this->assertArrayNotHasKey('errors', $responseBody); + $this->assertStringContainsString($expected, $responseBody['categories']['items'][0]['description']); + } + + private function getQueryResponseCacheKey(string $query): string + { + $response = $this->graphQlQueryWithResponseHeaders($query); + $this->assertArrayHasKey(CacheIdCalculator::CACHE_ID_HEADER, $response['headers']); + return $response['headers'][CacheIdCalculator::CACHE_ID_HEADER]; + } + + private function updateBlockContent(int $id, string $content): void + { + $blockRepository = Bootstrap::getObjectManager()->get(BlockRepository::class); + $block = $blockRepository->getById($id); + $block->setContent($content); + $blockRepository->save($block); + } + + private function updateCategoryDescription(int $id, string $description): void + { + $categoryRepository = Bootstrap::getObjectManager()->get(CategoryRepositoryInterface::class); + $category = $categoryRepository->get($id, Store::DEFAULT_STORE_ID); + $category->setCustomAttribute('description', $description); + $categoryRepository->save($category); + } + + private function getBlockWidget(int $blockId): string + { + return "{{widget type=\"Magento\\Cms\\Block\\Widget\\Block\" " . + "template=\"widget/static_block/default.phtml\" " . + "block_id=\"$blockId\" " . + "type_name=\"CMS Static Block\"}}"; + } + + private function getCategoriesQuery(int $categoryId): string + { + return << Date: Fri, 5 Jan 2024 13:57:55 +0200 Subject: [PATCH 07/24] ACP2E-2673: Price partial indexing performance - slow delete query is excluded from execution when its conditions result in 0 affected rows --- .../Model/Indexer/Product/Price/AbstractAction.php | 8 +++++++- .../Unit/Model/Indexer/Product/Price/Action/RowsTest.php | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Price/AbstractAction.php b/app/code/Magento/Catalog/Model/Indexer/Product/Price/AbstractAction.php index 219467033ecd..d064dffaefc6 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Price/AbstractAction.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Price/AbstractAction.php @@ -437,12 +437,18 @@ function ($type) use ($productsTypes) { */ private function deleteOutdatedData(array $entityIds, string $temporaryTable, string $mainTable): void { + $connection = $this->getConnection(); + $describe = $connection->describeTable($connection->getTableName($temporaryTable)); + if (false === $describe['entity_id']['NULLABLE']) { + return; + } + $joinCondition = [ 'tmp_table.entity_id = main_table.entity_id', 'tmp_table.customer_group_id = main_table.customer_group_id', 'tmp_table.website_id = main_table.website_id', ]; - $select = $this->getConnection()->select() + $select = $connection->select() ->from(['main_table' => $mainTable], null) ->joinLeft(['tmp_table' => $temporaryTable], implode(' AND ', $joinCondition), null) ->where('tmp_table.entity_id IS NULL') diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Price/Action/RowsTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Price/Action/RowsTest.php index 7a6a91028b51..1e832806e60c 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Price/Action/RowsTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Price/Action/RowsTest.php @@ -143,7 +143,7 @@ public function testBatchProcessing() $select->method('join')->willReturnSelf(); $adapter = $this->createMock(AdapterInterface::class); $adapter->method('select')->willReturn($select); - $adapter->method('describeTable')->willReturn([]); + $adapter->method('describeTable')->willReturn(['entity_id' => ['NULLABLE' => true]]); $this->defaultIndexerResource->method('getConnection')->willReturn($adapter); $adapter->method('fetchAll')->with($select)->willReturn([]); From bc2b81cb64bdb6d0c9545be13719dcfddb2b3690 Mon Sep 17 00:00:00 2001 From: aplapana Date: Fri, 5 Jan 2024 16:31:09 +0200 Subject: [PATCH 08/24] ACP2E-2673: Price partial indexing performance - added indexes for temporary indexer database table - temporary indexer table is now being dropped instead of deleting data --- .../Indexer/Product/Price/AbstractAction.php | 10 ++-------- .../Indexer/Product/Price/Action/RowsTest.php | 19 +++++++++++++++++-- app/code/Magento/Catalog/etc/db_schema.xml | 5 +++++ .../Catalog/etc/db_schema_whitelist.json | 3 ++- 4 files changed, 26 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Price/AbstractAction.php b/app/code/Magento/Catalog/Model/Indexer/Product/Price/AbstractAction.php index d064dffaefc6..96fbe5227d27 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Price/AbstractAction.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Price/AbstractAction.php @@ -408,11 +408,11 @@ function ($type) use ($productsTypes) { foreach ($this->dimensionCollectionFactory->create() as $dimensions) { $this->tableMaintainer->createMainTmpTable($dimensions); $temporaryTable = $this->tableMaintainer->getMainTmpTable($dimensions); - $this->_emptyTable($temporaryTable); $indexer->executeByDimensions($dimensions, \SplFixedArray::fromArray($entityIds, false)); $mainTable = $this->tableMaintainer->getMainTableByDimensions($dimensions); $this->_insertFromTable($temporaryTable, $mainTable); $this->deleteOutdatedData($entityIds, $temporaryTable, $mainTable); + $this->_connection->dropTable($temporaryTable); } } else { // handle 3d-party indexers for backward compatibility @@ -437,18 +437,12 @@ function ($type) use ($productsTypes) { */ private function deleteOutdatedData(array $entityIds, string $temporaryTable, string $mainTable): void { - $connection = $this->getConnection(); - $describe = $connection->describeTable($connection->getTableName($temporaryTable)); - if (false === $describe['entity_id']['NULLABLE']) { - return; - } - $joinCondition = [ 'tmp_table.entity_id = main_table.entity_id', 'tmp_table.customer_group_id = main_table.customer_group_id', 'tmp_table.website_id = main_table.website_id', ]; - $select = $connection->select() + $select = $this->getConnection()->select() ->from(['main_table' => $mainTable], null) ->joinLeft(['tmp_table' => $temporaryTable], implode(' AND ', $joinCondition), null) ->where('tmp_table.entity_id IS NULL') diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Price/Action/RowsTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Price/Action/RowsTest.php index 1e832806e60c..82b1af651229 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Price/Action/RowsTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Price/Action/RowsTest.php @@ -143,7 +143,8 @@ public function testBatchProcessing() $select->method('join')->willReturnSelf(); $adapter = $this->createMock(AdapterInterface::class); $adapter->method('select')->willReturn($select); - $adapter->method('describeTable')->willReturn(['entity_id' => ['NULLABLE' => true]]); + $adapter->method('describeTable')->willReturn([]); + $adapter->expects($this->exactly(4))->method('dropTable'); $this->defaultIndexerResource->method('getConnection')->willReturn($adapter); $adapter->method('fetchAll')->with($select)->willReturn([]); @@ -193,7 +194,21 @@ public function testBatchProcessing() ->method('getPrimaryKeyName') ->willReturn('entity_id'); - $this->actionRows->execute($ids); + $actionRows = new Rows( + $this->config, + $this->storeManager, + $this->currencyFactory, + $this->localeDate, + $this->dateTime, + $this->catalogProductType, + $this->indexerPriceFactory, + $this->defaultIndexerResource, + $this->tierPriceIndexResource, + $this->dimensionCollectionFactory, + $this->tableMaintainer, + 2 + ); + $actionRows->execute($ids); } public function testDeletedProductsBatchProcessing() diff --git a/app/code/Magento/Catalog/etc/db_schema.xml b/app/code/Magento/Catalog/etc/db_schema.xml index 36decbcff089..45df7e1a5e0e 100644 --- a/app/code/Magento/Catalog/etc/db_schema.xml +++ b/app/code/Magento/Catalog/etc/db_schema.xml @@ -1645,6 +1645,11 @@ + + + + + diff --git a/app/code/Magento/Catalog/etc/db_schema_whitelist.json b/app/code/Magento/Catalog/etc/db_schema_whitelist.json index fd332606bb22..efaf73e4633b 100644 --- a/app/code/Magento/Catalog/etc/db_schema_whitelist.json +++ b/app/code/Magento/Catalog/etc/db_schema_whitelist.json @@ -992,7 +992,8 @@ "index": { "CATALOG_PRODUCT_INDEX_PRICE_TMP_CUSTOMER_GROUP_ID": true, "CATALOG_PRODUCT_INDEX_PRICE_TMP_WEBSITE_ID": true, - "CATALOG_PRODUCT_INDEX_PRICE_TMP_MIN_PRICE": true + "CATALOG_PRODUCT_INDEX_PRICE_TMP_MIN_PRICE": true, + "CATALOG_PRODUCT_INDEX_ENTITY_CUSTOMER_WEBSITE": true }, "constraint": { "PRIMARY": true From 5bfa715ef8a79db54763cd93a1c755bfd75ab355 Mon Sep 17 00:00:00 2001 From: aplapana Date: Sun, 7 Jan 2024 11:35:26 +0200 Subject: [PATCH 09/24] ACP2E-2673: Price partial indexing performance - adjusted index naming --- app/code/Magento/Catalog/etc/db_schema.xml | 2 +- app/code/Magento/Catalog/etc/db_schema_whitelist.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/etc/db_schema.xml b/app/code/Magento/Catalog/etc/db_schema.xml index 45df7e1a5e0e..b564ffa3e652 100644 --- a/app/code/Magento/Catalog/etc/db_schema.xml +++ b/app/code/Magento/Catalog/etc/db_schema.xml @@ -1645,7 +1645,7 @@ - + diff --git a/app/code/Magento/Catalog/etc/db_schema_whitelist.json b/app/code/Magento/Catalog/etc/db_schema_whitelist.json index efaf73e4633b..4b6a247f5ff4 100644 --- a/app/code/Magento/Catalog/etc/db_schema_whitelist.json +++ b/app/code/Magento/Catalog/etc/db_schema_whitelist.json @@ -993,7 +993,7 @@ "CATALOG_PRODUCT_INDEX_PRICE_TMP_CUSTOMER_GROUP_ID": true, "CATALOG_PRODUCT_INDEX_PRICE_TMP_WEBSITE_ID": true, "CATALOG_PRODUCT_INDEX_PRICE_TMP_MIN_PRICE": true, - "CATALOG_PRODUCT_INDEX_ENTITY_CUSTOMER_WEBSITE": true + "CAT_PRD_IDX_PRICE_TMP_ENTT_ID_CSTR_GROUP_ID_WS_ID": true }, "constraint": { "PRIMARY": true From c7832d01ef69aef591773a04998dc808b73082f2 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Wed, 10 Jan 2024 10:30:53 -0600 Subject: [PATCH 10/24] ACP2E-2692: "Base table or view not found" error occurs when partial reindex --- .../Mview/View/ChangelogBatchWalker.php | 4 +- .../ChangelogBatchWalker/IdsTableBuilder.php | 1 - .../IdsTableBuilderTest.php | 86 +++++++++++++++++++ 3 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 lib/internal/Magento/Framework/Test/Unit/Mview/View/ChangelogBatchWalker/IdsTableBuilderTest.php diff --git a/lib/internal/Magento/Framework/Mview/View/ChangelogBatchWalker.php b/lib/internal/Magento/Framework/Mview/View/ChangelogBatchWalker.php index 61956b0cd3ae..097ac300217e 100644 --- a/lib/internal/Magento/Framework/Mview/View/ChangelogBatchWalker.php +++ b/lib/internal/Magento/Framework/Mview/View/ChangelogBatchWalker.php @@ -80,7 +80,7 @@ public function walk( $idsTable = $this->idsTableBuilder->build($changelog); try { - $connection->createTemporaryTable($idsTable); + $connection->createTable($idsTable); $columns = $this->getIdsColumns($idsTable); @@ -122,7 +122,7 @@ public function walk( yield $ids; } } finally { - $connection->dropTemporaryTable($idsTable->getName()); + $connection->dropTable($idsTable->getName()); } } diff --git a/lib/internal/Magento/Framework/Mview/View/ChangelogBatchWalker/IdsTableBuilder.php b/lib/internal/Magento/Framework/Mview/View/ChangelogBatchWalker/IdsTableBuilder.php index 69b1527fe661..fe0753144dca 100644 --- a/lib/internal/Magento/Framework/Mview/View/ChangelogBatchWalker/IdsTableBuilder.php +++ b/lib/internal/Magento/Framework/Mview/View/ChangelogBatchWalker/IdsTableBuilder.php @@ -50,7 +50,6 @@ public function build(ChangelogInterface $changelog): Table ['unsigned' => true, 'nullable' => false], 'Entity ID' ); - $table->setOption('type', 'memory'); $table->addIndex( self::INDEX_NAME_UNIQUE, [ diff --git a/lib/internal/Magento/Framework/Test/Unit/Mview/View/ChangelogBatchWalker/IdsTableBuilderTest.php b/lib/internal/Magento/Framework/Test/Unit/Mview/View/ChangelogBatchWalker/IdsTableBuilderTest.php new file mode 100644 index 000000000000..5933d7eba262 --- /dev/null +++ b/lib/internal/Magento/Framework/Test/Unit/Mview/View/ChangelogBatchWalker/IdsTableBuilderTest.php @@ -0,0 +1,86 @@ +changeLog = $this->getMockBuilder(ChangelogInterface::class) + ->getMockForAbstractClass(); + $this->connection = $this->getMockBuilder(AdapterInterface::class) + ->getMockForAbstractClass(); + $this->table = $this->getMockBuilder(Table::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->resourceConnection = $this->getMockBuilder(ResourceConnection::class) + ->disableOriginalConstructor() + ->getMock(); + $this->resourceConnection->expects($this->any()) + ->method('getConnection') + ->willReturn($this->connection); + $this->connection->expects($this->any()) + ->method('newTable') + ->willReturn($this->table); + + $this->model = new IdsTableBuilder($this->resourceConnection); + } + + public function testBuildDoNotCreateMemoryTable() : void + { + $this->table->expects($this->never()) + ->method('setOption') + ->with('type', 'memory'); + + $this->model->build($this->changeLog); + } +} From 5f6dfc20fb8b425130703770249892389ad638f3 Mon Sep 17 00:00:00 2001 From: aplapana Date: Fri, 12 Jan 2024 10:34:23 +0200 Subject: [PATCH 11/24] ACP2E-2673: Price partial indexing performance - test with truncate instead of drop --- .../Catalog/Model/Indexer/Product/Price/AbstractAction.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Price/AbstractAction.php b/app/code/Magento/Catalog/Model/Indexer/Product/Price/AbstractAction.php index 96fbe5227d27..c016b01f35c4 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Price/AbstractAction.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Price/AbstractAction.php @@ -412,7 +412,10 @@ function ($type) use ($productsTypes) { $mainTable = $this->tableMaintainer->getMainTableByDimensions($dimensions); $this->_insertFromTable($temporaryTable, $mainTable); $this->deleteOutdatedData($entityIds, $temporaryTable, $mainTable); - $this->_connection->dropTable($temporaryTable); + // phpcs:ignore Magento2.SQL.RawQuery.FoundRawSql + $this->getConnection()->query( + 'TRUNCATE TABLE ' . $this->getConnection()->quoteIdentifier($temporaryTable) + ); } } else { // handle 3d-party indexers for backward compatibility From 4f9a7c9e3769d1fb852c74c2d91c69f4d2f04eae Mon Sep 17 00:00:00 2001 From: aplapana Date: Fri, 12 Jan 2024 15:53:56 +0200 Subject: [PATCH 12/24] ACP2E-2673: Price partial indexing performance - reverted to delete statement for empty tables because of Galera cluster locks --- .../Indexer/Product/Price/AbstractAction.php | 5 +---- .../Indexer/Product/Price/Action/RowsTest.php | 17 +---------------- 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Price/AbstractAction.php b/app/code/Magento/Catalog/Model/Indexer/Product/Price/AbstractAction.php index c016b01f35c4..219467033ecd 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Price/AbstractAction.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Price/AbstractAction.php @@ -408,14 +408,11 @@ function ($type) use ($productsTypes) { foreach ($this->dimensionCollectionFactory->create() as $dimensions) { $this->tableMaintainer->createMainTmpTable($dimensions); $temporaryTable = $this->tableMaintainer->getMainTmpTable($dimensions); + $this->_emptyTable($temporaryTable); $indexer->executeByDimensions($dimensions, \SplFixedArray::fromArray($entityIds, false)); $mainTable = $this->tableMaintainer->getMainTableByDimensions($dimensions); $this->_insertFromTable($temporaryTable, $mainTable); $this->deleteOutdatedData($entityIds, $temporaryTable, $mainTable); - // phpcs:ignore Magento2.SQL.RawQuery.FoundRawSql - $this->getConnection()->query( - 'TRUNCATE TABLE ' . $this->getConnection()->quoteIdentifier($temporaryTable) - ); } } else { // handle 3d-party indexers for backward compatibility diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Price/Action/RowsTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Price/Action/RowsTest.php index 82b1af651229..7a6a91028b51 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Price/Action/RowsTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Price/Action/RowsTest.php @@ -144,7 +144,6 @@ public function testBatchProcessing() $adapter = $this->createMock(AdapterInterface::class); $adapter->method('select')->willReturn($select); $adapter->method('describeTable')->willReturn([]); - $adapter->expects($this->exactly(4))->method('dropTable'); $this->defaultIndexerResource->method('getConnection')->willReturn($adapter); $adapter->method('fetchAll')->with($select)->willReturn([]); @@ -194,21 +193,7 @@ public function testBatchProcessing() ->method('getPrimaryKeyName') ->willReturn('entity_id'); - $actionRows = new Rows( - $this->config, - $this->storeManager, - $this->currencyFactory, - $this->localeDate, - $this->dateTime, - $this->catalogProductType, - $this->indexerPriceFactory, - $this->defaultIndexerResource, - $this->tierPriceIndexResource, - $this->dimensionCollectionFactory, - $this->tableMaintainer, - 2 - ); - $actionRows->execute($ids); + $this->actionRows->execute($ids); } public function testDeletedProductsBatchProcessing() From cc260f886d82f9ecbf08166cc9a764c0d41e52fc Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Fri, 12 Jan 2024 15:25:15 -0600 Subject: [PATCH 13/24] ACP2E-2692: "Base table or view not found" error occurs when partial reindex --- .../Mview/View/ChangelogBatchWalker.php | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/lib/internal/Magento/Framework/Mview/View/ChangelogBatchWalker.php b/lib/internal/Magento/Framework/Mview/View/ChangelogBatchWalker.php index 097ac300217e..6f31a393444f 100644 --- a/lib/internal/Magento/Framework/Mview/View/ChangelogBatchWalker.php +++ b/lib/internal/Magento/Framework/Mview/View/ChangelogBatchWalker.php @@ -24,30 +24,34 @@ class ChangelogBatchWalker implements ChangelogBatchWalkerInterface { /** - * @var \Magento\Framework\App\ResourceConnection + * @var ResourceConnection */ private ResourceConnection $resourceConnection; + /** - * @var \Magento\Framework\DB\Query\Generator + * @var Generator */ private Generator $generator; + /** - * @var \Magento\Framework\Mview\View\ChangelogBatchWalker\IdsTableBuilderInterface + * @var IdsTableBuilderInterface */ private IdsTableBuilderInterface $idsTableBuilder; + /** - * @var \Magento\Framework\Mview\View\ChangelogBatchWalker\IdsSelectBuilderInterface + * @var IdsSelectBuilderInterface */ private IdsSelectBuilderInterface $idsSelectBuilder; + /** - * @var \Magento\Framework\Mview\View\ChangelogBatchWalker\IdsFetcherInterface + * @var IdsFetcherInterface */ private IdsFetcherInterface $idsFetcher; /** * @param ResourceConnection $resourceConnection - * @param \Magento\Framework\DB\Query\Generator $generator - * @param \Magento\Framework\Mview\View\ChangelogBatchWalker\IdsContext $idsContext + * @param Generator $generator + * @param IdsContext $idsContext */ public function __construct( ResourceConnection $resourceConnection, @@ -70,9 +74,11 @@ public function walk( int $lastVersionId, int $batchSize ): iterable { + echo '0'; $connection = $this->resourceConnection->getConnection(); $changelogTableName = $this->resourceConnection->getTableName($changelog->getName()); + if (!$connection->isTableExists($changelogTableName)) { throw new ChangelogTableNotExistsException(new Phrase("Table %1 does not exist", [$changelogTableName])); } From 045267ca57c5db08622d23ed02c77897b28f18b2 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Fri, 12 Jan 2024 15:30:00 -0600 Subject: [PATCH 14/24] ACP2E-2692: "Base table or view not found" error occurs when partial reindex --- .../View/ChangelogBatchWalker/IdsTableBuilderTest.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/internal/Magento/Framework/Test/Unit/Mview/View/ChangelogBatchWalker/IdsTableBuilderTest.php b/lib/internal/Magento/Framework/Test/Unit/Mview/View/ChangelogBatchWalker/IdsTableBuilderTest.php index 5933d7eba262..a46cab3dc2cc 100644 --- a/lib/internal/Magento/Framework/Test/Unit/Mview/View/ChangelogBatchWalker/IdsTableBuilderTest.php +++ b/lib/internal/Magento/Framework/Test/Unit/Mview/View/ChangelogBatchWalker/IdsTableBuilderTest.php @@ -23,27 +23,28 @@ use Magento\Framework\DB\Ddl\Table; use Magento\Framework\Mview\View\ChangelogBatchWalker\IdsTableBuilder; use Magento\Framework\Mview\View\ChangelogInterface; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; class IdsTableBuilderTest extends TestCase { /** - * @var ResourceConnection + * @var ResourceConnection|MockObject */ private $resourceConnection; /** - * @var ChangelogInterface + * @var ChangelogInterface|MockObject */ private $changeLog; /** - * @var AdapterInterface + * @var AdapterInterface|MockObject */ private $connection; /** - * @var Table + * @var Table|MockObject */ private $table; From c64c473b4e7b82ab4826bcfc7068f9d5a1276aa9 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Fri, 12 Jan 2024 16:07:39 -0600 Subject: [PATCH 15/24] ACP2E-2692: "Base table or view not found" error occurs when partial reindex --- .../Unit/View/ChangelogBatchWalkerTest.php | 174 ++++++++++++++++++ .../Mview/View/ChangelogBatchWalker.php | 2 - 2 files changed, 174 insertions(+), 2 deletions(-) create mode 100644 lib/internal/Magento/Framework/Mview/Test/Unit/View/ChangelogBatchWalkerTest.php diff --git a/lib/internal/Magento/Framework/Mview/Test/Unit/View/ChangelogBatchWalkerTest.php b/lib/internal/Magento/Framework/Mview/Test/Unit/View/ChangelogBatchWalkerTest.php new file mode 100644 index 000000000000..afbe2cf33e05 --- /dev/null +++ b/lib/internal/Magento/Framework/Mview/Test/Unit/View/ChangelogBatchWalkerTest.php @@ -0,0 +1,174 @@ +resourceConnection = $this->getMockBuilder(ResourceConnection::class) + ->disableOriginalConstructor() + ->getMock(); + $this->generator = $this->getMockBuilder(Generator::class) + ->disableOriginalConstructor() + ->getMock(); + $this->idsTableBuilder = $this->getMockBuilder(IdsTableBuilderInterface::class) + ->getMockForAbstractClass(); + $this->idsSelectBuilder = $this->getMockBuilder(IdsSelectBuilderInterface::class) + ->getMockForAbstractClass(); + $this->idsContext = $this->getMockBuilder(IdsContext::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->idsContext->expects($this->any()) + ->method('getSelectBuilder') + ->willReturn($this->idsSelectBuilder); + $this->idsContext->expects($this->any()) + ->method('getTableBuilder') + ->willReturn($this->idsTableBuilder); + + $this->changeLog = $this->getMockBuilder(ChangelogInterface::class) + ->getMockForAbstractClass(); + $this->connection = $this->getMockBuilder(AdapterInterface::class) + ->getMockForAbstractClass(); + $this->table = $this->getMockBuilder(Table::class) + ->disableOriginalConstructor() + ->getMock(); + $this->resourceConnection->expects($this->any()) + ->method('getConnection') + ->willReturn($this->connection); + + $this->select = $this->getMockBuilder(Select::class) + ->disableOriginalConstructor() + ->getMock(); + $this->select->expects($this->any()) + ->method('from') + ->willReturnSelf(); + $this->select->expects($this->any()) + ->method('where') + ->willReturnSelf(); + $this->select->expects($this->any()) + ->method('distinct') + ->willReturnSelf(); + $this->connection->expects($this->any()) + ->method('select') + ->willReturn($this->select); + + $this->model = new ChangelogBatchWalker( + $this->resourceConnection, + $this->generator, + $this->idsContext + ); + } + + public function testNoTemporaryTablesUsed() + { + $this->connection->expects($this->once()) + ->method('isTableExists') + ->willReturn(true); + $this->table->expects($this->any()) + ->method('getColumns') + ->willReturn([]); + $this->idsTableBuilder->expects($this->any()) + ->method('build') + ->willReturn($this->table); + $this->idsSelectBuilder->expects($this->any()) + ->method('build') + ->willReturn($this->select); + $this->generator->expects($this->any()) + ->method('generate') + ->willReturn([]); + + foreach ($this->model->walk($this->changeLog, 1, 2, 1) as $iteration) { + $this->assertEmpty($iteration); + $this->connection->expects($this->once()) + ->method('createTable'); + $this->connection->expects($this->never()) + ->method('createTemporaryTableTable'); + } + } +} diff --git a/lib/internal/Magento/Framework/Mview/View/ChangelogBatchWalker.php b/lib/internal/Magento/Framework/Mview/View/ChangelogBatchWalker.php index 6f31a393444f..474c687a1381 100644 --- a/lib/internal/Magento/Framework/Mview/View/ChangelogBatchWalker.php +++ b/lib/internal/Magento/Framework/Mview/View/ChangelogBatchWalker.php @@ -74,11 +74,9 @@ public function walk( int $lastVersionId, int $batchSize ): iterable { - echo '0'; $connection = $this->resourceConnection->getConnection(); $changelogTableName = $this->resourceConnection->getTableName($changelog->getName()); - if (!$connection->isTableExists($changelogTableName)) { throw new ChangelogTableNotExistsException(new Phrase("Table %1 does not exist", [$changelogTableName])); } From 404168107cbd234b2f453de532b27400b3d3760a Mon Sep 17 00:00:00 2001 From: aplapana Date: Thu, 25 Jan 2024 11:26:52 +0200 Subject: [PATCH 16/24] ACP2E-2704: Getting Unable to send the cookie. Size of 'mage-messages' while trying to Reorder --- .../Sales/Controller/AbstractController/Reorder.php | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/app/code/Magento/Sales/Controller/AbstractController/Reorder.php b/app/code/Magento/Sales/Controller/AbstractController/Reorder.php index 5eb485e26219..e4dc6b00204d 100644 --- a/app/code/Magento/Sales/Controller/AbstractController/Reorder.php +++ b/app/code/Magento/Sales/Controller/AbstractController/Reorder.php @@ -94,16 +94,6 @@ public function execute() // to session for guest customer, as it does \Magento\Checkout\Model\Cart::save which is deprecated. $this->checkoutSession->setQuoteId($reorderOutput->getCart()->getId()); - $errors = $reorderOutput->getErrors(); - if (!empty($errors)) { - $useNotice = $this->_objectManager->get(\Magento\Checkout\Model\Session::class)->getUseNotice(true); - foreach ($errors as $error) { - $useNotice - ? $this->messageManager->addNoticeMessage($error->getMessage()) - : $this->messageManager->addErrorMessage($error->getMessage()); - } - } - return $resultRedirect->setPath('checkout/cart'); } } From 9dbe14e517c6d9c3df911e6ebdfe4deee346dd32 Mon Sep 17 00:00:00 2001 From: aplapana Date: Thu, 25 Jan 2024 15:26:37 +0200 Subject: [PATCH 17/24] ACP2E-2704: Getting Unable to send the cookie. Size of 'mage-messages' while trying to Reorder --- .../Magento/Sales/Controller/Order/ReorderTest.php | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Order/ReorderTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Order/ReorderTest.php index 6a508e9a95ee..6e2015c5a241 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Order/ReorderTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Order/ReorderTest.php @@ -108,13 +108,7 @@ public function testReorderProductLowQty(): void $order = $this->orderFactory->create()->loadByIncrementId('55555555'); $this->customerSession->setCustomerId($order->getCustomerId()); $this->dispatchReorderRequest((int)$order->getId()); - $origMessage = (string)__('The requested qty is not available'); - $message = $this->escaper->escapeHtml( - __('Could not add the product with SKU "%1" to the shopping cart: %2', 'simple-1', $origMessage) - ); - $constraint = $this->logicalOr($this->containsEqual($origMessage), $this->containsEqual($message)); - $this->assertThat($this->getMessages(MessageInterface::TYPE_ERROR), $constraint); - $this->quote = $this->checkoutSession->getQuote(); + $this->assertRedirect($this->stringContains('checkout/cart')); } /** From 410db1c640af1f7d27e8bba7a236a35fed36339f Mon Sep 17 00:00:00 2001 From: aplapana Date: Tue, 30 Jan 2024 14:03:13 +0200 Subject: [PATCH 18/24] ACP2E-2704: Getting Unable to send the cookie. Size of 'mage-messages' while trying to Reorder --- app/code/Magento/Quote/Model/Quote.php | 4 +- .../Quote/Test/Unit/Model/QuoteTest.php | 39 +++++++++++++++++-- .../Magento/Sales/Model/Reorder/Reorder.php | 1 + 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Quote/Model/Quote.php b/app/code/Magento/Quote/Model/Quote.php index 7270734e3ff4..617849463bbc 100644 --- a/app/code/Magento/Quote/Model/Quote.php +++ b/app/code/Magento/Quote/Model/Quote.php @@ -1699,7 +1699,9 @@ public function addProduct( // collect errors instead of throwing first one if ($item->getHasError()) { - $this->deleteItem($item); + if (!$request->getForceAddToCart()) { + $this->deleteItem($item); + } foreach ($item->getMessage(false) as $message) { if (!in_array($message, $errors)) { // filter duplicate messages diff --git a/app/code/Magento/Quote/Test/Unit/Model/QuoteTest.php b/app/code/Magento/Quote/Test/Unit/Model/QuoteTest.php index b7571da30b9b..4b8a9f31c4ea 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/QuoteTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/QuoteTest.php @@ -30,6 +30,7 @@ use Magento\Framework\DataObject\Copy; use Magento\Framework\DataObject\Factory; use Magento\Framework\Event\Manager; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Model\Context; use Magento\Framework\Phrase; @@ -1072,17 +1073,28 @@ public function testAddProductItemPreparation(): void } /** + * @param $request + * @param $hasError * @return void + * @throws LocalizedException + * @dataProvider dataProviderForTestAddProductItem */ - public function testAddProductItemNew(): void + public function testAddProductItemNew($request, $hasError): void { - $itemMock = $this->createMock(Item::class); + $itemMock = $this->getMockBuilder(Item::class) + ->disableOriginalConstructor() + ->addMethods(['getHasError']) + ->onlyMethods(['representProduct', 'setProduct', 'setOptions', 'setQuote', 'getProduct']) + ->getMock(); + $itemMock->expects($this->once())->method('getHasError')->willReturn($hasError); + $product = $this->createMock(Product::class); + $itemMock->expects($this->any())->method('getProduct')->willReturn($product); $expectedResult = $itemMock; $requestMock = $this->createMock( DataObject::class ); - $this->objectFactoryMock->expects($this->once()) + $this->objectFactoryMock->expects($this->any()) ->method('create') ->with(['qty' => 1]) ->willReturn($requestMock); @@ -1145,10 +1157,29 @@ public function testAddProductItemNew(): void ->method('getTypeInstance') ->willReturn($typeInstanceMock); - $result = $this->quote->addProduct($this->productMock, null); + $result = $this->quote->addProduct($this->productMock, $request); $this->assertEquals($expectedResult, $result); } + /** + * @return array[] + */ + private function dataProviderForTestAddProductItem(): array + { + return [ + 'not_force_item' => [null, false], + 'force_item' => [ + new DataObject( + [ + 'force_add_to_cart' => true, + 'qty' => 1 + ] + ), + true + ] + ]; + } + /** * @return void */ diff --git a/app/code/Magento/Sales/Model/Reorder/Reorder.php b/app/code/Magento/Sales/Model/Reorder/Reorder.php index cbf281ab47d7..754d54784126 100644 --- a/app/code/Magento/Sales/Model/Reorder/Reorder.php +++ b/app/code/Magento/Sales/Model/Reorder/Reorder.php @@ -264,6 +264,7 @@ private function addItemToCart(OrderItemInterface $orderItem, Quote $cart, Produ $addProductResult = null; try { + $infoBuyRequest->setForceAddToCart(true); $addProductResult = $cart->addProduct($product, $infoBuyRequest); } catch (\Magento\Framework\Exception\LocalizedException $e) { $this->addError($this->getCartItemErrorMessage($orderItem, $product, $e->getMessage())); From 89d5fa34b5851a69198351353f849880bd9c17f4 Mon Sep 17 00:00:00 2001 From: aplapana Date: Wed, 31 Jan 2024 10:29:08 +0200 Subject: [PATCH 19/24] ACP2E-2673: Price partial indexing performance - added integration test for table index check --- .../Magento/Catalog/DbSchemaTest.php | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/DbSchemaTest.php diff --git a/dev/tests/integration/testsuite/Magento/Catalog/DbSchemaTest.php b/dev/tests/integration/testsuite/Magento/Catalog/DbSchemaTest.php new file mode 100644 index 000000000000..ce667d12a3b8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/DbSchemaTest.php @@ -0,0 +1,62 @@ +get(ResourceConnection::class)->getConnection(); + $indexes = $connection->getIndexList($tableName); + $this->assertArrayHasKey($indexName, $indexes); + $this->assertSame($columns, $indexes[$indexName]['COLUMNS_LIST']); + $this->assertSame($indexType, $indexes[$indexName]['INDEX_TYPE']); + } + + /** + * @return array[] + */ + public function indexDataProvider(): array + { + return [ + [ + 'catalog_product_index_price_tmp', + 'CAT_PRD_IDX_PRICE_TMP_ENTT_ID_CSTR_GROUP_ID_WS_ID', + ['entity_id', 'customer_group_id', 'website_id'] + ] + ]; + } +} From 2a7960b95e0c43325912057741e97b39683f51d5 Mon Sep 17 00:00:00 2001 From: aplapana Date: Wed, 31 Jan 2024 11:27:58 +0200 Subject: [PATCH 20/24] ACP2E-2704: Getting Unable to send the cookie. Size of 'mage-messages' while trying to Reorder --- .../Quote/Test/Unit/Model/QuoteTest.php | 2 +- .../Magento/Sales/Model/Reorder/Reorder.php | 12 +++++++-- app/code/Magento/Sales/etc/di.xml | 5 ++++ app/code/Magento/Sales/etc/graphql/di.xml | 25 +++++++++++++++++++ app/code/Magento/Sales/etc/webapi_rest/di.xml | 5 ++++ app/code/Magento/Sales/etc/webapi_soap/di.xml | 5 ++++ 6 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 app/code/Magento/Sales/etc/graphql/di.xml diff --git a/app/code/Magento/Quote/Test/Unit/Model/QuoteTest.php b/app/code/Magento/Quote/Test/Unit/Model/QuoteTest.php index 4b8a9f31c4ea..0a75ca2c3e3f 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/QuoteTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/QuoteTest.php @@ -1164,7 +1164,7 @@ public function testAddProductItemNew($request, $hasError): void /** * @return array[] */ - private function dataProviderForTestAddProductItem(): array + public function dataProviderForTestAddProductItem(): array { return [ 'not_force_item' => [null, false], diff --git a/app/code/Magento/Sales/Model/Reorder/Reorder.php b/app/code/Magento/Sales/Model/Reorder/Reorder.php index 754d54784126..8dee96830b4e 100644 --- a/app/code/Magento/Sales/Model/Reorder/Reorder.php +++ b/app/code/Magento/Sales/Model/Reorder/Reorder.php @@ -104,6 +104,11 @@ class Reorder */ private $orderInfoBuyRequestGetter; + /** + * @var bool + */ + private bool $forceAdd; + /** * @param OrderFactory $orderFactory * @param CustomerCartResolver $customerCartProvider @@ -113,6 +118,7 @@ class Reorder * @param LoggerInterface $logger * @param ProductCollectionFactory $productCollectionFactory * @param OrderInfoBuyRequestGetter $orderInfoBuyRequestGetter + * @param bool $forceAdd */ public function __construct( OrderFactory $orderFactory, @@ -122,7 +128,8 @@ public function __construct( ReorderHelper $reorderHelper, LoggerInterface $logger, ProductCollectionFactory $productCollectionFactory, - OrderInfoBuyRequestGetter $orderInfoBuyRequestGetter + OrderInfoBuyRequestGetter $orderInfoBuyRequestGetter, + bool $forceAdd = false ) { $this->orderFactory = $orderFactory; $this->cartRepository = $cartRepository; @@ -132,6 +139,7 @@ public function __construct( $this->guestCartResolver = $guestCartResolver; $this->productCollectionFactory = $productCollectionFactory; $this->orderInfoBuyRequestGetter = $orderInfoBuyRequestGetter; + $this->forceAdd = $forceAdd; } /** @@ -264,7 +272,7 @@ private function addItemToCart(OrderItemInterface $orderItem, Quote $cart, Produ $addProductResult = null; try { - $infoBuyRequest->setForceAddToCart(true); + $infoBuyRequest->setForceAddToCart($this->forceAdd); $addProductResult = $cart->addProduct($product, $infoBuyRequest); } catch (\Magento\Framework\Exception\LocalizedException $e) { $this->addError($this->getCartItemErrorMessage($orderItem, $product, $e->getMessage())); diff --git a/app/code/Magento/Sales/etc/di.xml b/app/code/Magento/Sales/etc/di.xml index d5dc1938bdab..32576f5bc370 100644 --- a/app/code/Magento/Sales/etc/di.xml +++ b/app/code/Magento/Sales/etc/di.xml @@ -1038,4 +1038,9 @@ + + + true + + diff --git a/app/code/Magento/Sales/etc/graphql/di.xml b/app/code/Magento/Sales/etc/graphql/di.xml new file mode 100644 index 000000000000..6df956b25ef5 --- /dev/null +++ b/app/code/Magento/Sales/etc/graphql/di.xml @@ -0,0 +1,25 @@ + + + + + + false + + + diff --git a/app/code/Magento/Sales/etc/webapi_rest/di.xml b/app/code/Magento/Sales/etc/webapi_rest/di.xml index 12ad410279a0..fc411ca1f975 100644 --- a/app/code/Magento/Sales/etc/webapi_rest/di.xml +++ b/app/code/Magento/Sales/etc/webapi_rest/di.xml @@ -22,4 +22,9 @@ + + + false + + diff --git a/app/code/Magento/Sales/etc/webapi_soap/di.xml b/app/code/Magento/Sales/etc/webapi_soap/di.xml index 12ad410279a0..fc411ca1f975 100644 --- a/app/code/Magento/Sales/etc/webapi_soap/di.xml +++ b/app/code/Magento/Sales/etc/webapi_soap/di.xml @@ -22,4 +22,9 @@ + + + false + + From 5d4374307f57e678fab84654bdca7f8524425a40 Mon Sep 17 00:00:00 2001 From: arnsaha Date: Wed, 31 Jan 2024 11:54:11 -0600 Subject: [PATCH 21/24] ACP2E-2757: Products not showing on category and search but direct links are working - Adding price_mapping regular expression --- .../OpenSearch/Model/Adapter/DynamicTemplates/PriceMapper.php | 3 ++- app/code/Magento/OpenSearch/Test/Unit/Model/OpenSearchTest.php | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/OpenSearch/Model/Adapter/DynamicTemplates/PriceMapper.php b/app/code/Magento/OpenSearch/Model/Adapter/DynamicTemplates/PriceMapper.php index 03ced99cc632..545d8b95c006 100644 --- a/app/code/Magento/OpenSearch/Model/Adapter/DynamicTemplates/PriceMapper.php +++ b/app/code/Magento/OpenSearch/Model/Adapter/DynamicTemplates/PriceMapper.php @@ -19,7 +19,8 @@ public function processTemplates(array $templates): array { $templates[] = [ 'price_mapping' => [ - 'match' => 'price_*', + "match_pattern" => "regex", + 'match' => 'price_\\d+_\\d+', 'match_mapping_type' => 'string', 'mapping' => [ 'type' => 'double', diff --git a/app/code/Magento/OpenSearch/Test/Unit/Model/OpenSearchTest.php b/app/code/Magento/OpenSearch/Test/Unit/Model/OpenSearchTest.php index 5fa55dcab9ca..551b4ee98275 100644 --- a/app/code/Magento/OpenSearch/Test/Unit/Model/OpenSearchTest.php +++ b/app/code/Magento/OpenSearch/Test/Unit/Model/OpenSearchTest.php @@ -147,7 +147,8 @@ public function testAddFieldsMapping() 'dynamic_templates' => [ [ 'price_mapping' => [ - 'match' => 'price_*', + "match_pattern" => "regex", + 'match' => 'price_\\d+_\\d+', 'match_mapping_type' => 'string', 'mapping' => [ 'type' => 'double', From b9b23b94be839ff1036b71b306053ceef099ec35 Mon Sep 17 00:00:00 2001 From: aplapana Date: Wed, 7 Feb 2024 10:25:35 +0200 Subject: [PATCH 22/24] ACP2E-2704: Getting Unable to send the cookie. Size of 'mage-messages' while trying to Reorder --- app/code/Magento/Quote/Model/Quote.php | 32 ++++++++++++++----- .../Magento/Sales/Model/Reorder/Reorder.php | 10 +++--- app/code/Magento/Sales/etc/di.xml | 5 --- app/code/Magento/Sales/etc/frontend/di.xml | 5 +++ app/code/Magento/Sales/etc/graphql/di.xml | 25 --------------- app/code/Magento/Sales/etc/webapi_rest/di.xml | 5 --- app/code/Magento/Sales/etc/webapi_soap/di.xml | 5 --- 7 files changed, 34 insertions(+), 53 deletions(-) delete mode 100644 app/code/Magento/Sales/etc/graphql/di.xml diff --git a/app/code/Magento/Quote/Model/Quote.php b/app/code/Magento/Quote/Model/Quote.php index 617849463bbc..380d2d7ca1c6 100644 --- a/app/code/Magento/Quote/Model/Quote.php +++ b/app/code/Magento/Quote/Model/Quote.php @@ -12,6 +12,7 @@ use Magento\Framework\Api\AttributeValueFactory; use Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface; use Magento\Framework\App\ObjectManager; +use Magento\Framework\DataObject; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Model\AbstractExtensibleModel; use Magento\Quote\Api\Data\PaymentInterface; @@ -1619,7 +1620,7 @@ public function addItem(\Magento\Quote\Model\Quote\Item $item) * Add product. Returns error message if product type instance can't prepare product. * * @param mixed $product - * @param null|float|\Magento\Framework\DataObject $request + * @param null|float|DataObject $request * @param null|string $processMode * @return \Magento\Quote\Model\Quote\Item|string * @throws \Magento\Framework\Exception\LocalizedException @@ -1637,11 +1638,12 @@ public function addProduct( if (is_numeric($request)) { $request = $this->objectFactory->create(['qty' => $request]); } - if (!$request instanceof \Magento\Framework\DataObject) { + if (!$request instanceof DataObject) { throw new \Magento\Framework\Exception\LocalizedException( __('We found an invalid request for adding product to quote.') ); } + $invalidProductAddFlag = $this->checkForInvalidProductAdd($request); if (!$product->isSalable()) { throw new \Magento\Framework\Exception\LocalizedException( @@ -1699,7 +1701,7 @@ public function addProduct( // collect errors instead of throwing first one if ($item->getHasError()) { - if (!$request->getForceAddToCart()) { + if (!$invalidProductAddFlag) { $this->deleteItem($item); } foreach ($item->getMessage(false) as $message) { @@ -1719,6 +1721,20 @@ public function addProduct( return $parentItem; } + /** + * Checks if invalid products should be added to quote + * + * @param DataObject $request + * @return bool + */ + private function checkForInvalidProductAdd(DataObject $request): bool + { + $forceAdd = $request->getAddToCartInvalidProduct(); + $request->unsetData('add_to_cart_invalid_product'); + + return (bool) $forceAdd; + } + /** * Adding catalog product object data to quote * @@ -1774,8 +1790,8 @@ protected function _addCatalogProduct(\Magento\Catalog\Model\Product $product, $ * For more options see \Magento\Catalog\Helper\Product->addParamsToBuyRequest() * * @param int $itemId - * @param \Magento\Framework\DataObject $buyRequest - * @param null|array|\Magento\Framework\DataObject $params + * @param DataObject $buyRequest + * @param null|array|DataObject $params * @return \Magento\Quote\Model\Quote\Item * @throws \Magento\Framework\Exception\LocalizedException * @@ -1797,9 +1813,9 @@ public function updateItem($itemId, $buyRequest, $params = null) $product = clone $this->productRepository->getById($productId, false, $this->getStore()->getId()); if (!$params) { - $params = new \Magento\Framework\DataObject(); + $params = new DataObject(); } elseif (is_array($params)) { - $params = new \Magento\Framework\DataObject($params); + $params = new DataObject($params); } $params->setCurrentConfig($item->getBuyRequest()); $buyRequest = $this->_catalogProduct->addParamsToBuyRequest($buyRequest, $params); @@ -2148,7 +2164,7 @@ protected function _clearErrorInfo() * @param string|null $origin Usually a name of module, that embeds error * @param int|null $code Error code, unique for origin, that sets it * @param string|null $message Error message - * @param \Magento\Framework\DataObject|null $additionalData Any additional data, that caller would like to store + * @param DataObject|null $additionalData Any additional data, that caller would like to store * @return $this */ public function addErrorInfo( diff --git a/app/code/Magento/Sales/Model/Reorder/Reorder.php b/app/code/Magento/Sales/Model/Reorder/Reorder.php index 8dee96830b4e..099e5c791ba7 100644 --- a/app/code/Magento/Sales/Model/Reorder/Reorder.php +++ b/app/code/Magento/Sales/Model/Reorder/Reorder.php @@ -107,7 +107,7 @@ class Reorder /** * @var bool */ - private bool $forceAdd; + private bool $addToCartInvalidProduct; /** * @param OrderFactory $orderFactory @@ -118,7 +118,7 @@ class Reorder * @param LoggerInterface $logger * @param ProductCollectionFactory $productCollectionFactory * @param OrderInfoBuyRequestGetter $orderInfoBuyRequestGetter - * @param bool $forceAdd + * @param bool $addToCartInvalidProduct */ public function __construct( OrderFactory $orderFactory, @@ -129,7 +129,7 @@ public function __construct( LoggerInterface $logger, ProductCollectionFactory $productCollectionFactory, OrderInfoBuyRequestGetter $orderInfoBuyRequestGetter, - bool $forceAdd = false + bool $addToCartInvalidProduct = false ) { $this->orderFactory = $orderFactory; $this->cartRepository = $cartRepository; @@ -139,7 +139,7 @@ public function __construct( $this->guestCartResolver = $guestCartResolver; $this->productCollectionFactory = $productCollectionFactory; $this->orderInfoBuyRequestGetter = $orderInfoBuyRequestGetter; - $this->forceAdd = $forceAdd; + $this->addToCartInvalidProduct = $addToCartInvalidProduct; } /** @@ -272,7 +272,7 @@ private function addItemToCart(OrderItemInterface $orderItem, Quote $cart, Produ $addProductResult = null; try { - $infoBuyRequest->setForceAddToCart($this->forceAdd); + $infoBuyRequest->setAddToCartInvalidProduct($this->addToCartInvalidProduct); $addProductResult = $cart->addProduct($product, $infoBuyRequest); } catch (\Magento\Framework\Exception\LocalizedException $e) { $this->addError($this->getCartItemErrorMessage($orderItem, $product, $e->getMessage())); diff --git a/app/code/Magento/Sales/etc/di.xml b/app/code/Magento/Sales/etc/di.xml index 32576f5bc370..d5dc1938bdab 100644 --- a/app/code/Magento/Sales/etc/di.xml +++ b/app/code/Magento/Sales/etc/di.xml @@ -1038,9 +1038,4 @@ - - - true - - diff --git a/app/code/Magento/Sales/etc/frontend/di.xml b/app/code/Magento/Sales/etc/frontend/di.xml index b2d4a7d78a6e..695cf037d776 100644 --- a/app/code/Magento/Sales/etc/frontend/di.xml +++ b/app/code/Magento/Sales/etc/frontend/di.xml @@ -22,4 +22,9 @@ + + + true + + diff --git a/app/code/Magento/Sales/etc/graphql/di.xml b/app/code/Magento/Sales/etc/graphql/di.xml deleted file mode 100644 index 6df956b25ef5..000000000000 --- a/app/code/Magento/Sales/etc/graphql/di.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - false - - - diff --git a/app/code/Magento/Sales/etc/webapi_rest/di.xml b/app/code/Magento/Sales/etc/webapi_rest/di.xml index fc411ca1f975..12ad410279a0 100644 --- a/app/code/Magento/Sales/etc/webapi_rest/di.xml +++ b/app/code/Magento/Sales/etc/webapi_rest/di.xml @@ -22,9 +22,4 @@ - - - false - - diff --git a/app/code/Magento/Sales/etc/webapi_soap/di.xml b/app/code/Magento/Sales/etc/webapi_soap/di.xml index fc411ca1f975..12ad410279a0 100644 --- a/app/code/Magento/Sales/etc/webapi_soap/di.xml +++ b/app/code/Magento/Sales/etc/webapi_soap/di.xml @@ -22,9 +22,4 @@ - - - false - - From 60163c396a83ae589055330714cadfaf0ac44b84 Mon Sep 17 00:00:00 2001 From: aplapana Date: Wed, 7 Feb 2024 14:04:52 +0200 Subject: [PATCH 23/24] ACP2E-2704: Getting Unable to send the cookie. Size of 'mage-messages' while trying to Reorder --- app/code/Magento/Quote/Test/Unit/Model/QuoteTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Quote/Test/Unit/Model/QuoteTest.php b/app/code/Magento/Quote/Test/Unit/Model/QuoteTest.php index 0a75ca2c3e3f..4698ab56dfbf 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/QuoteTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/QuoteTest.php @@ -1167,11 +1167,11 @@ public function testAddProductItemNew($request, $hasError): void public function dataProviderForTestAddProductItem(): array { return [ - 'not_force_item' => [null, false], - 'force_item' => [ + 'not_invalid_product_add' => [null, false], + 'invalid_product_add' => [ new DataObject( [ - 'force_add_to_cart' => true, + 'add_to_cart_invalid_product' => true, 'qty' => 1 ] ), From 1b188ee0144d3c2e9652a00dcf4635a8fbfe4eeb Mon Sep 17 00:00:00 2001 From: Alexandru Plapana Date: Mon, 11 Mar 2024 13:04:09 +0200 Subject: [PATCH 24/24] ACP2E-2704: Getting Unable to send the cookie. Size of 'mage-messages' while trying to Reorder --- app/code/Magento/Sales/Model/Reorder/Reorder.php | 2 ++ .../Sales/Controller/Order/ReorderWithDifferentStoreTest.php | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Sales/Model/Reorder/Reorder.php b/app/code/Magento/Sales/Model/Reorder/Reorder.php index f298a8bc12e5..27c2e280848a 100644 --- a/app/code/Magento/Sales/Model/Reorder/Reorder.php +++ b/app/code/Magento/Sales/Model/Reorder/Reorder.php @@ -127,6 +127,8 @@ class Reorder * @param OrderInfoBuyRequestGetter $orderInfoBuyRequestGetter * @param StoreManagerInterface|null $storeManager * @param bool $addToCartInvalidProduct + * + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( OrderFactory $orderFactory, diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Order/ReorderWithDifferentStoreTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Order/ReorderWithDifferentStoreTest.php index 33d86189aa4f..0e9f60719df9 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Order/ReorderWithDifferentStoreTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Order/ReorderWithDifferentStoreTest.php @@ -117,7 +117,7 @@ public function testReorderWithDifferentStoreAndGlobalCustomerAccount(): void $this->dispatch('sales/order/reorder/'); $this->assertRedirect($this->stringContains('checkout/cart')); $this->quote = $this->checkoutSession->getQuote(); - $quoteItemsCollection = $this->quote->getAllItems(); - $this->assertCount(0, $quoteItemsCollection); + + $this->assertCount(1, $this->quote->getErrors()); } }