| Current Path : /home/rtorresani/www/vendor/magento/module-catalog/Model/Indexer/Category/Flat/Action/ |
| Current File : //home/rtorresani/www/vendor/magento/module-catalog/Model/Indexer/Category/Flat/Action/Full.php |
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Magento\Catalog\Model\Indexer\Category\Flat\Action;
/**
* Class for full reindex flat categories
*/
class Full extends \Magento\Catalog\Model\Indexer\Category\Flat\AbstractAction
{
/**
* Suffix for table to show it is old
*/
const OLD_TABLE_SUFFIX = '_old';
/**
* Whether table changes are allowed
*
* @var bool
*/
protected $allowTableChanges = true;
/**
* Add suffix to table name to show it is old
*
* @param string $tableName
* @return string
*/
protected function addOldTableSuffix($tableName)
{
return $tableName . self::OLD_TABLE_SUFFIX;
}
/**
* Populate category flat tables with data
*
* @param \Magento\Store\Model\Store[] $stores
* @return Full
*/
protected function populateFlatTables(array $stores)
{
$rootId = \Magento\Catalog\Model\Category::TREE_ROOT_ID;
$categories = [];
$categoriesIds = [];
/* @var $store \Magento\Store\Model\Store */
foreach ($stores as $store) {
if (!isset($categories[$store->getRootCategoryId()])) {
$select = $this->connection->select()->from(
$this->connection->getTableName($this->getTableName('catalog_category_entity'))
)->where(
'path = ?',
(string)$rootId
)->orWhere(
'path = ?',
"{$rootId}/{$store->getRootCategoryId()}"
)->orWhere(
'path LIKE ?',
"{$rootId}/{$store->getRootCategoryId()}/%"
);
$categories[$store->getRootCategoryId()] = $this->connection->fetchAll($select);
$categoriesIds[$store->getRootCategoryId()] = [];
foreach ($categories[$store->getRootCategoryId()] as $category) {
$categoriesIds[$store->getRootCategoryId()][] = $category['entity_id'];
}
}
/** @TODO Do something with chunks */
$categoriesIdsChunks = array_chunk($categoriesIds[$store->getRootCategoryId()], 500);
foreach ($categoriesIdsChunks as $categoriesIdsChunk) {
$attributesData = $this->getAttributeValues($categoriesIdsChunk, $store->getId());
$linkField = $this->categoryMetadata->getLinkField();
$data = [];
foreach ($categories[$store->getRootCategoryId()] as $category) {
if (!isset($attributesData[$category[$linkField]])) {
continue;
}
$category['store_id'] = $store->getId();
$data[] = $this->prepareValuesToInsert(
// phpcs:ignore Magento2.Performance.ForeachArrayMerge
array_merge($category, $attributesData[$category[$linkField]])
);
}
$this->connection->insertMultiple(
$this->addTemporaryTableSuffix($this->getMainStoreTable($store->getId())),
$data
);
}
}
return $this;
}
/**
* Create table and add attributes as fields for specified store.
*
* This routine assumes that DDL operations are allowed
*
* @param int $store
* @return Full
*/
protected function createTable($store)
{
$temporaryTable = $this->addTemporaryTableSuffix($this->getMainStoreTable($store));
$table = $this->getFlatTableStructure($temporaryTable);
$this->connection->dropTable($temporaryTable);
$this->connection->createTable($table);
return $this;
}
/**
* Create category flat tables and add attributes as fields.
*
* Tables are created only if DDL operations are allowed
*
* @param \Magento\Store\Model\Store[] $stores if empty, create tables for all stores of the application
* @return Full
*/
protected function createTables(array $stores = [])
{
if ($this->connection->getTransactionLevel() > 0) {
return $this;
}
if (empty($stores)) {
$stores = $this->storeManager->getStores();
}
/* @var $store \Magento\Store\Model\Store */
foreach ($stores as $store) {
$this->createTable($store->getId());
}
return $this;
}
/**
* Switch table (temporary becomes active, old active will be dropped)
*
* @param \Magento\Store\Model\Store[] $stores
* @return Full
*/
protected function switchTables(array $stores = [])
{
/** @var $store \Magento\Store\Model\Store */
foreach ($stores as $store) {
$activeTableName = $this->getMainStoreTable($store->getId());
$temporaryTableName = $this->addTemporaryTableSuffix($this->getMainStoreTable($store->getId()));
$oldTableName = $this->addOldTableSuffix($this->getMainStoreTable($store->getId()));
//switch tables
$tablesToRename = [];
if ($this->connection->isTableExists($activeTableName)) {
$tablesToRename[] = ['oldName' => $activeTableName, 'newName' => $oldTableName];
}
$tablesToRename[] = ['oldName' => $temporaryTableName, 'newName' => $activeTableName];
foreach ($tablesToRename as $tableToRename) {
$this->connection->renameTable($tableToRename['oldName'], $tableToRename['newName']);
}
//delete inactive table
$tableToDelete = $oldTableName;
if ($this->connection->isTableExists($tableToDelete)) {
$this->connection->dropTable($tableToDelete);
}
}
return $this;
}
/**
* Retrieve all actual Catalog Product Flat Table names
*
* @return string[]
*/
private function getActualStoreTablesForCategoryFlat(): array
{
$actualStoreTables = [];
foreach ($this->storeManager->getStores() as $store) {
$actualStoreTables[] = sprintf(
'%s_store_%s',
$this->connection->getTableName($this->getTableName('catalog_category_flat')),
$store->getId()
);
}
return $actualStoreTables;
}
/**
* Delete all category flat tables for not existing stores
*
* @return void
*/
private function deleteAbandonedStoreCategoryFlatTables(): void
{
$existentTables = $this->connection->getTables(
$this->connection->getTableName($this->getTableName('catalog_category_flat_store_%'))
);
$actualStoreTables = $this->getActualStoreTablesForCategoryFlat();
$tablesToDelete = array_diff($existentTables, $actualStoreTables);
foreach ($tablesToDelete as $table) {
$this->connection->dropTable($table);
}
}
/**
* Transactional rebuild flat data from eav
*
* @return Full
*/
public function reindexAll()
{
$this->createTables();
if ($this->allowTableChanges) {
$this->allowTableChanges = false;
}
$stores = $this->storeManager->getStores();
$this->populateFlatTables($stores);
$this->switchTables($stores);
$this->deleteAbandonedStoreCategoryFlatTables();
$this->allowTableChanges = true;
return $this;
}
}