diff --git a/vendor/magento/module-catalog/Model/Indexer/Product/Full.php b/vendor/magento/module-catalog/Model/Indexer/Product/Full.php index bb696c5cab4ce..aed594ee4144e 100644 --- a/vendor/magento/module-catalog/Model/Indexer/Product/Full.php +++ b/vendor/magento/module-catalog/Model/Indexer/Product/Full.php @@ -6,7 +6,9 @@ namespace Magento\Catalog\Model\Indexer\Product; +use Magento\Framework\App\ObjectManager; use Magento\Framework\Indexer\ActionInterface; +use Magento\Framework\Indexer\ConfigInterface; use Magento\Framework\Indexer\IndexerRegistry; /** @@ -24,26 +26,35 @@ class Full implements ActionInterface */ private $indexerList; + /** + * @var ConfigInterface + */ + private $config; + /** * Initialize dependencies * * @param IndexerRegistry $indexerRegistry * @param string[] $indexerList + * @param ConfigInterface|null $config */ public function __construct( IndexerRegistry $indexerRegistry, - array $indexerList + array $indexerList, + ?ConfigInterface $config = null ) { $this->indexerRegistry = $indexerRegistry; $this->indexerList = $indexerList; + $this->config = $config + ?? ObjectManager::getInstance()->get(ConfigInterface::class); } /** - * {@inheritdoc} + * @inheritdoc */ public function executeFull() { - foreach ($this->indexerList as $indexerName) { + foreach ($this->getIndexerList() as $indexerName) { $indexer = $this->indexerRegistry->get($indexerName); if (!$indexer->isScheduled()) { $indexer->reindexAll(); @@ -52,12 +63,12 @@ public function executeFull() } /** - * {@inheritdoc} + * @inheritdoc */ public function executeList(array $ids) { if (!empty($ids)) { - foreach ($this->indexerList as $indexerName) { + foreach ($this->getIndexerList() as $indexerName) { $indexer = $this->indexerRegistry->get($indexerName); if (!$indexer->isScheduled()) { $indexer->reindexList($ids); @@ -67,12 +78,12 @@ public function executeList(array $ids) } /** - * {@inheritDoc} + * @inheritDoc */ public function executeRow($id) { if (!empty($id)) { - foreach ($this->indexerList as $indexerName) { + foreach ($this->getIndexerList() as $indexerName) { $indexer = $this->indexerRegistry->get($indexerName); if (!$indexer->isScheduled()) { $indexer->reindexRow($id); @@ -80,4 +91,21 @@ public function executeRow($id) } } } + + /** + * Returns indexers in the order according to dependency tree + * + * @return array + */ + private function getIndexerList(): array + { + $indexers = []; + foreach (array_keys($this->config->getIndexers()) as $indexerId) { + if (in_array($indexerId, $this->indexerList, true)) { + $indexers[] = $indexerId; + } + } + + return $indexers; + } } diff --git a/vendor/magento/module-catalog-rule/Model/Indexer/Rule/GetAffectedProductIds.php b/vendor/magento/module-catalog-rule/Model/Indexer/Rule/GetAffectedProductIds.php new file mode 100644 index 0000000000000..a7e36dcbb665b --- /dev/null +++ b/vendor/magento/module-catalog-rule/Model/Indexer/Rule/GetAffectedProductIds.php @@ -0,0 +1,54 @@ +ruleResourceModel->getProductIdsByRuleIds($ids); + $rules = $this->ruleCollectionFactory->create() + ->addFieldToFilter('rule_id', ['in' => array_map('intval', $ids)]); + foreach ($rules as $rule) { + /** @var Rule $rule */ + array_push($productIds, ...array_keys($rule->getMatchingProductIds())); + } + return array_values(array_unique($productIds)); + } +} diff --git a/vendor/magento/module-catalog-rule/Model/Indexer/Rule/RuleProductIndexer.php b/vendor/magento/module-catalog-rule/Model/Indexer/Rule/RuleProductIndexer.php index 3e978ffe5d3ba..f01fc873bf367 100644 --- a/vendor/magento/module-catalog-rule/Model/Indexer/Rule/RuleProductIndexer.php +++ b/vendor/magento/module-catalog-rule/Model/Indexer/Rule/RuleProductIndexer.php @@ -3,30 +3,63 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\CatalogRule\Model\Indexer\Rule; use Magento\CatalogRule\Model\Indexer\AbstractIndexer; +use Magento\CatalogRule\Model\Indexer\IndexBuilder; +use Magento\CatalogRule\Model\Indexer\Product\ProductRuleProcessor; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Event\ManagerInterface; class RuleProductIndexer extends AbstractIndexer { /** - * {@inheritdoc} - * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @var ProductRuleProcessor + */ + private $productRuleProcessor; + + /** + * @var GetAffectedProductIds + */ + private $getAffectedProductIds; + + /** + * @param IndexBuilder $indexBuilder + * @param ManagerInterface $eventManager + * @param ProductRuleProcessor|null $productRuleProcessor + * @param GetAffectedProductIds|null $getAffectedProductIds + */ + public function __construct( + IndexBuilder $indexBuilder, + ManagerInterface $eventManager, + ?ProductRuleProcessor $productRuleProcessor = null, + ?GetAffectedProductIds $getAffectedProductIds = null + ) { + $this->productRuleProcessor = $productRuleProcessor + ?? ObjectManager::getInstance()->get(ProductRuleProcessor::class); + $this->getAffectedProductIds = $getAffectedProductIds + ?? ObjectManager::getInstance()->get(GetAffectedProductIds::class); + parent::__construct($indexBuilder, $eventManager); + } + + /** + * @inheritdoc */ protected function doExecuteList($ids) { - $this->indexBuilder->reindexFull(); - $this->getCacheContext()->registerTags($this->getIdentities()); + $affectedProductIds = $this->getAffectedProductIds->execute($ids); + if (!$affectedProductIds) { + return; + } + $this->productRuleProcessor->reindexList($affectedProductIds, true); } /** - * {@inheritdoc} - * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @inheritdoc */ protected function doExecuteRow($id) { - $this->indexBuilder->reindexFull(); + $this->doExecuteList([$id]); } } diff --git a/vendor/magento/module-catalog-rule/Model/Indexer/Rule/RuleProductProcessor.php b/vendor/magento/module-catalog-rule/Model/Indexer/Rule/RuleProductProcessor.php index 260b119cb2903..062d54325e6a4 100644 --- a/vendor/magento/module-catalog-rule/Model/Indexer/Rule/RuleProductProcessor.php +++ b/vendor/magento/module-catalog-rule/Model/Indexer/Rule/RuleProductProcessor.php @@ -5,12 +5,79 @@ */ namespace Magento\CatalogRule\Model\Indexer\Rule; +use Magento\Catalog\Model\Indexer\Product\Price\Processor as ProductPriceProcessor; +use Magento\CatalogRule\Model\Indexer\Product\ProductRuleProcessor; +use Magento\Framework\App\ObjectManager; use Magento\Framework\Indexer\AbstractProcessor; +use Magento\Framework\Indexer\IndexerRegistry; class RuleProductProcessor extends AbstractProcessor { /** * Indexer id */ - const INDEXER_ID = 'catalogrule_rule'; + public const INDEXER_ID = 'catalogrule_rule'; + + /** + * @var ProductRuleProcessor + */ + private $productRuleProcessor; + + /** + * @var ProductPriceProcessor + */ + private $productPriceProcessor; + + /** + * @var GetAffectedProductIds + */ + private $getAffectedProductIds; + + /** + * @param IndexerRegistry $indexerRegistry + * @param ProductRuleProcessor|null $productRuleProcessor + * @param ProductPriceProcessor|null $productPriceProcessor + * @param GetAffectedProductIds|null $getAffectedProductIds + */ + public function __construct( + IndexerRegistry $indexerRegistry, + ?ProductRuleProcessor $productRuleProcessor = null, + ?ProductPriceProcessor $productPriceProcessor = null, + ?GetAffectedProductIds $getAffectedProductIds = null + ) { + $this->productRuleProcessor = $productRuleProcessor + ?? ObjectManager::getInstance()->get(ProductRuleProcessor::class); + $this->productPriceProcessor = $productPriceProcessor + ?? ObjectManager::getInstance()->get(ProductPriceProcessor::class); + $this->getAffectedProductIds = $getAffectedProductIds + ?? ObjectManager::getInstance()->get(GetAffectedProductIds::class); + parent::__construct($indexerRegistry); + } + + /** + * @inheritdoc + */ + public function reindexRow($id, $forceReindex = false) + { + $this->reindexList([$id], $forceReindex); + } + + /** + * @inheritdoc + */ + public function reindexList($ids, $forceReindex = false) + { + if (empty($ids) || !$forceReindex && $this->isIndexerScheduled()) { + return; + } + $affectedProductIds = $this->getAffectedProductIds->execute($ids); + if (!$affectedProductIds) { + return; + } + // catalog_product_price depends on catalogrule_rule. However, their interfaces are not compatible, + // thus the rule is indexed using catalogrule_product + // and price indexer is triggered to update dependent indexes. + $this->productRuleProcessor->reindexList($affectedProductIds); + $this->productPriceProcessor->reindexList($affectedProductIds); + } } diff --git a/vendor/magento/module-catalog-rule/Model/ResourceModel/Rule.php b/vendor/magento/module-catalog-rule/Model/ResourceModel/Rule.php index dd4f3306b8603..5576187379244 100644 --- a/vendor/magento/module-catalog-rule/Model/ResourceModel/Rule.php +++ b/vendor/magento/module-catalog-rule/Model/ResourceModel/Rule.php @@ -12,6 +12,8 @@ namespace Magento\CatalogRule\Model\ResourceModel; use Magento\Catalog\Model\Product; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\EntityManager\EntityManager; use Magento\Framework\Model\AbstractModel; use Magento\Framework\Pricing\PriceCurrencyInterface; @@ -23,7 +25,7 @@ class Rule extends \Magento\Rule\Model\ResourceModel\AbstractResource /** * Store number of seconds in a day */ - const SECONDS_IN_DAY = 86400; + public const SECONDS_IN_DAY = 86400; /** * @var \Psr\Log\LoggerInterface @@ -31,8 +33,6 @@ class Rule extends \Magento\Rule\Model\ResourceModel\AbstractResource protected $_logger; /** - * Catalog rule data - * * @var \Magento\CatalogRule\Helper\Data */ protected $_catalogRuleData = null; @@ -75,7 +75,7 @@ class Rule extends \Magento\Rule\Model\ResourceModel\AbstractResource protected $priceCurrency; /** - * @var \Magento\Framework\EntityManager\EntityManager + * @var EntityManager */ protected $entityManager; @@ -91,7 +91,8 @@ class Rule extends \Magento\Rule\Model\ResourceModel\AbstractResource * @param \Psr\Log\LoggerInterface $logger * @param \Magento\Framework\Stdlib\DateTime $dateTime * @param PriceCurrencyInterface $priceCurrency - * @param null $connectionName + * @param string|null $connectionName + * @param EntityManager|null $entityManager * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -105,7 +106,8 @@ public function __construct( \Psr\Log\LoggerInterface $logger, \Magento\Framework\Stdlib\DateTime $dateTime, PriceCurrencyInterface $priceCurrency, - $connectionName = null + $connectionName = null, + ?EntityManager $entityManager = null ) { $this->_storeManager = $storeManager; $this->_conditionFactory = $conditionFactory; @@ -116,7 +118,11 @@ public function __construct( $this->_logger = $logger; $this->dateTime = $dateTime; $this->priceCurrency = $priceCurrency; - $this->_associatedEntitiesMap = $this->getAssociatedEntitiesMap(); + $this->entityManager = $entityManager ?? ObjectManager::getInstance()->get(EntityManager::class); + $this->_associatedEntitiesMap = ObjectManager::getInstance() + // phpstan:ignore this is a virtual class + ->get(\Magento\CatalogRule\Model\ResourceModel\Rule\AssociatedEntityMap::class) + ->getData(); parent::__construct($context, $connectionName); } @@ -132,26 +138,7 @@ protected function _construct() } /** - * @param \Magento\Framework\Model\AbstractModel $rule - * @return $this - */ - protected function _afterDelete(\Magento\Framework\Model\AbstractModel $rule) - { - $connection = $this->getConnection(); - $connection->delete( - $this->getTable('catalogrule_product'), - ['rule_id=?' => $rule->getId()] - ); - $connection->delete( - $this->getTable('catalogrule_group_website'), - ['rule_id=?' => $rule->getId()] - ); - return parent::_afterDelete($rule); - } - - /** - * Get catalog rules product price for specific date, website and - * customer group + * Get catalog rules product price for specific date, website and customer group * * @param \DateTimeInterface $date * @param int $wId @@ -171,6 +158,7 @@ public function getRulePrice($date, $wId, $gId, $pId) /** * Retrieve product prices by catalog rule for specific date, website and customer group + * * Collect data with product Id => price pairs * * @param \DateTimeInterface $date @@ -219,6 +207,8 @@ public function getRulesFromProduct($date, $websiteId, $customerGroupId, $produc } /** + * Load an object + * * @param \Magento\Framework\Model\AbstractModel $object * @param mixed $value * @param string $field @@ -227,18 +217,16 @@ public function getRulesFromProduct($date, $websiteId, $customerGroupId, $produc */ public function load(\Magento\Framework\Model\AbstractModel $object, $value, $field = null) { - $this->getEntityManager()->load($object, $value); + $this->entityManager->load($object, $value); return $this; } /** - * @param AbstractModel $object - * @return $this - * @throws \Exception + * @inheritdoc */ public function save(\Magento\Framework\Model\AbstractModel $object) { - $this->getEntityManager()->save($object); + $this->entityManager->save($object); return $this; } @@ -251,34 +239,31 @@ public function save(\Magento\Framework\Model\AbstractModel $object) */ public function delete(AbstractModel $object) { - $this->getEntityManager()->delete($object); + $this->entityManager->delete($object); return $this; } /** + * Get product ids matching specified rules + * + * @param array $ruleIds * @return array - * @deprecated 100.1.0 */ - private function getAssociatedEntitiesMap() + public function getProductIdsByRuleIds(array $ruleIds): array { - if (!$this->_associatedEntitiesMap) { - $this->_associatedEntitiesMap = \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\CatalogRule\Model\ResourceModel\Rule\AssociatedEntityMap::class) - ->getData(); - } - return $this->_associatedEntitiesMap; - } - - /** - * @return \Magento\Framework\EntityManager\EntityManager - * @deprecated 100.1.0 - */ - private function getEntityManager() - { - if (null === $this->entityManager) { - $this->entityManager = \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Framework\EntityManager\EntityManager::class); - } - return $this->entityManager; + $connection = $this->getConnection(); + $select = $connection->select() + ->from( + $this->getTable('catalogrule_product'), + ['product_id'] + ) + ->where( + 'rule_id IN (?)', + array_map('intval', $ruleIds) + ) + ->distinct( + true + ); + return array_map('intval', $connection->fetchCol($select)); } } diff --git a/vendor/magento/module-catalog-rule/Model/Rule.php b/vendor/magento/module-catalog-rule/Model/Rule.php index 1eca8469db1c6..6abf06a95d4d8 100644 --- a/vendor/magento/module-catalog-rule/Model/Rule.php +++ b/vendor/magento/module-catalog-rule/Model/Rule.php @@ -605,13 +605,8 @@ public function afterSave() return parent::afterSave(); } - if ($this->isObjectNew() && !$this->_ruleProductProcessor->isIndexerScheduled()) { - $productIds = $this->getMatchingProductIds(); - if (!empty($productIds) && is_array($productIds)) { - $this->ruleResourceModel->addCommitCallback([$this, 'reindex']); - } - } else { - $this->_ruleProductProcessor->getIndexer()->invalidate(); + if (!$this->_ruleProductProcessor->isIndexerScheduled()) { + $this->ruleResourceModel->addCommitCallback([$this, 'reindex']); } return parent::afterSave(); @@ -624,15 +619,7 @@ public function afterSave() */ public function reindex() { - $productIds = $this->_productIds ? array_keys( - array_filter( - $this->_productIds, - function (array $data) { - return array_filter($data); - } - ) - ) : []; - $this->_ruleProductProcessor->reindexList($productIds); + $this->_ruleProductProcessor->reindexRow($this->getRuleId()); } /** @@ -642,7 +629,9 @@ function (array $data) { */ public function afterDelete() { - $this->_ruleProductProcessor->getIndexer()->invalidate(); + if ($this->getIsActive() && !$this->_ruleProductProcessor->isIndexerScheduled()) { + $this->ruleResourceModel->addCommitCallback([$this, 'reindex']); + } return parent::afterDelete(); } diff --git a/vendor/magento/module-catalog-search/etc/mview.xml b/vendor/magento/module-catalog-search/etc/mview.xml index 28737ce23b1e0..833954f993fe0 100644 --- a/vendor/magento/module-catalog-search/etc/mview.xml +++ b/vendor/magento/module-catalog-search/etc/mview.xml @@ -19,6 +19,7 @@