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 extends Mage_Catalog_Model_Resource_Abstract
36: {
37: 38: 39: 40: 41:
42: protected $_productWebsiteTable;
43:
44: 45: 46: 47: 48:
49: protected $_productCategoryTable;
50:
51: 52: 53:
54: public function __construct()
55: {
56: parent::__construct();
57: $this->setType(Mage_Catalog_Model_Product::ENTITY)
58: ->setConnection('catalog_read', 'catalog_write');
59: $this->_productWebsiteTable = $this->getTable('catalog/product_website');
60: $this->_productCategoryTable = $this->getTable('catalog/category_product');
61: }
62:
63: 64: 65: 66: 67:
68: protected function _getDefaultAttributes()
69: {
70: return array('entity_id', 'entity_type_id', 'attribute_set_id', 'type_id', 'created_at', 'updated_at');
71: }
72:
73: 74: 75: 76: 77: 78:
79: public function getWebsiteIds($product)
80: {
81: $adapter = $this->_getReadAdapter();
82:
83: if ($product instanceof Mage_Catalog_Model_Product) {
84: $productId = $product->getId();
85: } else {
86: $productId = $product;
87: }
88:
89: $select = $adapter->select()
90: ->from($this->_productWebsiteTable, 'website_id')
91: ->where('product_id = ?', (int)$productId);
92:
93: return $adapter->fetchCol($select);
94: }
95:
96: 97: 98: 99: 100: 101:
102: public function getWebsiteIdsByProductIds($productIds)
103: {
104: $select = $this->_getWriteAdapter()->select()
105: ->from($this->_productWebsiteTable, array('product_id', 'website_id'))
106: ->where('product_id IN (?)', $productIds);
107: $productsWebsites = array();
108: foreach ($this->_getWriteAdapter()->fetchAll($select) as $productInfo) {
109: $productId = $productInfo['product_id'];
110: if (!isset($productsWebsites[$productId])) {
111: $productsWebsites[$productId] = array();
112: }
113: $productsWebsites[$productId][] = $productInfo['website_id'];
114:
115: }
116:
117: return $productsWebsites;
118: }
119:
120: 121: 122: 123: 124: 125:
126: public function getCategoryIds($product)
127: {
128: $adapter = $this->_getReadAdapter();
129:
130: $select = $adapter->select()
131: ->from($this->_productCategoryTable, 'category_id')
132: ->where('product_id = ?', (int)$product->getId());
133:
134: return $adapter->fetchCol($select);
135: }
136:
137: 138: 139: 140: 141: 142:
143: public function getIdBySku($sku)
144: {
145: $adapter = $this->_getReadAdapter();
146:
147: $select = $adapter->select()
148: ->from($this->getEntityTable(), 'entity_id')
149: ->where('sku = :sku');
150:
151: $bind = array(':sku' => (string)$sku);
152:
153: return $adapter->fetchOne($select, $bind);
154: }
155:
156: 157: 158: 159: 160: 161:
162: protected function _beforeSave(Varien_Object $object)
163: {
164: 165: 166:
167: if (!$object->getId() && $object->getSku()) {
168: $object->setId($this->getIdBySku($object->getSku()));
169: }
170:
171: 172: 173:
174: if ($object->hasCategoryIds()) {
175: $categoryIds = Mage::getResourceSingleton('catalog/category')->verifyIds(
176: $object->getCategoryIds()
177: );
178: $object->setCategoryIds($categoryIds);
179: }
180:
181: return parent::_beforeSave($object);
182: }
183:
184: 185: 186: 187: 188: 189:
190: protected function _afterSave(Varien_Object $product)
191: {
192: $this->_saveWebsiteIds($product)
193: ->_saveCategories($product);
194:
195: return parent::_afterSave($product);
196: }
197:
198: 199: 200: 201: 202: 203:
204: protected function _saveWebsiteIds($product)
205: {
206: $websiteIds = $product->getWebsiteIds();
207: $oldWebsiteIds = array();
208:
209: $product->setIsChangedWebsites(false);
210:
211: $adapter = $this->_getWriteAdapter();
212:
213: $oldWebsiteIds = $this->getWebsiteIds($product);
214:
215: $insert = array_diff($websiteIds, $oldWebsiteIds);
216: $delete = array_diff($oldWebsiteIds, $websiteIds);
217:
218: if (!empty($insert)) {
219: $data = array();
220: foreach ($insert as $websiteId) {
221: $data[] = array(
222: 'product_id' => (int)$product->getId(),
223: 'website_id' => (int)$websiteId
224: );
225: }
226: $adapter->insertMultiple($this->_productWebsiteTable, $data);
227: }
228:
229: if (!empty($delete)) {
230: foreach ($delete as $websiteId) {
231: $condition = array(
232: 'product_id = ?' => (int)$product->getId(),
233: 'website_id = ?' => (int)$websiteId,
234: );
235:
236: $adapter->delete($this->_productWebsiteTable, $condition);
237: }
238: }
239:
240: if (!empty($insert) || !empty($delete)) {
241: $product->setIsChangedWebsites(true);
242: }
243:
244: return $this;
245: }
246:
247: 248: 249: 250: 251: 252:
253: protected function _saveCategories(Varien_Object $object)
254: {
255: 256: 257:
258: if (!$object->hasCategoryIds()) {
259: return $this;
260: }
261: $categoryIds = $object->getCategoryIds();
262: $oldCategoryIds = $this->getCategoryIds($object);
263:
264: $object->setIsChangedCategories(false);
265:
266: $insert = array_diff($categoryIds, $oldCategoryIds);
267: $delete = array_diff($oldCategoryIds, $categoryIds);
268:
269: $write = $this->_getWriteAdapter();
270: if (!empty($insert)) {
271: $data = array();
272: foreach ($insert as $categoryId) {
273: if (empty($categoryId)) {
274: continue;
275: }
276: $data[] = array(
277: 'category_id' => (int)$categoryId,
278: 'product_id' => (int)$object->getId(),
279: 'position' => 1
280: );
281: }
282: if ($data) {
283: $write->insertMultiple($this->_productCategoryTable, $data);
284: }
285: }
286:
287: if (!empty($delete)) {
288: foreach ($delete as $categoryId) {
289: $where = array(
290: 'product_id = ?' => (int)$object->getId(),
291: 'category_id = ?' => (int)$categoryId,
292: );
293:
294: $write->delete($this->_productCategoryTable, $where);
295: }
296: }
297:
298: if (!empty($insert) || !empty($delete)) {
299: $object->setAffectedCategoryIds(array_merge($insert, $delete));
300: $object->setIsChangedCategories(true);
301: }
302:
303: return $this;
304: }
305:
306: 307: 308: 309: 310: 311:
312: public function refreshIndex($product)
313: {
314: $writeAdapter = $this->_getWriteAdapter();
315:
316: 317: 318:
319: $categoryIds = $product->getCategoryIds();
320:
321: 322: 323:
324: $condition = array('product_id = ?' => (int)$product->getId());
325: $writeAdapter->delete($this->getTable('catalog/category_product_index'), $condition);
326:
327:
328: $categoryObject = Mage::getResourceSingleton('catalog/category');
329: if (!empty($categoryIds)) {
330: $categoriesSelect = $writeAdapter->select()
331: ->from($this->getTable('catalog/category'))
332: ->where('entity_id IN (?)', $categoryIds);
333:
334: $categoriesInfo = $writeAdapter->fetchAll($categoriesSelect);
335:
336: $indexCategoryIds = array();
337: foreach ($categoriesInfo as $categoryInfo) {
338: $ids = explode('/', $categoryInfo['path']);
339: $ids[] = $categoryInfo['entity_id'];
340: $indexCategoryIds = array_merge($indexCategoryIds, $ids);
341: }
342:
343: $indexCategoryIds = array_unique($indexCategoryIds);
344: $indexProductIds = array($product->getId());
345:
346: $categoryObject->refreshProductIndex($indexCategoryIds, $indexProductIds);
347: } else {
348: $websites = $product->getWebsiteIds();
349:
350: if ($websites) {
351: $storeIds = array();
352:
353: foreach ($websites as $websiteId) {
354: $website = Mage::app()->getWebsite($websiteId);
355: $storeIds = array_merge($storeIds, $website->getStoreIds());
356: }
357:
358: $categoryObject->refreshProductIndex(array(), array($product->getId()), $storeIds);
359: }
360: }
361:
362: 363: 364:
365: $this->refreshEnabledIndex(null, $product);
366:
367: return $this;
368: }
369:
370: 371: 372: 373: 374: 375: 376: 377: 378: 379:
380: public function refreshEnabledIndex($store = null, $product = null)
381: {
382: $statusAttribute = $this->getAttribute('status');
383: $visibilityAttribute = $this->getAttribute('visibility');
384: $statusAttributeId = $statusAttribute->getId();
385: $visibilityAttributeId = $visibilityAttribute->getId();
386: $statusTable = $statusAttribute->getBackend()->getTable();
387: $visibilityTable = $visibilityAttribute->getBackend()->getTable();
388:
389: $adapter = $this->_getWriteAdapter();
390:
391: $select = $adapter->select();
392: $condition = array();
393:
394: $indexTable = $this->getTable('catalog/product_enabled_index');
395: if (is_null($store) && is_null($product)) {
396: Mage::throwException(
397: Mage::helper('catalog')->__('To reindex the enabled product(s), the store or product must be specified')
398: );
399: } elseif (is_null($product) || is_array($product)) {
400: $storeId = $store->getId();
401: $websiteId = $store->getWebsiteId();
402:
403: if (is_array($product) && !empty($product)) {
404: $condition[] = $adapter->quoteInto('product_id IN (?)', $product);
405: }
406:
407: $condition[] = $adapter->quoteInto('store_id = ?', $storeId);
408:
409: $selectFields = array(
410: 't_v_default.entity_id',
411: new Zend_Db_Expr($storeId),
412: $adapter->getCheckSql('t_v.value_id > 0', 't_v.value', 't_v_default.value'),
413: );
414:
415: $select->joinInner(
416: array('w' => $this->getTable('catalog/product_website')),
417: $adapter->quoteInto(
418: 'w.product_id = t_v_default.entity_id AND w.website_id = ?', $websiteId
419: ),
420: array()
421: );
422: } elseif ($store === null) {
423: foreach ($product->getStoreIds() as $storeId) {
424: $store = Mage::app()->getStore($storeId);
425: $this->refreshEnabledIndex($store, $product);
426: }
427: return $this;
428: } else {
429: $productId = is_numeric($product) ? $product : $product->getId();
430: $storeId = is_numeric($store) ? $store : $store->getId();
431:
432: $condition = array(
433: 'product_id = ?' => (int)$productId,
434: 'store_id = ?' => (int)$storeId,
435: );
436:
437: $selectFields = array(
438: new Zend_Db_Expr($productId),
439: new Zend_Db_Expr($storeId),
440: $adapter->getCheckSql('t_v.value_id > 0', 't_v.value', 't_v_default.value')
441: );
442:
443: $select->where('t_v_default.entity_id = ?', $productId);
444: }
445:
446: $adapter->delete($indexTable, $condition);
447:
448: $select->from(array('t_v_default' => $visibilityTable), $selectFields);
449:
450: $visibilityTableJoinCond = array(
451: 't_v.entity_id = t_v_default.entity_id',
452: $adapter->quoteInto('t_v.attribute_id = ?', $visibilityAttributeId),
453: $adapter->quoteInto('t_v.store_id = ?', $storeId),
454: );
455:
456: $select->joinLeft(
457: array('t_v' => $visibilityTable),
458: implode(' AND ', $visibilityTableJoinCond),
459: array()
460: );
461:
462: $defaultStatusJoinCond = array(
463: 't_s_default.entity_id = t_v_default.entity_id',
464: 't_s_default.store_id = 0',
465: $adapter->quoteInto('t_s_default.attribute_id = ?', $statusAttributeId),
466: );
467:
468: $select->joinInner(
469: array('t_s_default' => $statusTable),
470: implode(' AND ', $defaultStatusJoinCond),
471: array()
472: );
473:
474:
475: $statusJoinCond = array(
476: 't_s.entity_id = t_v_default.entity_id',
477: $adapter->quoteInto('t_s.store_id = ?', $storeId),
478: $adapter->quoteInto('t_s.attribute_id = ?', $statusAttributeId),
479: );
480:
481: $select->joinLeft(
482: array('t_s' => $statusTable),
483: implode(' AND ', $statusJoinCond),
484: array()
485: );
486:
487: $valueCondition = $adapter->getCheckSql('t_s.value_id > 0', 't_s.value', 't_s_default.value');
488:
489: $select->where('t_v_default.attribute_id = ?', $visibilityAttributeId)
490: ->where('t_v_default.store_id = ?', 0)
491: ->where(sprintf('%s = ?', $valueCondition), Mage_Catalog_Model_Product_Status::STATUS_ENABLED);
492:
493: if (is_array($product) && !empty($product)) {
494: $select->where('t_v_default.entity_id IN (?)', $product);
495: }
496:
497: $adapter->query($adapter->insertFromSelect($select, $indexTable));
498:
499:
500: return $this;
501: }
502:
503: 504: 505: 506: 507: 508:
509: public function getCategoryCollection($product)
510: {
511: $collection = Mage::getResourceModel('catalog/category_collection')
512: ->joinField('product_id',
513: 'catalog/category_product',
514: 'product_id',
515: 'category_id = entity_id',
516: null)
517: ->addFieldToFilter('product_id', (int)$product->getId());
518: return $collection;
519: }
520:
521: 522: 523: 524: 525: 526:
527: public function getAvailableInCategories($object)
528: {
529:
530:
531: $select = $this->_getReadAdapter()->select()->distinct()
532: ->from($this->getTable('catalog/category_product_index'), array('category_id'))
533: ->where('product_id = ? AND is_parent = 1', (int)$object->getEntityId());
534:
535: return $this->_getReadAdapter()->fetchCol($select);
536: }
537:
538: 539: 540: 541: 542:
543: public function getDefaultAttributeSourceModel()
544: {
545: return 'eav/entity_attribute_source_table';
546: }
547:
548: 549: 550: 551: 552: 553: 554:
555: public function canBeShowInCategory($product, $categoryId)
556: {
557: $select = $this->_getReadAdapter()->select()
558: ->from($this->getTable('catalog/category_product_index'), 'product_id')
559: ->where('product_id = ?', (int)$product->getId())
560: ->where('category_id = ?', (int)$categoryId);
561:
562: return $this->_getReadAdapter()->fetchOne($select);
563: }
564:
565: 566: 567: 568: 569: 570: 571:
572: public function duplicate($oldId, $newId)
573: {
574: $adapter = $this->_getWriteAdapter();
575: $eavTables = array('datetime', 'decimal', 'int', 'text', 'varchar');
576:
577: $adapter = $this->_getWriteAdapter();
578:
579:
580: foreach ($eavTables as $suffix) {
581: $tableName = $this->getTable(array('catalog/product', $suffix));
582:
583: $select = $adapter->select()
584: ->from($tableName, array(
585: 'entity_type_id',
586: 'attribute_id',
587: 'store_id',
588: 'entity_id' => new Zend_Db_Expr($adapter->quote($newId)),
589: 'value'
590: ))
591: ->where('entity_id = ?', $oldId)
592: ->where('store_id > ?', 0);
593:
594: $adapter->query($adapter->insertFromSelect(
595: $select,
596: $tableName,
597: array(
598: 'entity_type_id',
599: 'attribute_id',
600: 'store_id',
601: 'entity_id',
602: 'value'
603: ),
604: Varien_Db_Adapter_Interface::INSERT_ON_DUPLICATE
605: ));
606: }
607:
608:
609: $statusAttribute = $this->getAttribute('status');
610: $statusAttributeId = $statusAttribute->getAttributeId();
611: $statusAttributeTable = $statusAttribute->getBackend()->getTable();
612: $updateCond[] = 'store_id > 0';
613: $updateCond[] = $adapter->quoteInto('entity_id = ?', $newId);
614: $updateCond[] = $adapter->quoteInto('attribute_id = ?', $statusAttributeId);
615: $adapter->update(
616: $statusAttributeTable,
617: array('value' => Mage_Catalog_Model_Product_Status::STATUS_DISABLED),
618: $updateCond
619: );
620:
621: return $this;
622: }
623:
624: 625: 626: 627: 628: 629:
630: public function getProductsSku(array $productIds)
631: {
632: $select = $this->_getReadAdapter()->select()
633: ->from($this->getTable('catalog/product'), array('entity_id', 'sku'))
634: ->where('entity_id IN (?)', $productIds);
635: return $this->_getReadAdapter()->fetchAll($select);
636: }
637:
638: 639: 640: 641: 642:
643: public function getParentProductIds($object)
644: {
645: return array();
646: }
647:
648: 649: 650: 651: 652: 653:
654: public function getProductEntitiesInfo($columns = null)
655: {
656: if (!empty($columns) && is_string($columns)) {
657: $columns = array($columns);
658: }
659: if (empty($columns) || !is_array($columns)) {
660: $columns = $this->_getDefaultAttributes();
661: }
662:
663: $adapter = $this->_getReadAdapter();
664: $select = $adapter->select()
665: ->from($this->getTable('catalog/product'), $columns);
666:
667: return $adapter->fetchAll($select);
668: }
669:
670: 671: 672: 673: 674: 675: 676: 677:
678: public function getAssignedImages($product, $storeIds)
679: {
680: if (!is_array($storeIds)) {
681: $storeIds = array($storeIds);
682: }
683:
684: $mainTable = $product->getResource()->getAttribute('image')
685: ->getBackend()
686: ->getTable();
687: $read = $this->_getReadAdapter();
688: $select = $read->select()
689: ->from(
690: array('images' => $mainTable),
691: array('value as filepath', 'store_id')
692: )
693: ->joinLeft(
694: array('attr' => $this->getTable('eav/attribute')),
695: 'images.attribute_id = attr.attribute_id',
696: array('attribute_code')
697: )
698: ->where('entity_id = ?', $product->getId())
699: ->where('store_id IN (?)', $storeIds)
700: ->where('attribute_code IN (?)', array('small_image', 'thumbnail', 'image'));
701:
702: $images = $read->fetchAll($select);
703: return $images;
704: }
705: }
706: