PageRenderTime 45ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/Source.php

https://gitlab.com/svillegas/magento2
PHP | 351 lines | 236 code | 30 blank | 85 comment | 17 complexity | b246c6c9877d9b5e6efe71ed394295d2 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\ResourceModel\Product\Indexer\Eav;
  7. use Magento\Catalog\Model\Product\Attribute\Source\Status as ProductStatus;
  8. use Magento\Catalog\Api\Data\ProductInterface;
  9. /**
  10. * Catalog Product Eav Select and Multiply Select Attributes Indexer resource model
  11. *
  12. * @author Magento Core Team <core@magentocommerce.com>
  13. */
  14. class Source extends AbstractEav
  15. {
  16. /**
  17. * Catalog resource helper
  18. *
  19. * @var \Magento\Catalog\Model\ResourceModel\Helper
  20. */
  21. protected $_resourceHelper;
  22. /**
  23. * Construct
  24. *
  25. * @param \Magento\Framework\Model\ResourceModel\Db\Context $context
  26. * @param \Magento\Framework\Indexer\Table\StrategyInterface $tableStrategy
  27. * @param \Magento\Eav\Model\Config $eavConfig
  28. * @param \Magento\Framework\Event\ManagerInterface $eventManager
  29. * @param \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper
  30. * @param string $connectionName
  31. */
  32. public function __construct(
  33. \Magento\Framework\Model\ResourceModel\Db\Context $context,
  34. \Magento\Framework\Indexer\Table\StrategyInterface $tableStrategy,
  35. \Magento\Eav\Model\Config $eavConfig,
  36. \Magento\Framework\Event\ManagerInterface $eventManager,
  37. \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper,
  38. $connectionName = null
  39. ) {
  40. $this->_resourceHelper = $resourceHelper;
  41. parent::__construct($context, $tableStrategy, $eavConfig, $eventManager, $connectionName);
  42. }
  43. /**
  44. * Initialize connection and define main index table
  45. *
  46. * @return void
  47. */
  48. protected function _construct()
  49. {
  50. $this->_init('catalog_product_index_eav', 'entity_id');
  51. }
  52. /**
  53. * Retrieve indexable eav attribute ids
  54. *
  55. * @param bool $multiSelect
  56. * @return array
  57. */
  58. protected function _getIndexableAttributes($multiSelect)
  59. {
  60. $select = $this->getConnection()->select()->from(
  61. ['ca' => $this->getTable('catalog_eav_attribute')],
  62. 'attribute_id'
  63. )->join(
  64. ['ea' => $this->getTable('eav_attribute')],
  65. 'ca.attribute_id = ea.attribute_id',
  66. []
  67. )->where(
  68. $this->_getIndexableAttributesCondition()
  69. );
  70. if ($multiSelect == true) {
  71. $select->where('ea.backend_type = ?', 'varchar')->where('ea.frontend_input = ?', 'multiselect');
  72. } else {
  73. $select->where('ea.backend_type = ?', 'int')->where('ea.frontend_input = ?', 'select');
  74. }
  75. return $this->getConnection()->fetchCol($select);
  76. }
  77. /**
  78. * Prepare data index for indexable attributes
  79. *
  80. * @param array $entityIds the entity ids limitation
  81. * @param int $attributeId the attribute id limitation
  82. * @return $this
  83. */
  84. protected function _prepareIndex($entityIds = null, $attributeId = null)
  85. {
  86. $this->_prepareSelectIndex($entityIds, $attributeId);
  87. $this->_prepareMultiselectIndex($entityIds, $attributeId);
  88. return $this;
  89. }
  90. /**
  91. * Prepare data index for indexable select attributes
  92. *
  93. * @param array $entityIds the entity ids limitation
  94. * @param int $attributeId the attribute id limitation
  95. * @return $this
  96. * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
  97. */
  98. protected function _prepareSelectIndex($entityIds = null, $attributeId = null)
  99. {
  100. $connection = $this->getConnection();
  101. $idxTable = $this->getIdxTable();
  102. // prepare select attributes
  103. if ($attributeId === null) {
  104. $attrIds = $this->_getIndexableAttributes(false);
  105. } else {
  106. $attrIds = [$attributeId];
  107. }
  108. if (!$attrIds) {
  109. return $this;
  110. }
  111. $productIdField = $this->getMetadataPool()->getMetadata(ProductInterface::class)->getLinkField();
  112. /**@var $subSelect \Magento\Framework\DB\Select*/
  113. $subSelect = $connection->select()->from(
  114. ['s' => $this->getTable('store')],
  115. ['store_id', 'website_id']
  116. )->joinLeft(
  117. ['dd' => $this->getTable('catalog_product_entity_int')],
  118. 'dd.store_id = 0',
  119. ['attribute_id']
  120. )->joinLeft(
  121. ['ds' => $this->getTable('catalog_product_entity_int')],
  122. "ds.store_id = s.store_id AND ds.attribute_id = dd.attribute_id AND " .
  123. "ds.{$productIdField} = dd.{$productIdField}",
  124. ['value' => new \Zend_Db_Expr('COALESCE(ds.value, dd.value)')]
  125. )->joinLeft(
  126. ['d2d' => $this->getTable('catalog_product_entity_int')],
  127. sprintf(
  128. "d2d.store_id = 0 AND d2d.{$productIdField} = dd.{$productIdField} AND d2d.attribute_id = %s",
  129. $this->_eavConfig->getAttribute(\Magento\Catalog\Model\Product::ENTITY, 'status')->getId()
  130. ),
  131. []
  132. )->joinLeft(
  133. ['d2s' => $this->getTable('catalog_product_entity_int')],
  134. "d2s.store_id = s.store_id AND d2s.attribute_id = d2d.attribute_id AND " .
  135. "d2s.{$productIdField} = d2d.{$productIdField}",
  136. []
  137. )->joinLeft(
  138. ['cpe' => $this->getTable('catalog_product_entity')],
  139. "cpe.{$productIdField} = dd.{$productIdField}",
  140. array_unique([$productIdField, 'entity_id'])
  141. )->where(
  142. 's.store_id != 0'
  143. )->where(
  144. '(ds.value IS NOT NULL OR dd.value IS NOT NULL)'
  145. )->where(
  146. (new \Zend_Db_Expr('COALESCE(d2s.value, d2d.value)')) . ' = ' . ProductStatus::STATUS_ENABLED
  147. )->distinct(true);
  148. if ($entityIds !== null) {
  149. $subSelect->where('cpe.entity_id IN(?)', $entityIds);
  150. }
  151. $ifNullSql = $connection->getIfNullSql('pis.value', 'pid.value');
  152. /**@var $select \Magento\Framework\DB\Select*/
  153. $select = $connection->select()->distinct(true)->from(
  154. ['pid' => new \Zend_Db_Expr(sprintf('(%s)', $subSelect->assemble()))],
  155. []
  156. )->joinLeft(
  157. ['pis' => $this->getTable('catalog_product_entity_int')],
  158. "pis.{$productIdField} = pid.{$productIdField}"
  159. .' AND pis.attribute_id = pid.attribute_id AND pis.store_id = pid.store_id',
  160. []
  161. )->columns(
  162. [
  163. 'pid.entity_id',
  164. 'pid.attribute_id',
  165. 'pid.store_id',
  166. 'value' => $ifNullSql,
  167. ]
  168. )->where(
  169. 'pid.attribute_id IN(?)',
  170. $attrIds
  171. );
  172. $select->where($ifNullSql . ' IS NOT NULL');
  173. /**
  174. * Exclude attribute values that contains NULL
  175. */
  176. $select->where('NOT(pis.value IS NULL AND pis.value_id IS NOT NULL)');
  177. /**
  178. * Add additional external limitation
  179. */
  180. $this->_eventManager->dispatch(
  181. 'prepare_catalog_product_index_select',
  182. [
  183. 'select' => $select,
  184. 'entity_field' => new \Zend_Db_Expr('pid.entity_id'),
  185. 'website_field' => new \Zend_Db_Expr('pid.website_id'),
  186. 'store_field' => new \Zend_Db_Expr('pid.store_id')
  187. ]
  188. );
  189. $query = $select->insertFromSelect($idxTable);
  190. $connection->query($query);
  191. return $this;
  192. }
  193. /**
  194. * Prepare data index for indexable multiply select attributes
  195. *
  196. * @param array $entityIds the entity ids limitation
  197. * @param int $attributeId the attribute id limitation
  198. * @return $this
  199. */
  200. protected function _prepareMultiselectIndex($entityIds = null, $attributeId = null)
  201. {
  202. $connection = $this->getConnection();
  203. // prepare multiselect attributes
  204. if ($attributeId === null) {
  205. $attrIds = $this->_getIndexableAttributes(true);
  206. } else {
  207. $attrIds = [$attributeId];
  208. }
  209. if (!$attrIds) {
  210. return $this;
  211. }
  212. $productIdField = $this->getMetadataPool()->getMetadata(ProductInterface::class)->getLinkField();
  213. // load attribute options
  214. $options = [];
  215. $select = $connection->select()->from(
  216. $this->getTable('eav_attribute_option'),
  217. ['attribute_id', 'option_id']
  218. )->where('attribute_id IN(?)', $attrIds);
  219. $query = $select->query();
  220. while ($row = $query->fetch()) {
  221. $options[$row['attribute_id']][$row['option_id']] = true;
  222. }
  223. // prepare get multiselect values query
  224. $productValueExpression = $connection->getCheckSql('pvs.value_id > 0', 'pvs.value', 'pvd.value');
  225. $select = $connection->select()->from(
  226. ['pvd' => $this->getTable('catalog_product_entity_varchar')],
  227. [$productIdField, 'attribute_id']
  228. )->join(
  229. ['cs' => $this->getTable('store')],
  230. '',
  231. ['store_id']
  232. )->joinLeft(
  233. ['pvs' => $this->getTable('catalog_product_entity_varchar')],
  234. "pvs.{$productIdField} = pvd.{$productIdField} AND pvs.attribute_id = pvd.attribute_id"
  235. . ' AND pvs.store_id=cs.store_id',
  236. ['value' => $productValueExpression]
  237. )->joinLeft(
  238. ['cpe' => $this->getTable('catalog_product_entity')],
  239. "cpe.{$productIdField} = pvd.{$productIdField}",
  240. ['entity_id']
  241. )->where(
  242. 'pvd.store_id=?',
  243. $connection->getIfNullSql('pvs.store_id', \Magento\Store\Model\Store::DEFAULT_STORE_ID)
  244. )->where(
  245. 'cs.store_id!=?',
  246. \Magento\Store\Model\Store::DEFAULT_STORE_ID
  247. )->where(
  248. 'pvd.attribute_id IN(?)',
  249. $attrIds
  250. )->where(
  251. 'cpe.entity_id IS NOT NULL'
  252. );
  253. $statusCond = $connection->quoteInto('=?', ProductStatus::STATUS_ENABLED);
  254. $this->_addAttributeToSelect($select, 'status', "pvd.{$productIdField}", 'cs.store_id', $statusCond);
  255. if ($entityIds !== null) {
  256. $select->where('cpe.entity_id IN(?)', $entityIds);
  257. }
  258. /**
  259. * Add additional external limitation
  260. */
  261. $this->_eventManager->dispatch(
  262. 'prepare_catalog_product_index_select',
  263. [
  264. 'select' => $select,
  265. 'entity_field' => new \Zend_Db_Expr('cpe.entity_id'),
  266. 'website_field' => new \Zend_Db_Expr('cs.website_id'),
  267. 'store_field' => new \Zend_Db_Expr('cs.store_id')
  268. ]
  269. );
  270. $i = 0;
  271. $data = [];
  272. $query = $select->query();
  273. while ($row = $query->fetch()) {
  274. $values = explode(',', $row['value']);
  275. foreach ($values as $valueId) {
  276. if (isset($options[$row['attribute_id']][$valueId])) {
  277. $data[] = [$row['entity_id'], $row['attribute_id'], $row['store_id'], $valueId];
  278. $i++;
  279. if ($i % 10000 == 0) {
  280. $this->_saveIndexData($data);
  281. $data = [];
  282. }
  283. }
  284. }
  285. }
  286. $this->_saveIndexData($data);
  287. unset($options);
  288. unset($data);
  289. return $this;
  290. }
  291. /**
  292. * Save a data to temporary source index table
  293. *
  294. * @param array $data
  295. * @return $this
  296. */
  297. protected function _saveIndexData(array $data)
  298. {
  299. if (!$data) {
  300. return $this;
  301. }
  302. $connection = $this->getConnection();
  303. $connection->insertArray(
  304. $this->getIdxTable(),
  305. ['entity_id', 'attribute_id', 'store_id', 'value'],
  306. $data
  307. );
  308. return $this;
  309. }
  310. /**
  311. * Retrieve temporary source index table name
  312. *
  313. * @param string|null $table
  314. * @return string
  315. * @SuppressWarnings(PHPMD.UnusedFormalParameter)
  316. */
  317. public function getIdxTable($table = null)
  318. {
  319. return $this->tableStrategy->getTableName('catalog_product_index_eav');
  320. }
  321. }