1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25:
26:
27:
28: 29: 30: 31: 32: 33: 34:
35: class Mage_Catalog_Model_Resource_Product_Indexer_Price extends Mage_Index_Model_Resource_Abstract
36: {
37: 38: 39: 40: 41:
42: protected $_defaultPriceIndexer = 'catalog/product_indexer_price_default';
43:
44: 45: 46: 47: 48:
49: protected $_indexers;
50:
51: 52: 53: 54:
55: protected function _construct()
56: {
57: $this->_init('catalog/product_index_price', 'entity_id');
58: }
59:
60: 61: 62: 63: 64: 65: 66:
67: public function getProductParentsByChild($childId)
68: {
69: $write = $this->_getWriteAdapter();
70: $select = $write->select()
71: ->from(array('l' => $this->getTable('catalog/product_relation')), array('parent_id'))
72: ->join(
73: array('e' => $this->getTable('catalog/product')),
74: 'l.parent_id = e.entity_id',
75: array('e.type_id'))
76: ->where('l.child_id = ?', $childId);
77:
78: return $write->fetchPairs($select);
79: }
80:
81: 82: 83: 84: 85: 86: 87:
88: public function catalogProductDelete(Mage_Index_Model_Event $event)
89: {
90: $data = $event->getNewData();
91: if (empty($data['reindex_price_parent_ids'])) {
92: return $this;
93: }
94:
95: $this->clearTemporaryIndexTable();
96:
97: $processIds = array_keys($data['reindex_price_parent_ids']);
98: $parentIds = array();
99: foreach ($data['reindex_price_parent_ids'] as $parentId => $parentType) {
100: $parentIds[$parentType][$parentId] = $parentId;
101: }
102:
103: $this->_copyRelationIndexData($processIds);
104: foreach ($parentIds as $parentType => $entityIds) {
105: $this->_getIndexer($parentType)->reindexEntity($entityIds);
106: }
107:
108: $this->_copyIndexDataToMainTable($parentIds);
109:
110: return $this;
111: }
112:
113: 114: 115: 116: 117: 118: 119:
120: protected function _copyIndexDataToMainTable($processIds)
121: {
122: $write = $this->_getWriteAdapter();
123: $this->beginTransaction();
124: try {
125:
126: $where = $write->quoteInto('entity_id IN(?)', $processIds);
127: $write->delete($this->getMainTable(), $where);
128:
129:
130: $where = $write->quoteInto('entity_id NOT IN(?)', $processIds);
131: $write->delete($this->getIdxTable(), $where);
132:
133:
134: $this->useDisableKeys(false);
135: $this->insertFromTable($this->getIdxTable(), $this->getMainTable());
136: $this->useDisableKeys(true);
137:
138: $this->commit();
139: } catch (Exception $e) {
140: $this->rollBack();
141: throw $e;
142: }
143:
144: return $this;
145: }
146:
147: 148: 149: 150: 151: 152: 153: 154:
155: public function catalogProductSave(Mage_Index_Model_Event $event)
156: {
157: $productId = $event->getEntityPk();
158: $data = $event->getNewData();
159:
160: 161: 162:
163: if (!isset($data['reindex_price'])) {
164: return $this;
165: }
166:
167: $this->clearTemporaryIndexTable();
168: $this->_prepareWebsiteDateTable();
169:
170: $indexer = $this->_getIndexer($data['product_type_id']);
171: $processIds = array($productId);
172: if ($indexer->getIsComposite()) {
173: $this->_copyRelationIndexData($productId);
174: $this->_prepareTierPriceIndex($productId);
175: $this->_prepareGroupPriceIndex($productId);
176: $indexer->reindexEntity($productId);
177: } else {
178: $parentIds = $this->getProductParentsByChild($productId);
179:
180: if ($parentIds) {
181: $processIds = array_merge($processIds, array_keys($parentIds));
182: $this->_copyRelationIndexData(array_keys($parentIds), $productId);
183: $this->_prepareTierPriceIndex($processIds);
184: $this->_prepareGroupPriceIndex($processIds);
185: $indexer->reindexEntity($productId);
186:
187: $parentByType = array();
188: foreach ($parentIds as $parentId => $parentType) {
189: $parentByType[$parentType][$parentId] = $parentId;
190: }
191:
192: foreach ($parentByType as $parentType => $entityIds) {
193: $this->_getIndexer($parentType)->reindexEntity($entityIds);
194: }
195: } else {
196: $this->_prepareTierPriceIndex($productId);
197: $this->_prepareGroupPriceIndex($productId);
198: $indexer->reindexEntity($productId);
199: }
200: }
201:
202: $this->_copyIndexDataToMainTable($processIds);
203:
204: return $this;
205: }
206:
207: 208: 209: 210: 211: 212:
213: public function catalogProductMassAction(Mage_Index_Model_Event $event)
214: {
215: $data = $event->getNewData();
216: if (empty($data['reindex_price_product_ids'])) {
217: return $this;
218: }
219:
220: $processIds = $data['reindex_price_product_ids'];
221:
222: $write = $this->_getWriteAdapter();
223: $select = $write->select()
224: ->from($this->getTable('catalog/product'), 'COUNT(*)');
225: $pCount = $write->fetchOne($select);
226:
227:
228: if ($pCount * 0.3 < count($processIds)) {
229: return $this->reindexAll();
230: }
231:
232:
233: $select = $write->select()
234: ->from($this->getTable('catalog/product_relation'), 'COUNT(DISTINCT parent_id)')
235: ->where('child_id IN(?)', $processIds);
236: $aCount = $write->fetchOne($select);
237: $select = $write->select()
238: ->from($this->getTable('catalog/product_relation'), 'COUNT(DISTINCT child_id)')
239: ->where('parent_id IN(?)', $processIds);
240: $bCount = $write->fetchOne($select);
241:
242:
243: if ($pCount * 0.3 < count($processIds) + $aCount + $bCount) {
244: return $this->reindexAll();
245: }
246: $this->reindexProductIds($processIds);
247: return $this;
248: }
249:
250: 251: 252: 253: 254: 255:
256: public function reindexProductIds($ids)
257: {
258: if (empty($ids)) {
259: return $this;
260: }
261: if (!is_array($ids)) {
262: $ids = array($ids);
263: }
264: $this->clearTemporaryIndexTable();
265: $write = $this->_getWriteAdapter();
266:
267: $select = $write->select()
268: ->from($this->getTable('catalog/product'), array('entity_id', 'type_id'))
269: ->where('entity_id IN(?)', $ids);
270: $pairs = $write->fetchPairs($select);
271: $byType = array();
272: foreach ($pairs as $productId => $productType) {
273: $byType[$productType][$productId] = $productId;
274: }
275:
276: $compositeIds = array();
277: $notCompositeIds = array();
278:
279: foreach ($byType as $productType => $entityIds) {
280: $indexer = $this->_getIndexer($productType);
281: if ($indexer->getIsComposite()) {
282: $compositeIds += $entityIds;
283: } else {
284: $notCompositeIds += $entityIds;
285: }
286: }
287:
288: if (!empty($notCompositeIds)) {
289: $select = $write->select()
290: ->from(
291: array('l' => $this->getTable('catalog/product_relation')),
292: 'parent_id')
293: ->join(
294: array('e' => $this->getTable('catalog/product')),
295: 'e.entity_id = l.parent_id',
296: array('type_id'))
297: ->where('l.child_id IN(?)', $notCompositeIds);
298: $pairs = $write->fetchPairs($select);
299: foreach ($pairs as $productId => $productType) {
300: if (!in_array($productId, $ids)) {
301: $ids[] = $productId;
302: $byType[$productType][$productId] = $productId;
303: $compositeIds[$productId] = $productId;
304: }
305: }
306: }
307:
308: if (!empty($compositeIds)) {
309: $this->_copyRelationIndexData($compositeIds, $notCompositeIds);
310: }
311:
312: $indexers = $this->getTypeIndexers();
313: foreach ($indexers as $indexer) {
314: if (!empty($byType[$indexer->getTypeId()])) {
315: $indexer->reindexEntity($byType[$indexer->getTypeId()]);
316: }
317: }
318:
319: $this->_copyIndexDataToMainTable($ids);
320: return $this;
321: }
322:
323: 324: 325: 326: 327: 328: 329:
330: protected function _getIndexer($productTypeId)
331: {
332: $types = $this->getTypeIndexers();
333: if (!isset($types[$productTypeId])) {
334: Mage::throwException(Mage::helper('catalog')->__('Unsupported product type "%s".', $productTypeId));
335: }
336: return $types[$productTypeId];
337: }
338:
339: 340: 341: 342: 343:
344: public function getTypeIndexers()
345: {
346: if (is_null($this->_indexers)) {
347: $this->_indexers = array();
348: $types = Mage::getSingleton('catalog/product_type')->getTypesByPriority();
349: foreach ($types as $typeId => $typeInfo) {
350: if (isset($typeInfo['price_indexer'])) {
351: $modelName = $typeInfo['price_indexer'];
352: } else {
353: $modelName = $this->_defaultPriceIndexer;
354: }
355: $isComposite = !empty($typeInfo['composite']);
356: $indexer = Mage::getResourceModel($modelName)
357: ->setTypeId($typeId)
358: ->setIsComposite($isComposite);
359:
360: $this->_indexers[$typeId] = $indexer;
361: }
362: }
363:
364: return $this->_indexers;
365: }
366:
367: 368: 369: 370: 371:
372: public function reindexAll()
373: {
374: $this->useIdxTable(true);
375: $this->beginTransaction();
376: try {
377: $this->clearTemporaryIndexTable();
378: $this->_prepareWebsiteDateTable();
379: $this->_prepareTierPriceIndex();
380: $this->_prepareGroupPriceIndex();
381:
382: $indexers = $this->getTypeIndexers();
383: foreach ($indexers as $indexer) {
384:
385: $indexer->reindexAll();
386: }
387:
388: $this->syncData();
389: $this->commit();
390: } catch (Exception $e) {
391: $this->rollBack();
392: throw $e;
393: }
394: return $this;
395: }
396:
397: 398: 399: 400: 401:
402: protected function _getTierPriceIndexTable()
403: {
404: return $this->getTable('catalog/product_index_tier_price');
405: }
406:
407: 408: 409: 410: 411:
412: protected function _getGroupPriceIndexTable()
413: {
414: return $this->getTable('catalog/product_index_group_price');
415: }
416:
417: 418: 419: 420: 421: 422:
423: protected function _prepareTierPriceIndex($entityIds = null)
424: {
425: $write = $this->_getWriteAdapter();
426: $table = $this->_getTierPriceIndexTable();
427: $write->delete($table);
428:
429: $websiteExpression = $write->getCheckSql('tp.website_id = 0', 'ROUND(tp.value * cwd.rate, 4)', 'tp.value');
430: $select = $write->select()
431: ->from(
432: array('tp' => $this->getValueTable('catalog/product', 'tier_price')),
433: array('entity_id'))
434: ->join(
435: array('cg' => $this->getTable('customer/customer_group')),
436: 'tp.all_groups = 1 OR (tp.all_groups = 0 AND tp.customer_group_id = cg.customer_group_id)',
437: array('customer_group_id'))
438: ->join(
439: array('cw' => $this->getTable('core/website')),
440: 'tp.website_id = 0 OR tp.website_id = cw.website_id',
441: array('website_id'))
442: ->join(
443: array('cwd' => $this->_getWebsiteDateTable()),
444: 'cw.website_id = cwd.website_id',
445: array())
446: ->where('cw.website_id != 0')
447: ->columns(new Zend_Db_Expr("MIN({$websiteExpression})"))
448: ->group(array('tp.entity_id', 'cg.customer_group_id', 'cw.website_id'));
449:
450: if (!empty($entityIds)) {
451: $select->where('tp.entity_id IN(?)', $entityIds);
452: }
453:
454: $query = $select->insertFromSelect($table);
455: $write->query($query);
456:
457: return $this;
458: }
459:
460: 461: 462: 463: 464: 465:
466: protected function _prepareGroupPriceIndex($entityIds = null)
467: {
468: $write = $this->_getWriteAdapter();
469: $table = $this->_getGroupPriceIndexTable();
470: $write->delete($table);
471:
472: $websiteExpression = $write->getCheckSql('gp.website_id = 0', 'ROUND(gp.value * cwd.rate, 4)', 'gp.value');
473: $select = $write->select()
474: ->from(
475: array('gp' => $this->getValueTable('catalog/product', 'group_price')),
476: array('entity_id'))
477: ->join(
478: array('cg' => $this->getTable('customer/customer_group')),
479: 'gp.all_groups = 1 OR (gp.all_groups = 0 AND gp.customer_group_id = cg.customer_group_id)',
480: array('customer_group_id'))
481: ->join(
482: array('cw' => $this->getTable('core/website')),
483: 'gp.website_id = 0 OR gp.website_id = cw.website_id',
484: array('website_id'))
485: ->join(
486: array('cwd' => $this->_getWebsiteDateTable()),
487: 'cw.website_id = cwd.website_id',
488: array())
489: ->where('cw.website_id != 0')
490: ->columns(new Zend_Db_Expr("MIN({$websiteExpression})"))
491: ->group(array('gp.entity_id', 'cg.customer_group_id', 'cw.website_id'));
492:
493: if (!empty($entityIds)) {
494: $select->where('gp.entity_id IN(?)', $entityIds);
495: }
496:
497: $query = $select->insertFromSelect($table);
498: $write->query($query);
499:
500: return $this;
501: }
502:
503: 504: 505: 506: 507: 508: 509: 510: 511:
512: protected function _copyRelationIndexData($parentIds, $excludeIds = null)
513: {
514: $write = $this->_getWriteAdapter();
515: $select = $write->select()
516: ->from($this->getTable('catalog/product_relation'), array('child_id'))
517: ->where('parent_id IN(?)', $parentIds);
518: if (!empty($excludeIds)) {
519: $select->where('child_id NOT IN(?)', $excludeIds);
520: }
521:
522: $children = $write->fetchCol($select);
523:
524: if ($children) {
525: $select = $write->select()
526: ->from($this->getMainTable())
527: ->where('entity_id IN(?)', $children);
528: $query = $select->insertFromSelect($this->getIdxTable(), array(), false);
529: $write->query($query);
530: }
531:
532: return $this;
533: }
534:
535: 536: 537: 538: 539:
540: protected function _getWebsiteDateTable()
541: {
542: return $this->getTable('catalog/product_index_website');
543: }
544:
545: 546: 547: 548: 549:
550: protected function _prepareWebsiteDateTable()
551: {
552: $write = $this->_getWriteAdapter();
553: $baseCurrency = Mage::app()->getBaseCurrencyCode();
554:
555: $select = $write->select()
556: ->from(
557: array('cw' => $this->getTable('core/website')),
558: array('website_id'))
559: ->join(
560: array('csg' => $this->getTable('core/store_group')),
561: 'cw.default_group_id = csg.group_id',
562: array('store_id' => 'default_store_id'))
563: ->where('cw.website_id != 0');
564:
565:
566: $data = array();
567: foreach ($write->fetchAll($select) as $item) {
568:
569: $website = Mage::app()->getWebsite($item['website_id']);
570:
571: if ($website->getBaseCurrencyCode() != $baseCurrency) {
572: $rate = Mage::getModel('directory/currency')
573: ->load($baseCurrency)
574: ->getRate($website->getBaseCurrencyCode());
575: if (!$rate) {
576: $rate = 1;
577: }
578: } else {
579: $rate = 1;
580: }
581:
582:
583: $store = Mage::app()->getStore($item['store_id']);
584: if ($store) {
585: $timestamp = Mage::app()->getLocale()->storeTimeStamp($store);
586: $data[] = array(
587: 'website_id' => $website->getId(),
588: 'website_date' => $this->formatDate($timestamp, false),
589: 'rate' => $rate
590: );
591: }
592: }
593:
594: $write->beginTransaction();
595: $table = $this->_getWebsiteDateTable();
596: $write->delete($table);
597:
598: if ($data) {
599: $write->insertMultiple($table, $data);
600: }
601: $write->commit();
602:
603: return $this;
604: }
605:
606: 607: 608: 609: 610: 611:
612: public function getIdxTable($table = null)
613: {
614: if ($this->useIdxTable()) {
615: return $this->getTable('catalog/product_price_indexer_idx');
616: }
617: return $this->getTable('catalog/product_price_indexer_tmp');
618: }
619: }
620: