PageRenderTime 47ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/app/code/Magento/Catalog/Model/Indexer/Product/Flat/AbstractAction.php

https://gitlab.com/crazybutterfly815/magento2
PHP | 348 lines | 196 code | 34 blank | 118 comment | 19 complexity | de5ddc72eab77dc8edd20aa2c6a70b48 MD5 | raw file
  1. <?php
  2. /**
  3. * Copyright © 2016 Magento. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Catalog\Model\Indexer\Product\Flat;
  7. use Magento\Catalog\Api\Data\ProductInterface;
  8. use Magento\Framework\App\ResourceConnection;
  9. use Magento\Framework\EntityManager\MetadataPool;
  10. /**
  11. * Abstract action reindex class
  12. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  13. */
  14. abstract class AbstractAction
  15. {
  16. /**
  17. * Suffix for value field on composite attributes
  18. *
  19. * @var string
  20. */
  21. protected $_valueFieldSuffix = '_value';
  22. /**
  23. * Suffix for drop table (uses on flat table rename)
  24. *
  25. * @var string
  26. */
  27. protected $_tableDropSuffix = '_drop_indexer';
  28. /**
  29. * @var \Magento\Store\Model\StoreManagerInterface
  30. */
  31. protected $_storeManager;
  32. /**
  33. * @var \Magento\Catalog\Helper\Product\Flat\Indexer
  34. */
  35. protected $_productIndexerHelper;
  36. /**
  37. * @var \Magento\Framework\DB\Adapter\AdapterInterface
  38. */
  39. protected $_connection;
  40. /**
  41. * @var \Magento\Catalog\Model\Product\Type
  42. */
  43. protected $_productType;
  44. /**
  45. * Existing flat tables flags pool
  46. *
  47. * @var array
  48. */
  49. protected $_flatTablesExist = [];
  50. /**
  51. * List of product types available in installation
  52. *
  53. * @var array
  54. */
  55. protected $_productTypes = [];
  56. /**
  57. * @var TableBuilder
  58. */
  59. protected $_tableBuilder;
  60. /**
  61. * @var FlatTableBuilder
  62. */
  63. protected $_flatTableBuilder;
  64. /**
  65. * @var MetadataPool
  66. */
  67. private $metadataPool;
  68. /**
  69. * @param \Magento\Framework\App\ResourceConnection $resource
  70. * @param \Magento\Store\Model\StoreManagerInterface $storeManager
  71. * @param \Magento\Catalog\Helper\Product\Flat\Indexer $productHelper
  72. * @param \Magento\Catalog\Model\Product\Type $productType
  73. * @param TableBuilder $tableBuilder
  74. * @param FlatTableBuilder $flatTableBuilder
  75. */
  76. public function __construct(
  77. \Magento\Framework\App\ResourceConnection $resource,
  78. \Magento\Store\Model\StoreManagerInterface $storeManager,
  79. \Magento\Catalog\Helper\Product\Flat\Indexer $productHelper,
  80. \Magento\Catalog\Model\Product\Type $productType,
  81. TableBuilder $tableBuilder,
  82. FlatTableBuilder $flatTableBuilder
  83. ) {
  84. $this->_storeManager = $storeManager;
  85. $this->_productIndexerHelper = $productHelper;
  86. $this->_productType = $productType;
  87. $this->_connection = $resource->getConnection();
  88. $this->_tableBuilder = $tableBuilder;
  89. $this->_flatTableBuilder = $flatTableBuilder;
  90. }
  91. /**
  92. * Execute action for given ids
  93. *
  94. * @param array|int $ids
  95. * @return \Magento\Catalog\Model\Indexer\Product\Flat\AbstractAction
  96. */
  97. abstract public function execute($ids);
  98. /**
  99. * Return temporary table name by regular table name
  100. *
  101. * @param string $tableName
  102. * @return string
  103. */
  104. protected function _getTemporaryTableName($tableName)
  105. {
  106. return sprintf('%s_tmp_indexer', $tableName);
  107. }
  108. /**
  109. * Drop temporary tables created by reindex process
  110. *
  111. * @param array $tablesList
  112. * @param int|string $storeId
  113. * @return void
  114. * @SuppressWarnings(PHPMD.UnusedLocalVariable)
  115. */
  116. protected function _cleanOnFailure(array $tablesList, $storeId)
  117. {
  118. foreach ($tablesList as $table => $columns) {
  119. $this->_connection->dropTemporaryTable($table);
  120. }
  121. $tableName = $this->_getTemporaryTableName($this->_productIndexerHelper->getFlatTableName($storeId));
  122. $this->_connection->dropTable($tableName);
  123. }
  124. /**
  125. * Rebuild catalog flat index from scratch
  126. *
  127. * @param int $storeId
  128. * @param array $changedIds
  129. * @return void
  130. * @throws \Exception
  131. */
  132. protected function _reindex($storeId, array $changedIds = [])
  133. {
  134. try {
  135. $this->_tableBuilder->build($storeId, $changedIds, $this->_valueFieldSuffix);
  136. $this->_flatTableBuilder->build(
  137. $storeId,
  138. $changedIds,
  139. $this->_valueFieldSuffix,
  140. $this->_tableDropSuffix,
  141. true
  142. );
  143. $this->_updateRelationProducts($storeId, $changedIds);
  144. $this->_cleanRelationProducts($storeId);
  145. } catch (\Exception $e) {
  146. $attributes = $this->_productIndexerHelper->getAttributes();
  147. $eavAttributes = $this->_productIndexerHelper->getTablesStructure($attributes);
  148. $this->_cleanOnFailure($eavAttributes, $storeId);
  149. throw $e;
  150. }
  151. }
  152. /**
  153. * Retrieve Product Type Instances
  154. * as key - type code, value - instance model
  155. *
  156. * @return array
  157. */
  158. protected function _getProductTypeInstances()
  159. {
  160. if ($this->_productTypes === null) {
  161. $this->_productTypes = [];
  162. $productEmulator = new \Magento\Framework\DataObject();
  163. foreach (array_keys($this->_productType->getTypes()) as $typeId) {
  164. $productEmulator->setTypeId($typeId);
  165. $this->_productTypes[$typeId] = $this->_productType->factory($productEmulator);
  166. }
  167. }
  168. return $this->_productTypes;
  169. }
  170. /**
  171. * Update relation products
  172. *
  173. * @param int $storeId
  174. * @param int|array $productIds Update child product(s) only
  175. * @return \Magento\Catalog\Model\Indexer\Product\Flat\AbstractAction
  176. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  177. */
  178. protected function _updateRelationProducts($storeId, $productIds = null)
  179. {
  180. if (!$this->_productIndexerHelper->isAddChildData() || !$this->_isFlatTableExists($storeId)) {
  181. return $this;
  182. }
  183. $metadata = $this->getMetadataPool()->getMetadata(ProductInterface::class);
  184. foreach ($this->_getProductTypeInstances() as $typeInstance) {
  185. /** @var $typeInstance \Magento\Catalog\Model\Product\Type\AbstractType */
  186. if (!$typeInstance->isComposite(null)) {
  187. continue;
  188. }
  189. $relation = $typeInstance->getRelationInfo();
  190. if ($relation && $relation->getTable() && $relation->getParentFieldName() && $relation->getChildFieldName()
  191. ) {
  192. $columns = $this->_productIndexerHelper->getFlatColumns();
  193. $fieldList = array_keys($columns);
  194. unset($columns['entity_id']);
  195. unset($columns['child_id']);
  196. unset($columns['is_child']);
  197. /** @var $select \Magento\Framework\DB\Select */
  198. $select = $this->_connection->select()->from(
  199. ['t' => $this->_productIndexerHelper->getTable($relation->getTable())],
  200. [$relation->getChildFieldName(), new \Zend_Db_Expr('1')]
  201. )->join(
  202. ['entity_table' => $this->_connection->getTableName('catalog_product_entity')],
  203. 'entity_table.' . $metadata->getLinkField() . 't.' . $relation->getParentFieldName(),
  204. [$relation->getParentFieldName() => 'entity_table.entity_id']
  205. )->join(
  206. ['e' => $this->_productIndexerHelper->getFlatTableName($storeId)],
  207. "e.entity_id = t.{$relation->getChildFieldName()}",
  208. array_keys($columns)
  209. );
  210. if ($relation->getWhere() !== null) {
  211. $select->where($relation->getWhere());
  212. }
  213. if ($productIds !== null) {
  214. $cond = [
  215. $this->_connection->quoteInto("{$relation->getChildFieldName()} IN(?)", $productIds),
  216. $this->_connection->quoteInto("entity_table.entity_id IN(?)", $productIds),
  217. ];
  218. $select->where(implode(' OR ', $cond));
  219. }
  220. $sql = $select->insertFromSelect($this->_productIndexerHelper->getFlatTableName($storeId), $fieldList);
  221. $this->_connection->query($sql);
  222. }
  223. }
  224. return $this;
  225. }
  226. /**
  227. * Clean unused relation products
  228. *
  229. * @param int $storeId
  230. * @return \Magento\Catalog\Model\Indexer\Product\Flat\AbstractAction
  231. */
  232. protected function _cleanRelationProducts($storeId)
  233. {
  234. if (!$this->_productIndexerHelper->isAddChildData()) {
  235. return $this;
  236. }
  237. $metadata = $this->getMetadataPool()->getMetadata(ProductInterface::class);
  238. foreach ($this->_getProductTypeInstances() as $typeInstance) {
  239. /** @var $typeInstance \Magento\Catalog\Model\Product\Type\AbstractType */
  240. if (!$typeInstance->isComposite(null)) {
  241. continue;
  242. }
  243. $relation = $typeInstance->getRelationInfo();
  244. if ($relation && $relation->getTable() && $relation->getParentFieldName() && $relation->getChildFieldName()
  245. ) {
  246. $select = $this->_connection->select()->distinct(
  247. true
  248. )->from(
  249. ['t' => $this->_productIndexerHelper->getTable($relation->getTable())],
  250. []
  251. )->join(
  252. ['entity_table' => $this->_connection->getTableName('catalog_product_entity')],
  253. 'entity_table.' . $metadata->getLinkField() . 't.' . $relation->getParentFieldName(),
  254. [$relation->getParentFieldName() => 'entity_table.entity_id']
  255. );
  256. $joinLeftCond = [
  257. "e.entity_id = entity_table.entity_id",
  258. "e.child_id = t.{$relation->getChildFieldName()}",
  259. ];
  260. if ($relation->getWhere() !== null) {
  261. $select->where($relation->getWhere());
  262. $joinLeftCond[] = $relation->getWhere();
  263. }
  264. $entitySelect = new \Zend_Db_Expr($select->__toString());
  265. /** @var $select \Magento\Framework\DB\Select */
  266. $select = $this->_connection->select()->from(
  267. ['e' => $this->_productIndexerHelper->getFlatTableName($storeId)],
  268. null
  269. )->joinLeft(
  270. ['t' => $this->_productIndexerHelper->getTable($relation->getTable())],
  271. implode(' AND ', $joinLeftCond),
  272. []
  273. )->where(
  274. 'e.is_child = ?',
  275. 1
  276. )->where(
  277. 'e.entity_id IN(?)',
  278. $entitySelect
  279. )->where(
  280. "t.{$relation->getChildFieldName()} IS NULL"
  281. );
  282. $sql = $select->deleteFromSelect('e');
  283. $this->_connection->query($sql);
  284. }
  285. }
  286. return $this;
  287. }
  288. /**
  289. * Check is flat table for store exists
  290. *
  291. * @param int $storeId
  292. * @return bool
  293. */
  294. protected function _isFlatTableExists($storeId)
  295. {
  296. if (!isset($this->_flatTablesExist[$storeId])) {
  297. $tableName = $this->_productIndexerHelper->getFlatTableName($storeId);
  298. $isTableExists = $this->_connection->isTableExists($tableName);
  299. $this->_flatTablesExist[$storeId] = $isTableExists ? true : false;
  300. }
  301. return $this->_flatTablesExist[$storeId];
  302. }
  303. /**
  304. * @return \Magento\Framework\EntityManager\MetadataPool
  305. */
  306. private function getMetadataPool()
  307. {
  308. if (null === $this->metadataPool) {
  309. $this->metadataPool = \Magento\Framework\App\ObjectManager::getInstance()
  310. ->get(\Magento\Framework\EntityManager\MetadataPool::class);
  311. }
  312. return $this->metadataPool;
  313. }
  314. }