PageRenderTime 118ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

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

https://bitbucket.org/claudiu_marginean/magento-hg-mirror
PHP | 404 lines | 234 code | 47 blank | 123 comment | 20 complexity | ac66277dd0e1ee55dccf9ae34838e14b MD5 | raw file
Possible License(s): CC-BY-SA-3.0, LGPL-2.1, GPL-2.0, WTFPL
  1. <?php
  2. /**
  3. * Magento
  4. *
  5. * NOTICE OF LICENSE
  6. *
  7. * This source file is subject to the Open Software License (OSL 3.0)
  8. * that is bundled with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://opensource.org/licenses/osl-3.0.php
  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) 2010 Magento Inc. (http://www.magentocommerce.com)
  24. * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
  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_Mysql4_Indexer_Stock
  34. extends Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Indexer_Abstract
  35. {
  36. /**
  37. * Stock Indexer models per product type
  38. * Sorted by priority
  39. *
  40. * @var array
  41. */
  42. protected $_indexers;
  43. /**
  44. * Default Stock Indexer resource model name
  45. *
  46. * @var string
  47. */
  48. protected $_defaultIndexer = 'cataloginventory/indexer_stock_default';
  49. /**
  50. * Initialize connection and define main table
  51. *
  52. */
  53. protected function _construct()
  54. {
  55. $this->_init('cataloginventory/stock_status', 'product_id');
  56. }
  57. /**
  58. * Process stock item save action
  59. *
  60. * @param Mage_Index_Model_Event $event
  61. * @return Mage_CatalogInventory_Model_Mysql4_Indexer_Stock
  62. */
  63. public function cataloginventoryStockItemSave(Mage_Index_Model_Event $event)
  64. {
  65. $data = $event->getNewData();
  66. if (empty($data['product_id'])) {
  67. return $this;
  68. }
  69. $productId = $data['product_id'];
  70. $this->reindexProducts($productId);
  71. if (!empty($data['force_reindex_required'])) {
  72. $massObject = new Varien_Object();
  73. $massObject->setAttributesData(array(
  74. 'force_reindex_required' => 1
  75. ));
  76. $massObject->setProductIds(array($productId));
  77. Mage::getSingleton('index/indexer')->processEntityAction(
  78. $massObject, Mage_Catalog_Model_Product::ENTITY, Mage_Index_Model_Event::TYPE_MASS_ACTION
  79. );
  80. }
  81. return $this;
  82. }
  83. /**
  84. * Refresh stock index for specific product ids
  85. *
  86. * @param array $productIds
  87. * @return Mage_CatalogInventory_Model_Mysql4_Indexer_Stock
  88. */
  89. public function reindexProducts($productIds)
  90. {
  91. $adapter = $this->_getWriteAdapter();
  92. if (!is_array($productIds)) {
  93. $productIds = array($productIds);
  94. }
  95. $parentIds = $this->getRelationsByChild($productIds);
  96. if ($parentIds) {
  97. $processIds = array_merge($parentIds, $productIds);
  98. } else {
  99. $processIds = $productIds;
  100. }
  101. // retrieve product types by processIds
  102. $select = $adapter->select()
  103. ->from($this->getTable('catalog/product'), array('entity_id', 'type_id'))
  104. ->where('entity_id IN(?)', $processIds);
  105. $pairs = $adapter->fetchPairs($select);
  106. $byType = array();
  107. foreach ($pairs as $productId => $typeId) {
  108. $byType[$typeId][$productId] = $productId;
  109. }
  110. $adapter->beginTransaction();
  111. try {
  112. $indexers = $this->_getTypeIndexers();
  113. foreach ($indexers as $indexer) {
  114. if (isset($byType[$indexer->getTypeId()])) {
  115. $indexer->reindexEntity($byType[$indexer->getTypeId()]);
  116. }
  117. }
  118. } catch (Exception $e) {
  119. $adapter->rollback();
  120. throw $e;
  121. }
  122. $adapter->commit();
  123. return $this;
  124. }
  125. /**
  126. * Processing parent products after child product deleted
  127. *
  128. * @param Mage_Index_Model_Event $event
  129. * @return Mage_CatalogInventory_Model_Mysql4_Indexer_Stock
  130. */
  131. public function catalogProductDelete(Mage_Index_Model_Event $event)
  132. {
  133. $data = $event->getNewData();
  134. if (empty($data['reindex_stock_parent_ids'])) {
  135. return $this;
  136. }
  137. $adapter = $this->_getWriteAdapter();
  138. $parentIds = array();
  139. foreach ($data['reindex_stock_parent_ids'] as $parentId => $parentType) {
  140. $parentIds[$parentType][$parentId] = $parentId;
  141. }
  142. $adapter->beginTransaction();
  143. try {
  144. foreach ($parentIds as $parentType => $entityIds) {
  145. $this->_getIndexer($parentType)->reindexEntity($entityIds);
  146. }
  147. } catch (Exception $e) {
  148. $adapter->rollback();
  149. throw $e;
  150. }
  151. $adapter->commit();
  152. return $this;
  153. }
  154. /**
  155. * Process product mass update action
  156. *
  157. * @param Mage_Index_Model_Event $event
  158. * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Indexer_Price
  159. */
  160. public function catalogProductMassAction(Mage_Index_Model_Event $event)
  161. {
  162. $data = $event->getNewData();
  163. if (empty($data['reindex_stock_product_ids'])) {
  164. return $this;
  165. }
  166. $adapter = $this->_getWriteAdapter();
  167. $processIds = $data['reindex_stock_product_ids'];
  168. $select = $adapter->select()
  169. ->from($this->getTable('catalog/product'), 'COUNT(*)');
  170. $pCount = $adapter->fetchOne($select);
  171. // if affected more 30% of all products - run reindex all products
  172. if ($pCount * 0.3 < count($processIds)) {
  173. return $this->reindexAll();
  174. }
  175. // calculate relations
  176. $select = $adapter->select()
  177. ->from($this->getTable('catalog/product_relation'), 'COUNT(DISTINCT parent_id)')
  178. ->where('child_id IN(?)', $processIds);
  179. $aCount = $adapter->fetchOne($select);
  180. $select = $adapter->select()
  181. ->from($this->getTable('catalog/product_relation'), 'COUNT(DISTINCT child_id)')
  182. ->where('parent_id IN(?)', $processIds);
  183. $bCount = $adapter->fetchOne($select);
  184. // if affected with relations more 30% of all products - run reindex all products
  185. if ($pCount * 0.3 < count($processIds) + $aCount + $bCount) {
  186. return $this->reindexAll();
  187. }
  188. // retrieve affected parent relation products
  189. $parentIds = $this->getRelationsByChild($processIds);
  190. if ($parentIds) {
  191. $processIds = array_merge($processIds, $parentIds);
  192. }
  193. // retrieve products types
  194. $select = $adapter->select()
  195. ->from($this->getTable('catalog/product'), array('entity_id', 'type_id'))
  196. ->where('entity_id IN(?)', $processIds);
  197. $query = $select->query(Zend_Db::FETCH_ASSOC);
  198. $byType = array();
  199. while ($row = $query->fetch()) {
  200. $byType[$row['type_id']][] = $row['entity_id'];
  201. }
  202. $adapter->beginTransaction();
  203. try {
  204. $indexers = $this->_getTypeIndexers();
  205. foreach ($indexers as $indexer) {
  206. if (!empty($byType[$indexer->getTypeId()])) {
  207. $indexer->reindexEntity($byType[$indexer->getTypeId()]);
  208. }
  209. }
  210. } catch (Exception $e) {
  211. $adapter->rollback();
  212. throw $e;
  213. }
  214. $adapter->commit();
  215. return $this;
  216. }
  217. /**
  218. * Rebuild all index data
  219. *
  220. * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Indexer_Eav
  221. */
  222. public function reindexAll()
  223. {
  224. $this->useIdxTable(true);
  225. $this->clearTemporaryIndexTable();
  226. foreach ($this->_getTypeIndexers() as $indexer) {
  227. $indexer->reindexAll();
  228. }
  229. $this->syncData();
  230. return $this;
  231. }
  232. /**
  233. * Retrieve Stock Indexer Models per Product Type
  234. *
  235. * @return array
  236. */
  237. protected function _getTypeIndexers()
  238. {
  239. if (is_null($this->_indexers)) {
  240. $this->_indexers = array();
  241. $types = Mage::getSingleton('catalog/product_type')->getTypesByPriority();
  242. foreach ($types as $typeId => $typeInfo) {
  243. if (isset($typeInfo['stock_indexer'])) {
  244. $modelName = $typeInfo['stock_indexer'];
  245. } else {
  246. $modelName = $this->_defaultIndexer;
  247. }
  248. $isComposite = !empty($typeInfo['composite']);
  249. $indexer = Mage::getResourceModel($modelName)
  250. ->setTypeId($typeId)
  251. ->setIsComposite($isComposite);
  252. $this->_indexers[$typeId] = $indexer;
  253. }
  254. }
  255. return $this->_indexers;
  256. }
  257. /**
  258. * Retrieve Stock indexer by Product Type
  259. *
  260. * @param string $productTypeId
  261. * @return Mage_CatalogInventory_Model_Mysql4_Indexer_Stock_Interface
  262. */
  263. protected function _getIndexer($productTypeId)
  264. {
  265. $types = $this->_getTypeIndexers();
  266. if (!isset($types[$productTypeId])) {
  267. Mage::throwException(Mage::helper('catalog')->__('Unsupported product type "%s".', $productTypeId));
  268. }
  269. return $types[$productTypeId];
  270. }
  271. /**
  272. * Retrieve parent ids and types by child id
  273. *
  274. * Return array with key product_id and value as product type id
  275. *
  276. * @param int $childId
  277. * @return array
  278. */
  279. public function getProductParentsByChild($childId)
  280. {
  281. $write = $this->_getWriteAdapter();
  282. $select = $write->select()
  283. ->from(array('l' => $this->getTable('catalog/product_relation')), array('parent_id'))
  284. ->join(
  285. array('e' => $this->getTable('catalog/product')),
  286. 'l.parent_id=e.entity_id',
  287. array('e.type_id'))
  288. ->where('l.child_id=?', $childId);
  289. return $write->fetchPairs($select);
  290. }
  291. /**
  292. * Copy relations product index from primary index to temporary index table by parent entity
  293. *
  294. * @deprecated since 1.4.0
  295. * @param array|int $parentIds
  296. * @package array|int $excludeIds
  297. * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Indexer_Price
  298. */
  299. protected function _copyRelationIndexData($parentIds, $excludeIds = null)
  300. {
  301. $write = $this->_getWriteAdapter();
  302. $select = $write->select()
  303. ->from($this->getTable('catalog/product_relation'), array('child_id'))
  304. ->where('parent_id IN(?)', $parentIds);
  305. if (!is_null($excludeIds)) {
  306. $select->where('child_id NOT IN(?)', $excludeIds);
  307. }
  308. $children = $write->fetchCol($select);
  309. if ($children) {
  310. $select = $write->select()
  311. ->from($this->getMainTable())
  312. ->where('product_id IN(?)', $children);
  313. $query = $select->insertFromSelect($this->getIdxTable());
  314. $write->query($query);
  315. }
  316. return $this;
  317. }
  318. /**
  319. * Copy data from temporary index table to main table by defined ids
  320. *
  321. * @deprecated since 1.4.0
  322. * @param array $processIds
  323. * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Indexer_Price
  324. */
  325. protected function _copyIndexDataToMainTable($processIds)
  326. {
  327. $write = $this->_getWriteAdapter();
  328. $write->beginTransaction();
  329. try {
  330. // remove old index
  331. $where = $write->quoteInto('product_id IN(?)', $processIds);
  332. $write->delete($this->getMainTable(), $where);
  333. // remove additional data from index
  334. $where = $write->quoteInto('product_id NOT IN(?)', $processIds);
  335. $write->delete($this->getIdxTable(), $where);
  336. // insert new index
  337. $this->insertFromTable($this->getIdxTable(), $this->getMainTable());
  338. $this->commit();
  339. } catch (Exception $e) {
  340. $this->rollBack();
  341. throw $e;
  342. }
  343. return $this;
  344. }
  345. /**
  346. * Retrieve temporary index table name
  347. *
  348. * @return string
  349. */
  350. public function getIdxTable($table = null)
  351. {
  352. if ($this->useIdxTable()) {
  353. return $this->getTable('cataloginventory/stock_status_indexer_idx');
  354. }
  355. return $this->getTable('cataloginventory/stock_status_indexer_tmp');
  356. }
  357. }