![]() Server : Apache System : Linux server2.corals.io 4.18.0-348.2.1.el8_5.x86_64 #1 SMP Mon Nov 15 09:17:08 EST 2021 x86_64 User : corals ( 1002) PHP Version : 7.4.33 Disable Function : exec,passthru,shell_exec,system Directory : /home/corals/cartforge.co/vendor/magento/module-catalog/Model/Indexer/Product/Price/Action/ |
<?php /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ declare(strict_types=1); namespace Magento\Catalog\Model\Indexer\Product\Price\Action; use Magento\Catalog\Model\Indexer\Product\Price\AbstractAction; use Magento\Catalog\Model\Indexer\Product\Price\DimensionCollectionFactory; use Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer; use Magento\Catalog\Model\Product\Type; use Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher; use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\BatchSizeCalculator; use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\DefaultPrice; use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\Factory; use Magento\Directory\Model\CurrencyFactory; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\ObjectManager; use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\PriceInterface; use Magento\Framework\DB\Adapter\AdapterInterface; use Magento\Framework\DB\Query\BatchIterator; use Magento\Framework\DB\Query\Generator as QueryGenerator; use Magento\Framework\DB\Select; use Magento\Framework\EntityManager\EntityMetadataInterface; use Magento\Catalog\Api\Data\ProductInterface; use Magento\Framework\EntityManager\MetadataPool; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Indexer\BatchProviderInterface; use Magento\Framework\Indexer\DimensionalIndexerInterface; use Magento\Customer\Model\Indexer\CustomerGroupDimensionProvider; use Magento\Framework\Stdlib\DateTime; use Magento\Framework\Stdlib\DateTime\TimezoneInterface; use Magento\Indexer\Model\ProcessManager; use Magento\Store\Model\Indexer\WebsiteDimensionProvider; use Magento\Store\Model\StoreManagerInterface; /** * Class Full reindex action * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Full extends AbstractAction { /** * @var MetadataPool */ private $metadataPool; /** * @var BatchSizeCalculator */ private $batchSizeCalculator; /** * @var BatchProviderInterface */ private $batchProvider; /** * @var ActiveTableSwitcher */ private $activeTableSwitcher; /** * @var EntityMetadataInterface */ private $productMetaDataCached; /** * @var DimensionCollectionFactory */ private $dimensionCollectionFactory; /** * @var TableMaintainer */ private $dimensionTableMaintainer; /** * @var ProcessManager */ private $processManager; /** * @var QueryGenerator|null */ private $batchQueryGenerator; /** * @param ScopeConfigInterface $config * @param StoreManagerInterface $storeManager * @param CurrencyFactory $currencyFactory * @param TimezoneInterface $localeDate * @param DateTime $dateTime * @param Type $catalogProductType * @param Factory $indexerPriceFactory * @param DefaultPrice $defaultIndexerResource * @param MetadataPool|null $metadataPool * @param BatchSizeCalculator|null $batchSizeCalculator * @param BatchProviderInterface|null $batchProvider * @param ActiveTableSwitcher|null $activeTableSwitcher * @param DimensionCollectionFactory|null $dimensionCollectionFactory * @param TableMaintainer|null $dimensionTableMaintainer * @param ProcessManager $processManager * @param QueryGenerator|null $batchQueryGenerator * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( ScopeConfigInterface $config, StoreManagerInterface $storeManager, CurrencyFactory $currencyFactory, TimezoneInterface $localeDate, DateTime $dateTime, Type $catalogProductType, Factory $indexerPriceFactory, DefaultPrice $defaultIndexerResource, MetadataPool $metadataPool = null, BatchSizeCalculator $batchSizeCalculator = null, BatchProviderInterface $batchProvider = null, ActiveTableSwitcher $activeTableSwitcher = null, DimensionCollectionFactory $dimensionCollectionFactory = null, TableMaintainer $dimensionTableMaintainer = null, ProcessManager $processManager = null, QueryGenerator $batchQueryGenerator = null ) { parent::__construct( $config, $storeManager, $currencyFactory, $localeDate, $dateTime, $catalogProductType, $indexerPriceFactory, $defaultIndexerResource ); $this->metadataPool = $metadataPool ?: ObjectManager::getInstance()->get( MetadataPool::class ); $this->batchSizeCalculator = $batchSizeCalculator ?: ObjectManager::getInstance()->get( BatchSizeCalculator::class ); $this->batchProvider = $batchProvider ?: ObjectManager::getInstance()->get( BatchProviderInterface::class ); $this->activeTableSwitcher = $activeTableSwitcher ?: ObjectManager::getInstance()->get( ActiveTableSwitcher::class ); $this->dimensionCollectionFactory = $dimensionCollectionFactory ?: ObjectManager::getInstance()->get( DimensionCollectionFactory::class ); $this->dimensionTableMaintainer = $dimensionTableMaintainer ?: ObjectManager::getInstance()->get( TableMaintainer::class ); $this->processManager = $processManager ?: ObjectManager::getInstance()->get( ProcessManager::class ); $this->batchQueryGenerator = $batchQueryGenerator ?? ObjectManager::getInstance()->get(QueryGenerator::class); } /** * Execute Full reindex * * @param array|int|null $ids * @return void * @throws \Exception * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function execute($ids = null): void { try { //Prepare indexer tables before full reindex $this->prepareTables(); /** @var DefaultPrice $indexer */ foreach ($this->getTypeIndexers(true) as $typeId => $priceIndexer) { if ($priceIndexer instanceof DimensionalIndexerInterface) { //New price reindex mechanism $this->reindexProductTypeWithDimensions($priceIndexer, $typeId); continue; } $priceIndexer->getTableStrategy()->setUseIdxTable(false); //Old price reindex mechanism $this->reindexProductType($priceIndexer, $typeId); } //Final replacement of tables from replica to main $this->switchTables(); } catch (\Exception $e) { throw new LocalizedException(__($e->getMessage()), $e); } } /** * Prepare indexer tables before full reindex * * @return void * @throws \Exception */ private function prepareTables(): void { $this->_defaultIndexerResource->getTableStrategy()->setUseIdxTable(false); $this->_prepareWebsiteDateTable(); $this->truncateReplicaTables(); } /** * Truncate replica tables by dimensions * * @return void * @throws \Exception */ private function truncateReplicaTables(): void { foreach ($this->dimensionCollectionFactory->create() as $dimension) { $dimensionTable = $this->dimensionTableMaintainer->getMainReplicaTable($dimension); $this->_defaultIndexerResource->getConnection()->truncateTable($dimensionTable); } } /** * Reindex new 'Dimensional' price indexer by product type * * @param DimensionalIndexerInterface $priceIndexer * @param string $typeId * * @return void * @throws \Exception */ private function reindexProductTypeWithDimensions(DimensionalIndexerInterface $priceIndexer, string $typeId): void { $userFunctions = []; foreach ($this->dimensionCollectionFactory->create() as $dimensions) { $userFunctions[] = function () use ($priceIndexer, $dimensions, $typeId) { $this->reindexByBatches($priceIndexer, $dimensions, $typeId); }; } $this->processManager->execute($userFunctions); } /** * Reindex new 'Dimensional' price indexer by batches * * @param DimensionalIndexerInterface $priceIndexer * @param array $dimensions * @param string $typeId * * @return void * @throws \Exception */ private function reindexByBatches( DimensionalIndexerInterface $priceIndexer, array $dimensions, string $typeId ): void { foreach ($this->getBatchesForIndexer($typeId) as $batch) { $this->reindexByBatchWithDimensions($priceIndexer, $batch, $dimensions); } } /** * Get batches for new 'Dimensional' price indexer * * @param string $typeId * * @return BatchIterator * @throws \Exception */ private function getBatchesForIndexer(string $typeId): BatchIterator { $connection = $this->_defaultIndexerResource->getConnection(); $entityMetadata = $this->getProductMetaData(); $select = $connection->select(); $select->distinct(true); $select->from(['e' => $entityMetadata->getEntityTable()], $entityMetadata->getIdentifierField()); $select->where('type_id = ?', $typeId); return $this->batchQueryGenerator->generate( $this->getProductMetaData()->getIdentifierField(), $select, $this->batchSizeCalculator->estimateBatchSize( $connection, $typeId ) ); } /** * Reindex by batch for new 'Dimensional' price indexer * * @param DimensionalIndexerInterface $priceIndexer * @param Select $batchQuery * @param array $dimensions * * @return void * @throws \Exception */ private function reindexByBatchWithDimensions( DimensionalIndexerInterface $priceIndexer, Select $batchQuery, array $dimensions ): void { $entityIds = $this->getEntityIdsFromBatch($batchQuery); if (!empty($entityIds)) { $this->dimensionTableMaintainer->createMainTmpTable($dimensions); $temporaryTable = $this->dimensionTableMaintainer->getMainTmpTable($dimensions); $this->_emptyTable($temporaryTable); $priceIndexer->executeByDimensions($dimensions, \SplFixedArray::fromArray($entityIds, false)); // Sync data from temp table to index table $this->_insertFromTable( $temporaryTable, $this->dimensionTableMaintainer->getMainReplicaTable($dimensions) ); } } /** * Reindex old price indexer by product type * * @param PriceInterface $priceIndexer * @param string $typeId * * @return void * @throws \Exception */ private function reindexProductType(PriceInterface $priceIndexer, string $typeId): void { foreach ($this->getBatchesForIndexer($typeId) as $batch) { $this->reindexBatch($priceIndexer, $batch); } } /** * Reindex by batch for old price indexer * * @param PriceInterface $priceIndexer * @param Select $batch * @return void * @throws \Exception */ private function reindexBatch(PriceInterface $priceIndexer, Select $batch): void { $entityIds = $this->getEntityIdsFromBatch($batch); if (!empty($entityIds)) { // Temporary table will created if not exists $idxTableName = $this->_defaultIndexerResource->getIdxTable(); $this->_emptyTable($idxTableName); if ($priceIndexer->getIsComposite()) { $this->_copyRelationIndexData($entityIds); } // Reindex entities by id $priceIndexer->reindexEntity($entityIds); // Sync data from temp table to index table $this->_insertFromTable($idxTableName, $this->getReplicaTable()); // Drop temporary index table $this->_defaultIndexerResource->getConnection()->dropTable($idxTableName); } } /** * Get Entity Ids from batch * * @param Select $batch * @return array * @throws \Exception */ private function getEntityIdsFromBatch(Select $batch): array { $connection = $this->_defaultIndexerResource->getConnection(); return $connection->fetchCol($batch); } /** * Get product meta data * * @return EntityMetadataInterface * @throws \Exception */ private function getProductMetaData(): EntityMetadataInterface { if ($this->productMetaDataCached === null) { $this->productMetaDataCached = $this->metadataPool->getMetadata(ProductInterface::class); } return $this->productMetaDataCached; } /** * Get replica table * * @return string * @throws \Exception */ private function getReplicaTable(): string { return $this->activeTableSwitcher->getAdditionalTableName( $this->_defaultIndexerResource->getMainTable() ); } /** * Replacement of tables from replica to main * * @return void * @throws \Zend_Db_Statement_Exception */ private function switchTables(): void { // Switch dimension tables $mainTablesByDimension = []; foreach ($this->dimensionCollectionFactory->create() as $dimensions) { $mainTablesByDimension[] = $this->dimensionTableMaintainer->getMainTableByDimensions($dimensions); //Move data from indexers with old realisation $this->moveDataFromReplicaTableToReplicaTables($dimensions); } if (count($mainTablesByDimension) > 0) { $this->activeTableSwitcher->switchTable( $this->_defaultIndexerResource->getConnection(), $mainTablesByDimension ); } } /** * Move data from old price indexer mechanism to new indexer mechanism by dimensions. * * Used only for backward compatibility * * @param array $dimensions * @return void * @throws \Zend_Db_Statement_Exception */ private function moveDataFromReplicaTableToReplicaTables(array $dimensions): void { if (!$dimensions) { return; } $select = $this->dimensionTableMaintainer->getConnection()->select()->from( $this->dimensionTableMaintainer->getMainReplicaTable([]) ); $check = clone $select; $check->reset('columns')->columns('count(*)'); if (!$this->dimensionTableMaintainer->getConnection()->query($check)->fetchColumn()) { return; } $replicaTablesByDimension = $this->dimensionTableMaintainer->getMainReplicaTable($dimensions); foreach ($dimensions as $dimension) { if ($dimension->getName() === WebsiteDimensionProvider::DIMENSION_NAME) { $select->where('website_id = ?', $dimension->getValue()); } if ($dimension->getName() === CustomerGroupDimensionProvider::DIMENSION_NAME) { $select->where('customer_group_id = ?', $dimension->getValue()); } } $this->dimensionTableMaintainer->getConnection()->query( $this->dimensionTableMaintainer->getConnection()->insertFromSelect( $select, $replicaTablesByDimension, [], AdapterInterface::INSERT_ON_DUPLICATE ) ); } /** * Retrieves the index table that should be used * * @deprecated 102.0.6 */ protected function getIndexTargetTable(): string { return $this->activeTableSwitcher->getAdditionalTableName($this->_defaultIndexerResource->getMainTable()); } }