| Current Path : /home/rtorresani/www/vendor/magento/module-catalog/Model/Indexer/Category/Flat/ |
| Current File : //home/rtorresani/www/vendor/magento/module-catalog/Model/Indexer/Category/Flat/AbstractAction.php |
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Magento\Catalog\Model\Indexer\Category\Flat;
use Magento\Catalog\Api\Data\CategoryInterface;
use Magento\Catalog\Model\Category;
use Magento\Catalog\Model\ResourceModel\Helper;
use Magento\Framework\App\ObjectManager;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\DB\Adapter\AdapterInterface;
use Magento\Framework\DB\Ddl\Table;
use Magento\Framework\EntityManager\EntityMetadata;
use Magento\Framework\EntityManager\MetadataPool;
use Magento\Store\Model\Store;
use Magento\Store\Model\StoreManagerInterface;
/**
* Abstract action class for category flat indexers.
*
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class AbstractAction
{
/**
* Suffix for table to show it is temporary
*/
public const TEMPORARY_TABLE_SUFFIX = '_tmp';
/**
* @var array
*/
protected $attributeCodes;
/**
* @var Resource
*/
protected $resource;
/**
* @var StoreManagerInterface
*/
protected $storeManager;
/**
* Catalog resource helper
*
* @var Helper
*/
protected $resourceHelper;
/**
* Flat columns
*
* @var array
*/
protected $columns = [];
/**
* @var AdapterInterface
*/
protected $connection;
/**
* @var EntityMetadata
*/
protected $categoryMetadata;
/**
* Static columns to skip
*
* @var array
*/
protected $skipStaticColumns = [];
/**
* @var SkipStaticColumnsProvider
*/
private $skipStaticColumnsProvider;
/**
* @param ResourceConnection $resource
* @param StoreManagerInterface $storeManager
* @param Helper $resourceHelper
* @param MetadataPool|null $metadataPool
* @param SkipStaticColumnsProvider|null $skipStaticColumnsProvider
*/
public function __construct(
ResourceConnection $resource,
StoreManagerInterface $storeManager,
Helper $resourceHelper,
MetadataPool $metadataPool = null,
SkipStaticColumnsProvider $skipStaticColumnsProvider = null
) {
$this->resource = $resource;
$this->connection = $resource->getConnection();
$this->storeManager = $storeManager;
$this->resourceHelper = $resourceHelper;
$metadataPool = $metadataPool ?? ObjectManager::getInstance()->get(MetadataPool::class);
$this->categoryMetadata = $metadataPool->getMetadata(CategoryInterface::class);
$this->skipStaticColumnsProvider = $skipStaticColumnsProvider
?? ObjectManager::getInstance()->get(SkipStaticColumnsProvider::class);
$this->columns = array_merge($this->getStaticColumns(), $this->getEavColumns());
}
/**
* Add suffix to table name to show it is temporary
*
* @param string $tableName
* @return string
*/
protected function addTemporaryTableSuffix($tableName)
{
return $tableName . self::TEMPORARY_TABLE_SUFFIX;
}
/**
* Retrieve list of columns for flat structure
*
* @return array
*/
public function getColumns()
{
return $this->columns;
}
/**
* Return name of table for given $storeId.
*
* @param integer $storeId
* @return string
*/
public function getMainStoreTable($storeId = Store::DEFAULT_STORE_ID)
{
if (is_string($storeId)) {
$storeId = (int) $storeId;
}
$suffix = sprintf('store_%d', $storeId);
return $this->connection->getTableName($this->getTableName('catalog_category_flat_' . $suffix));
}
/**
* Return structure for flat catalog table
*
* @param string $tableName
* @return Table
* @throws \Zend_Db_Exception
*/
protected function getFlatTableStructure($tableName)
{
$table = $this->connection->newTable(
$tableName
)->setComment(
'Catalog Category Flat'
);
//Adding columns
foreach ($this->getColumns() as $fieldName => $fieldProp) {
$default = $fieldProp['default'];
if ($fieldProp['type'][0] == Table::TYPE_TIMESTAMP
&& $default == 'CURRENT_TIMESTAMP'
) {
$default = Table::TIMESTAMP_INIT;
}
$table->addColumn(
$fieldName,
$fieldProp['type'][0],
$fieldProp['type'][1],
[
'nullable' => $fieldProp['nullable'],
'unsigned' => $fieldProp['unsigned'],
'default' => $default,
'primary' => isset($fieldProp['primary']) ? $fieldProp['primary'] : false
],
$fieldProp['comment'] != '' ? $fieldProp['comment'] : ucwords(str_replace('_', ' ', $fieldName))
);
}
// Adding indexes
$table->addIndex(
$this->connection->getIndexName($tableName, ['entity_id']),
['entity_id'],
['type' => 'primary']
);
$table->addIndex(
$this->connection->getIndexName($tableName, ['store_id']),
['store_id'],
['type' => 'index']
);
$table->addIndex(
$this->connection->getIndexName($tableName, ['path']),
['path'],
['type' => 'index']
);
$table->addIndex(
$this->connection->getIndexName($tableName, ['level']),
['level'],
['type' => 'index']
);
return $table;
}
/**
* Return array of static columns
*
* @return array
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
protected function getStaticColumns()
{
$columns = [];
$describe = $this->connection->describeTable(
$this->connection->getTableName($this->getTableName('catalog_category_entity'))
);
foreach ($describe as $column) {
if (in_array($column['COLUMN_NAME'], $this->getSkipStaticColumns())) {
continue;
}
$isUnsigned = '';
$options = null;
$ddlType = $this->resourceHelper->getDdlTypeByColumnType($column['DATA_TYPE']);
$column['DEFAULT'] = $column['DEFAULT'] ? trim($column['DEFAULT'], "' ") : '';
switch ($ddlType) {
case Table::TYPE_SMALLINT:
case Table::TYPE_INTEGER:
case Table::TYPE_BIGINT:
$isUnsigned = (bool)$column['UNSIGNED'];
if ($column['DEFAULT'] === '') {
$column['DEFAULT'] = null;
}
$options = null;
if ($column['SCALE'] > 0) {
$ddlType = Table::TYPE_DECIMAL;
} else {
break;
}
// fall-through intentional
case Table::TYPE_DECIMAL:
$options = $column['PRECISION'] . ',' . $column['SCALE'];
$isUnsigned = null;
if ($column['DEFAULT'] === '') {
$column['DEFAULT'] = null;
}
break;
case Table::TYPE_TEXT:
$options = $column['LENGTH'];
$isUnsigned = null;
break;
case Table::TYPE_TIMESTAMP:
$options = null;
$isUnsigned = null;
break;
case Table::TYPE_DATETIME:
$isUnsigned = null;
break;
}
$columns[$column['COLUMN_NAME']] = [
'type' => [$ddlType, $options],
'unsigned' => $isUnsigned,
'nullable' => $column['NULLABLE'],
'default' => $column['DEFAULT'] === null ? false : $column['DEFAULT'],
'comment' => $column['COLUMN_NAME'],
];
}
$columns['store_id'] = [
'type' => [Table::TYPE_SMALLINT, 5],
'unsigned' => true,
'nullable' => false,
'default' => '0',
'comment' => 'Store Id',
];
return $columns;
}
/**
* Return array of eav columns, skip attribute with static type
*
* @return array
*/
protected function getEavColumns()
{
$columns = [];
foreach ($this->getAttributes() as $attribute) {
if ($attribute['backend_type'] == 'static') {
continue;
}
$columns[$attribute['attribute_code']] = [];
switch ($attribute['backend_type']) {
case 'varchar':
$columns[$attribute['attribute_code']] = [
'type' => [Table::TYPE_TEXT, 255],
'unsigned' => null,
'nullable' => true,
'default' => null,
'comment' => (string)$attribute['frontend_label'],
];
break;
case 'int':
$columns[$attribute['attribute_code']] = [
'type' => [Table::TYPE_INTEGER, null],
'unsigned' => null,
'nullable' => true,
'default' => null,
'comment' => (string)$attribute['frontend_label'],
];
break;
case 'text':
$columns[$attribute['attribute_code']] = [
'type' => [Table::TYPE_TEXT, '64k'],
'unsigned' => null,
'nullable' => true,
'default' => null,
'comment' => (string)$attribute['frontend_label'],
];
break;
case 'datetime':
$columns[$attribute['attribute_code']] = [
'type' => [Table::TYPE_DATETIME, null],
'unsigned' => null,
'nullable' => true,
'default' => null,
'comment' => (string)$attribute['frontend_label'],
];
break;
case 'decimal':
$columns[$attribute['attribute_code']] = [
'type' => [Table::TYPE_DECIMAL, '12,4'],
'unsigned' => null,
'nullable' => true,
'default' => null,
'comment' => (string)$attribute['frontend_label'],
];
break;
}
}
return $columns;
}
/**
* Return array of attribute codes for entity type 'catalog_category'
*
* @return array
*/
protected function getAttributes()
{
if ($this->attributeCodes === null) {
$select = $this->connection->select()->from(
$this->connection->getTableName($this->getTableName('eav_entity_type')),
[]
)->join(
$this->connection->getTableName($this->getTableName('eav_attribute')),
$this->connection->getTableName(
$this->getTableName('eav_attribute')
) . '.entity_type_id = ' . $this->connection->getTableName(
$this->getTableName('eav_entity_type')
) . '.entity_type_id',
$this->connection->getTableName($this->getTableName('eav_attribute')) . '.*'
)->where(
$this->connection->getTableName(
$this->getTableName('eav_entity_type')
) . '.entity_type_code = ?',
Category::ENTITY
);
$this->attributeCodes = [];
foreach ($this->connection->fetchAll($select) as $attribute) {
$this->attributeCodes[$attribute['attribute_id']] = $attribute;
}
}
return $this->attributeCodes;
}
/**
* Return attribute values for given entities and store
*
* @param array $entityIds
* @param integer $storeId
* @return array
*/
protected function getAttributeValues($entityIds, $storeId)
{
if (!is_array($entityIds)) {
$entityIds = [$entityIds];
}
$values = [];
$linkIds = $this->getLinkIds($entityIds);
foreach ($linkIds as $linkId) {
$values[$linkId] = [];
}
$attributes = $this->getAttributes();
$attributesType = ['varchar', 'int', 'decimal', 'text', 'datetime'];
$linkField = $this->categoryMetadata->getLinkField();
foreach ($attributesType as $type) {
foreach ($this->getAttributeTypeValues($type, $entityIds, $storeId) as $row) {
if (isset($row[$linkField], $row['attribute_id'])) {
$attributeId = $row['attribute_id'];
if (isset($attributes[$attributeId])) {
$attributeCode = $attributes[$attributeId]['attribute_code'];
$values[$row[$linkField]][$attributeCode] = $row['value'];
}
}
}
}
return $values;
}
/**
* Translate entity ids into link ids
*
* Used for rows with no EAV attributes set.
*
* @param array $entityIds
* @return array
*/
private function getLinkIds(array $entityIds)
{
$linkField = $this->categoryMetadata->getLinkField();
if ($linkField === 'entity_id') {
return $entityIds;
}
$select = $this->connection->select()->from(
['e' => $this->connection->getTableName($this->getTableName('catalog_category_entity'))],
[$linkField]
)->where(
'e.entity_id IN (?)',
$entityIds,
\Zend_Db::INT_TYPE
);
return $this->connection->fetchCol($select);
}
/**
* Return attribute values for given entities and store of specific attribute type
*
* @param string $type
* @param array $entityIds
* @param integer $storeId
* @return array
*/
protected function getAttributeTypeValues($type, $entityIds, $storeId)
{
$linkField = $this->categoryMetadata->getLinkField();
$select = $this->connection->select()->from(
[
'def' => $this->connection->getTableName($this->getTableName('catalog_category_entity_' . $type)),
],
[$linkField, 'attribute_id']
)->joinLeft(
[
'e' => $this->connection->getTableName($this->getTableName('catalog_category_entity'))
],
"def.{$linkField} = e.{$linkField}"
)->joinLeft(
[
'store' => $this->connection->getTableName(
$this->getTableName('catalog_category_entity_' . $type)
),
],
"store.{$linkField} = def.{$linkField} AND store.attribute_id = def.attribute_id " .
'AND store.store_id = ' .
$storeId,
[
'value' => $this->connection->getCheckSql(
'store.value_id > 0',
$this->connection->quoteIdentifier('store.value'),
$this->connection->quoteIdentifier('def.value')
)
]
)->where(
"e.entity_id IN (?)",
$entityIds,
\Zend_Db::INT_TYPE
)->where(
'def.store_id IN (?)',
[Store::DEFAULT_STORE_ID, $storeId],
\Zend_Db::INT_TYPE
);
return $this->connection->fetchAll($select);
}
/**
* Prepare array of column and columnValue pairs
*
* @param array $data
* @return array
*/
protected function prepareValuesToInsert($data)
{
$values = [];
foreach (array_keys($this->getColumns()) as $column) {
if (isset($data[$column])) {
$values[$column] = $data[$column];
} else {
$values[$column] = null;
}
}
return $values;
}
/**
* Get table name
*
* @param string $name
* @return string
*/
protected function getTableName($name)
{
return $this->resource->getTableName($name);
}
/**
* Gets skipped static columns.
*
* @return array
*/
private function getSkipStaticColumns()
{
if ($this->skipStaticColumns === []) {
$this->skipStaticColumns = $this->skipStaticColumnsProvider->get();
}
return $this->skipStaticColumns;
}
}