PageRenderTime 28ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/app/code/core/Mage/CatalogInventory/Model/Resource/Indexer/Stock.php

https://bitbucket.org/kdms/sh-magento
PHP | 336 lines | 195 code | 36 blank | 105 comment | 17 complexity | a17bc800999c37d8a877f8b31c2648bb MD5 | raw file
  1. <?php
  2. /**
  3. * Magento Enterprise Edition
  4. *
  5. * NOTICE OF LICENSE
  6. *
  7. * This source file is subject to the Magento Enterprise Edition License
  8. * that is bundled with this package in the file LICENSE_EE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://www.magentocommerce.com/license/enterprise-edition
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@magentocommerce.com so we can send you a copy immediately.
  14. *
  15. * DISCLAIMER
  16. *
  17. * Do not edit or add to this file if you wish to upgrade Magento to newer
  18. * versions in the future. If you wish to customize Magento for your
  19. * needs please refer to http://www.magentocommerce.com for more information.
  20. *
  21. * @category Mage
  22. * @package Mage_CatalogInventory
  23. * @copyright Copyright (c) 2012 Magento Inc. (http://www.magentocommerce.com)
  24. * @license http://www.magentocommerce.com/license/enterprise-edition
  25. */
  26. /**
  27. * CatalogInventory Stock Status Indexer Resource Model
  28. *
  29. * @category Mage
  30. * @package Mage_CatalogInventory
  31. * @author Magento Core Team <core@magentocommerce.com>
  32. */
  33. class Mage_CatalogInventory_Model_Resource_Indexer_Stock extends Mage_Catalog_Model_Resource_Product_Indexer_Abstract
  34. {
  35. /**
  36. * Stock Indexer models per product type
  37. * Sorted by priority
  38. *
  39. * @var array
  40. */
  41. protected $_indexers;
  42. /**
  43. * Default Stock Indexer resource model name
  44. *
  45. * @var string
  46. */
  47. protected $_defaultIndexer = 'cataloginventory/indexer_stock_default';
  48. /**
  49. * Initialize connection and define main table
  50. *
  51. */
  52. protected function _construct()
  53. {
  54. $this->_init('cataloginventory/stock_status', 'product_id');
  55. }
  56. /**
  57. * Process stock item save action
  58. *
  59. * @param Mage_Index_Model_Event $event
  60. * @return Mage_CatalogInventory_Model_Resource_Indexer_Stock
  61. */
  62. public function cataloginventoryStockItemSave(Mage_Index_Model_Event $event)
  63. {
  64. $data = $event->getNewData();
  65. if (empty($data['product_id'])) {
  66. return $this;
  67. }
  68. $productId = $data['product_id'];
  69. $this->reindexProducts($productId);
  70. return $this;
  71. }
  72. /**
  73. * Refresh stock index for specific product ids
  74. *
  75. * @param array $productIds
  76. * @return Mage_CatalogInventory_Model_Resource_Indexer_Stock
  77. */
  78. public function reindexProducts($productIds)
  79. {
  80. $adapter = $this->_getWriteAdapter();
  81. if (!is_array($productIds)) {
  82. $productIds = array($productIds);
  83. }
  84. $parentIds = $this->getRelationsByChild($productIds);
  85. if ($parentIds) {
  86. $processIds = array_merge($parentIds, $productIds);
  87. } else {
  88. $processIds = $productIds;
  89. }
  90. // retrieve product types by processIds
  91. $select = $adapter->select()
  92. ->from($this->getTable('catalog/product'), array('entity_id', 'type_id'))
  93. ->where('entity_id IN(?)', $processIds);
  94. $pairs = $adapter->fetchPairs($select);
  95. $byType = array();
  96. foreach ($pairs as $productId => $typeId) {
  97. $byType[$typeId][$productId] = $productId;
  98. }
  99. $adapter->beginTransaction();
  100. try {
  101. $indexers = $this->_getTypeIndexers();
  102. foreach ($indexers as $indexer) {
  103. if (isset($byType[$indexer->getTypeId()])) {
  104. $indexer->reindexEntity($byType[$indexer->getTypeId()]);
  105. }
  106. }
  107. } catch (Exception $e) {
  108. $adapter->rollback();
  109. throw $e;
  110. }
  111. $adapter->commit();
  112. return $this;
  113. }
  114. /**
  115. * Processing parent products after child product deleted
  116. *
  117. * @param Mage_Index_Model_Event $event
  118. * @return Mage_CatalogInventory_Model_Resource_Indexer_Stock
  119. */
  120. public function catalogProductDelete(Mage_Index_Model_Event $event)
  121. {
  122. $data = $event->getNewData();
  123. if (empty($data['reindex_stock_parent_ids'])) {
  124. return $this;
  125. }
  126. $adapter = $this->_getWriteAdapter();
  127. $parentIds = array();
  128. foreach ($data['reindex_stock_parent_ids'] as $parentId => $parentType) {
  129. $parentIds[$parentType][$parentId] = $parentId;
  130. }
  131. $adapter->beginTransaction();
  132. try {
  133. foreach ($parentIds as $parentType => $entityIds) {
  134. $this->_getIndexer($parentType)->reindexEntity($entityIds);
  135. }
  136. } catch (Exception $e) {
  137. $adapter->rollback();
  138. throw $e;
  139. }
  140. $adapter->commit();
  141. return $this;
  142. }
  143. /**
  144. * Process product mass update action
  145. *
  146. * @param Mage_Index_Model_Event $event
  147. * @return Mage_CatalogInventory_Model_Resource_Indexer_Stock
  148. */
  149. public function catalogProductMassAction(Mage_Index_Model_Event $event)
  150. {
  151. $data = $event->getNewData();
  152. if (empty($data['reindex_stock_product_ids'])) {
  153. return $this;
  154. }
  155. $adapter = $this->_getWriteAdapter();
  156. $processIds = $data['reindex_stock_product_ids'];
  157. $select = $adapter->select()
  158. ->from($this->getTable('catalog/product'), 'COUNT(*)');
  159. $pCount = $adapter->fetchOne($select);
  160. // if affected more 30% of all products - run reindex all products
  161. if ($pCount * 0.3 < count($processIds)) {
  162. return $this->reindexAll();
  163. }
  164. // calculate relations
  165. $select = $adapter->select()
  166. ->from($this->getTable('catalog/product_relation'), 'COUNT(DISTINCT parent_id)')
  167. ->where('child_id IN(?)', $processIds);
  168. $aCount = $adapter->fetchOne($select);
  169. $select = $adapter->select()
  170. ->from($this->getTable('catalog/product_relation'), 'COUNT(DISTINCT child_id)')
  171. ->where('parent_id IN(?)', $processIds);
  172. $bCount = $adapter->fetchOne($select);
  173. // if affected with relations more 30% of all products - run reindex all products
  174. if ($pCount * 0.3 < count($processIds) + $aCount + $bCount) {
  175. return $this->reindexAll();
  176. }
  177. // retrieve affected parent relation products
  178. $parentIds = $this->getRelationsByChild($processIds);
  179. if ($parentIds) {
  180. $processIds = array_merge($processIds, $parentIds);
  181. }
  182. // retrieve products types
  183. $select = $adapter->select()
  184. ->from($this->getTable('catalog/product'), array('entity_id', 'type_id'))
  185. ->where('entity_id IN(?)', $processIds);
  186. $query = $select->query(Zend_Db::FETCH_ASSOC);
  187. $byType = array();
  188. while ($row = $query->fetch()) {
  189. $byType[$row['type_id']][] = $row['entity_id'];
  190. }
  191. $adapter->beginTransaction();
  192. try {
  193. $indexers = $this->_getTypeIndexers();
  194. foreach ($indexers as $indexer) {
  195. if (!empty($byType[$indexer->getTypeId()])) {
  196. $indexer->reindexEntity($byType[$indexer->getTypeId()]);
  197. }
  198. }
  199. } catch (Exception $e) {
  200. $adapter->rollback();
  201. throw $e;
  202. }
  203. $adapter->commit();
  204. return $this;
  205. }
  206. /**
  207. * Rebuild all index data
  208. *
  209. * @return Mage_CatalogInventory_Model_Resource_Indexer_Stock
  210. */
  211. public function reindexAll()
  212. {
  213. $this->useIdxTable(true);
  214. $this->beginTransaction();
  215. try {
  216. $this->clearTemporaryIndexTable();
  217. foreach ($this->_getTypeIndexers() as $indexer) {
  218. $indexer->reindexAll();
  219. }
  220. $this->syncData();
  221. $this->commit();
  222. } catch (Exception $e) {
  223. $this->rollBack();
  224. throw $e;
  225. }
  226. return $this;
  227. }
  228. /**
  229. * Retrieve Stock Indexer Models per Product Type
  230. *
  231. * @return array
  232. */
  233. protected function _getTypeIndexers()
  234. {
  235. if (is_null($this->_indexers)) {
  236. $this->_indexers = array();
  237. $types = Mage::getSingleton('catalog/product_type')->getTypesByPriority();
  238. foreach ($types as $typeId => $typeInfo) {
  239. if (isset($typeInfo['stock_indexer'])) {
  240. $modelName = $typeInfo['stock_indexer'];
  241. } else {
  242. $modelName = $this->_defaultIndexer;
  243. }
  244. $isComposite = !empty($typeInfo['composite']);
  245. $indexer = Mage::getResourceModel($modelName)
  246. ->setTypeId($typeId)
  247. ->setIsComposite($isComposite);
  248. $this->_indexers[$typeId] = $indexer;
  249. }
  250. }
  251. return $this->_indexers;
  252. }
  253. /**
  254. * Retrieve Stock indexer by Product Type
  255. *
  256. * @param string $productTypeId
  257. * @return Mage_CatalogInventory_Model_Resource_Indexer_Stock_Interface
  258. */
  259. protected function _getIndexer($productTypeId)
  260. {
  261. $types = $this->_getTypeIndexers();
  262. if (!isset($types[$productTypeId])) {
  263. Mage::throwException(Mage::helper('catalog')->__('Unsupported product type "%s".', $productTypeId));
  264. }
  265. return $types[$productTypeId];
  266. }
  267. /**
  268. * Retrieve parent ids and types by child id
  269. * Return array with key product_id and value as product type id
  270. *
  271. * @param int $childId
  272. * @return array
  273. */
  274. public function getProductParentsByChild($childId)
  275. {
  276. $write = $this->_getWriteAdapter();
  277. $select = $write->select()
  278. ->from(array('l' => $this->getTable('catalog/product_relation')), array('parent_id'))
  279. ->join(
  280. array('e' => $this->getTable('catalog/product')),
  281. 'l.parent_id=e.entity_id',
  282. array('e.type_id')
  283. )
  284. ->where('l.child_id = :child_id');
  285. return $write->fetchPairs($select, array(':child_id' => $childId));
  286. }
  287. /**
  288. * Retrieve temporary index table name
  289. *
  290. * @param string $table
  291. * @return string
  292. */
  293. public function getIdxTable($table = null)
  294. {
  295. if ($this->useIdxTable()) {
  296. return $this->getTable('cataloginventory/stock_status_indexer_idx');
  297. }
  298. return $this->getTable('cataloginventory/stock_status_indexer_tmp');
  299. }
  300. }