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: class Mage_Catalog_Model_Convert_Adapter_Product
29: extends Mage_Eav_Model_Convert_Adapter_Entity
30: {
31: const MULTI_DELIMITER = ' , ';
32: const ENTITY = 'catalog_product_import';
33:
34: 35: 36: 37: 38:
39: protected $_eventPrefix = 'catalog_product_import';
40:
41: 42: 43: 44: 45:
46: protected $_productModel;
47:
48: 49: 50: 51: 52:
53: protected $_productTypes;
54:
55: 56: 57: 58: 59:
60: protected $_productTypeInstances = array();
61:
62: 63: 64: 65: 66:
67: protected $_productAttributeSets;
68:
69: protected $_stores;
70:
71: protected $_attributes = array();
72:
73: protected $_configs = array();
74:
75: protected $_requiredFields = array();
76:
77: protected $_ignoreFields = array();
78:
79: 80: 81: 82: 83:
84: protected $_imageFields = array();
85:
86: 87: 88: 89: 90:
91: protected $_inventoryFields = array();
92:
93: 94: 95: 96: 97:
98: protected $_inventoryFieldsProductTypes = array();
99:
100: protected $_toNumber = array();
101:
102: 103: 104: 105: 106:
107: public function getEventPrefix()
108: {
109: return $this->_eventPrefix;
110: }
111:
112: 113: 114: 115: 116:
117: protected $_affectedEntityIds = array();
118:
119: 120: 121: 122: 123: 124:
125: protected function _addAffectedEntityIds($ids)
126: {
127: if (is_array($ids)) {
128: foreach ($ids as $id) {
129: $this->_addAffectedEntityIds($id);
130: }
131: } else {
132: $this->_affectedEntityIds[] = $ids;
133: }
134:
135: return $this;
136: }
137:
138: 139: 140: 141: 142:
143: public function getAffectedEntityIds()
144: {
145: return $this->_affectedEntityIds;
146: }
147:
148: 149: 150: 151: 152:
153: public function clearAffectedEntityIds()
154: {
155: $this->_affectedEntityIds = array();
156: return $this;
157: }
158:
159: 160: 161:
162: public function load()
163: {
164: $attrFilterArray = array();
165: $attrFilterArray ['name'] = 'like';
166: $attrFilterArray ['sku'] = 'startsWith';
167: $attrFilterArray ['type'] = 'eq';
168: $attrFilterArray ['attribute_set'] = 'eq';
169: $attrFilterArray ['visibility'] = 'eq';
170: $attrFilterArray ['status'] = 'eq';
171: $attrFilterArray ['price'] = 'fromTo';
172: $attrFilterArray ['qty'] = 'fromTo';
173: $attrFilterArray ['store_id'] = 'eq';
174:
175: $attrToDb = array(
176: 'type' => 'type_id',
177: 'attribute_set' => 'attribute_set_id'
178: );
179:
180: $filters = $this->_parseVars();
181:
182: if ($qty = $this->getFieldValue($filters, 'qty')) {
183: $qtyFrom = isset($qty['from']) ? (float) $qty['from'] : 0;
184: $qtyTo = isset($qty['to']) ? (float) $qty['to'] : 0;
185:
186: $qtyAttr = array();
187: $qtyAttr['alias'] = 'qty';
188: $qtyAttr['attribute'] = 'cataloginventory/stock_item';
189: $qtyAttr['field'] = 'qty';
190: $qtyAttr['bind'] = 'product_id=entity_id';
191: $qtyAttr['cond'] = "{{table}}.qty between '{$qtyFrom}' AND '{$qtyTo}'";
192: $qtyAttr['joinType'] = 'inner';
193:
194: $this->setJoinField($qtyAttr);
195: }
196:
197: parent::setFilter($attrFilterArray, $attrToDb);
198:
199: if ($price = $this->getFieldValue($filters, 'price')) {
200: $this->_filter[] = array(
201: 'attribute' => 'price',
202: 'from' => $price['from'],
203: 'to' => $price['to']
204: );
205: $this->setJoinAttr(array(
206: 'alias' => 'price',
207: 'attribute' => 'catalog_product/price',
208: 'bind' => 'entity_id',
209: 'joinType' => 'LEFT'
210: ));
211: }
212:
213: return parent::load();
214: }
215:
216: 217: 218: 219: 220:
221: public function getProductModel()
222: {
223: if (is_null($this->_productModel)) {
224: $productModel = Mage::getModel('catalog/product');
225: $this->_productModel = Mage::objects()->save($productModel);
226: }
227: return Mage::objects()->load($this->_productModel);
228: }
229:
230: 231: 232: 233: 234: 235:
236: public function getAttribute($code)
237: {
238: if (!isset($this->_attributes[$code])) {
239: $this->_attributes[$code] = $this->getProductModel()->getResource()->getAttribute($code);
240: }
241: if ($this->_attributes[$code] instanceof Mage_Catalog_Model_Resource_Eav_Attribute) {
242: $applyTo = $this->_attributes[$code]->getApplyTo();
243: if ($applyTo && !in_array($this->getProductModel()->getTypeId(), $applyTo)) {
244: return false;
245: }
246: }
247: return $this->_attributes[$code];
248: }
249:
250: 251: 252: 253: 254:
255: public function getProductTypes()
256: {
257: if (is_null($this->_productTypes)) {
258: $this->_productTypes = array();
259: $options = Mage::getModel('catalog/product_type')
260: ->getOptionArray();
261: foreach ($options as $k => $v) {
262: $this->_productTypes[$k] = $k;
263: }
264: }
265: return $this->_productTypes;
266: }
267:
268: 269: 270: 271: 272: 273:
274: public function setProductTypeInstance(Mage_Catalog_Model_Product $product)
275: {
276: $type = $product->getTypeId();
277: if (!isset($this->_productTypeInstances[$type])) {
278: $this->_productTypeInstances[$type] = Mage::getSingleton('catalog/product_type')
279: ->factory($product, true);
280: }
281: $product->setTypeInstance($this->_productTypeInstances[$type], true);
282: return $this;
283: }
284:
285: 286: 287: 288: 289:
290: public function getProductAttributeSets()
291: {
292: if (is_null($this->_productAttributeSets)) {
293: $this->_productAttributeSets = array();
294:
295: $entityTypeId = Mage::getModel('eav/entity')
296: ->setType('catalog_product')
297: ->getTypeId();
298: $collection = Mage::getResourceModel('eav/entity_attribute_set_collection')
299: ->setEntityTypeFilter($entityTypeId);
300: foreach ($collection as $set) {
301: $this->_productAttributeSets[$set->getAttributeSetName()] = $set->getId();
302: }
303: }
304: return $this->_productAttributeSets;
305: }
306:
307: 308: 309:
310: protected function _initStores ()
311: {
312: if (is_null($this->_stores)) {
313: $this->_stores = Mage::app()->getStores(true, true);
314: foreach ($this->_stores as $code => $store) {
315: $this->_storesIdCode[$store->getId()] = $code;
316: }
317: }
318: }
319:
320: 321: 322: 323: 324: 325:
326: public function getStoreByCode($store)
327: {
328: $this->_initStores();
329: 330: 331:
332: if (Mage::app()->isSingleStoreMode()) {
333: return Mage::app()->getStore(Mage_Catalog_Model_Abstract::DEFAULT_STORE_ID);
334: }
335:
336: if (isset($this->_stores[$store])) {
337: return $this->_stores[$store];
338: }
339:
340: return false;
341: }
342:
343: 344: 345: 346: 347: 348:
349: public function getStoreById($id)
350: {
351: $this->_initStores();
352: 353: 354:
355: if (Mage::app()->isSingleStoreMode()) {
356: return Mage::app()->getStore(Mage_Catalog_Model_Abstract::DEFAULT_STORE_ID);
357: }
358:
359: if (isset($this->_storesIdCode[$id])) {
360: return $this->getStoreByCode($this->_storesIdCode[$id]);
361: }
362:
363: return false;
364: }
365:
366: public function parse()
367: {
368: $batchModel = Mage::getSingleton('dataflow/batch');
369:
370:
371: $batchImportModel = $batchModel->getBatchImportModel();
372: $importIds = $batchImportModel->getIdCollection();
373:
374: foreach ($importIds as $importId) {
375:
376: $batchImportModel->load($importId);
377: $importData = $batchImportModel->getBatchData();
378:
379: $this->saveRow($importData);
380: }
381: }
382:
383: protected $_productId = '';
384:
385: 386: 387: 388:
389: public function __construct()
390: {
391: $fieldset = Mage::getConfig()->getFieldset('catalog_product_dataflow', 'admin');
392: foreach ($fieldset as $code => $node) {
393:
394: if ($node->is('inventory')) {
395: foreach ($node->product_type->children() as $productType) {
396: $productType = $productType->getName();
397: $this->_inventoryFieldsProductTypes[$productType][] = $code;
398: if ($node->is('use_config')) {
399: $this->_inventoryFieldsProductTypes[$productType][] = 'use_config_' . $code;
400: }
401: }
402:
403: $this->_inventoryFields[] = $code;
404: if ($node->is('use_config')) {
405: $this->_inventoryFields[] = 'use_config_'.$code;
406: }
407: }
408: if ($node->is('required')) {
409: $this->_requiredFields[] = $code;
410: }
411: if ($node->is('ignore')) {
412: $this->_ignoreFields[] = $code;
413: }
414: if ($node->is('to_number')) {
415: $this->_toNumber[] = $code;
416: }
417: }
418:
419: $this->setVar('entity_type', 'catalog/product');
420: if (!Mage::registry('Object_Cache_Product')) {
421: $this->setProduct(Mage::getModel('catalog/product'));
422: }
423:
424: if (!Mage::registry('Object_Cache_StockItem')) {
425: $this->setStockItem(Mage::getModel('cataloginventory/stock_item'));
426: }
427: }
428:
429: 430: 431: 432: 433: 434:
435: protected function _getCollectionForLoad($entityType)
436: {
437: $collection = parent::_getCollectionForLoad($entityType)
438: ->setStoreId($this->getStoreId())
439: ->addStoreFilter($this->getStoreId());
440: return $collection;
441: }
442:
443: public function setProduct(Mage_Catalog_Model_Product $object)
444: {
445: $id = Mage::objects()->save($object);
446:
447: Mage::register('Object_Cache_Product', $id);
448: }
449:
450: public function getProduct()
451: {
452: return Mage::objects()->load(Mage::registry('Object_Cache_Product'));
453: }
454:
455: public function setStockItem(Mage_CatalogInventory_Model_Stock_Item $object)
456: {
457: $id = Mage::objects()->save($object);
458: Mage::register('Object_Cache_StockItem', $id);
459: }
460:
461: public function getStockItem()
462: {
463: return Mage::objects()->load(Mage::registry('Object_Cache_StockItem'));
464: }
465:
466: public function save()
467: {
468: $stores = array();
469: foreach (Mage::getConfig()->getNode('stores')->children() as $storeNode) {
470: $stores[(int)$storeNode->system->store->id] = $storeNode->getName();
471: }
472:
473: $collections = $this->getData();
474: if ($collections instanceof Mage_Catalog_Model_Entity_Product_Collection) {
475: $collections = array($collections->getEntity()->getStoreId()=>$collections);
476: } elseif (!is_array($collections)) {
477: $this->addException(
478: Mage::helper('catalog')->__('No product collections found.'),
479: Mage_Dataflow_Model_Convert_Exception::FATAL
480: );
481: }
482:
483: $stockItems = Mage::registry('current_imported_inventory');
484: if ($collections) foreach ($collections as $storeId=>$collection) {
485: $this->addException(Mage::helper('catalog')->__('Records for "'.$stores[$storeId].'" store found.'));
486:
487: if (!$collection instanceof Mage_Catalog_Model_Entity_Product_Collection) {
488: $this->addException(
489: Mage::helper('catalog')->__('Product collection expected.'),
490: Mage_Dataflow_Model_Convert_Exception::FATAL
491: );
492: }
493: try {
494: $i = 0;
495: foreach ($collection->getIterator() as $model) {
496: $new = false;
497:
498: if (!$model->getId()) {
499: $new = true;
500: $model->save();
501:
502:
503:
504: if (0 !== $storeId ) {
505: $data = $model->getData();
506: $default = Mage::getModel('catalog/product');
507: $default->setData($data);
508: $default->setStoreId(0);
509: $default->save();
510: unset($default);
511: }
512:
513:
514: }
515: if (!$new || 0!==$storeId) {
516: if (0!==$storeId) {
517: Mage::getResourceSingleton('catalog_entity/convert')->addProductToStore(
518: $model->getId(),
519: $storeId
520: );
521: }
522: $model->save();
523: }
524:
525: if (isset($stockItems[$model->getSku()]) && $stock = $stockItems[$model->getSku()]) {
526: $stockItem = Mage::getModel('cataloginventory/stock_item')->loadByProduct($model->getId());
527: $stockItemId = $stockItem->getId();
528:
529: if (!$stockItemId) {
530: $stockItem->setData('product_id', $model->getId());
531: $stockItem->setData('stock_id', 1);
532: $data = array();
533: } else {
534: $data = $stockItem->getData();
535: }
536:
537: foreach($stock as $field => $value) {
538: if (!$stockItemId) {
539: if (in_array($field, $this->_configs)) {
540: $stockItem->setData('use_config_'.$field, 0);
541: }
542: $stockItem->setData($field, $value?$value:0);
543: } else {
544:
545: if (in_array($field, $this->_configs)) {
546: if ($data['use_config_'.$field] == 0) {
547: $stockItem->setData($field, $value?$value:0);
548: }
549: } else {
550: $stockItem->setData($field, $value?$value:0);
551: }
552: }
553: }
554: $stockItem->save();
555: unset($data);
556: unset($stockItem);
557: unset($stockItemId);
558: }
559: unset($model);
560: $i++;
561: }
562: $this->addException(Mage::helper('catalog')->__("Saved %d record(s)", $i));
563: } catch (Exception $e) {
564: if (!$e instanceof Mage_Dataflow_Model_Convert_Exception) {
565: $this->addException(
566: Mage::helper('catalog')->__('An error occurred while saving the collection, aborting. Error message: %s', $e->getMessage()),
567: Mage_Dataflow_Model_Convert_Exception::FATAL
568: );
569: }
570: }
571: }
572: unset($collections);
573:
574: return $this;
575: }
576:
577: 578: 579: 580: 581: 582: 583:
584: public function saveRow(array $importData)
585: {
586: $product = $this->getProductModel()
587: ->reset();
588:
589: if (empty($importData['store'])) {
590: if (!is_null($this->getBatchParams('store'))) {
591: $store = $this->getStoreById($this->getBatchParams('store'));
592: } else {
593: $message = Mage::helper('catalog')->__('Skipping import row, required field "%s" is not defined.', 'store');
594: Mage::throwException($message);
595: }
596: } else {
597: $store = $this->getStoreByCode($importData['store']);
598: }
599:
600: if ($store === false) {
601: $message = Mage::helper('catalog')->__('Skipping import row, store "%s" field does not exist.', $importData['store']);
602: Mage::throwException($message);
603: }
604:
605: if (empty($importData['sku'])) {
606: $message = Mage::helper('catalog')->__('Skipping import row, required field "%s" is not defined.', 'sku');
607: Mage::throwException($message);
608: }
609: $product->setStoreId($store->getId());
610: $productId = $product->getIdBySku($importData['sku']);
611:
612: if ($productId) {
613: $product->load($productId);
614: } else {
615: $productTypes = $this->getProductTypes();
616: $productAttributeSets = $this->getProductAttributeSets();
617:
618: 619: 620:
621: if (empty($importData['type']) || !isset($productTypes[strtolower($importData['type'])])) {
622: $value = isset($importData['type']) ? $importData['type'] : '';
623: $message = Mage::helper('catalog')->__('Skip import row, is not valid value "%s" for field "%s"', $value, 'type');
624: Mage::throwException($message);
625: }
626: $product->setTypeId($productTypes[strtolower($importData['type'])]);
627: 628: 629:
630: if (empty($importData['attribute_set']) || !isset($productAttributeSets[$importData['attribute_set']])) {
631: $value = isset($importData['attribute_set']) ? $importData['attribute_set'] : '';
632: $message = Mage::helper('catalog')->__('Skip import row, the value "%s" is invalid for field "%s"', $value, 'attribute_set');
633: Mage::throwException($message);
634: }
635: $product->setAttributeSetId($productAttributeSets[$importData['attribute_set']]);
636:
637: foreach ($this->_requiredFields as $field) {
638: $attribute = $this->getAttribute($field);
639: if (!isset($importData[$field]) && $attribute && $attribute->getIsRequired()) {
640: $message = Mage::helper('catalog')->__('Skipping import row, required field "%s" for new products is not defined.', $field);
641: Mage::throwException($message);
642: }
643: }
644: }
645:
646: $this->setProductTypeInstance($product);
647:
648: if (isset($importData['category_ids'])) {
649: $product->setCategoryIds($importData['category_ids']);
650: }
651:
652: foreach ($this->_ignoreFields as $field) {
653: if (isset($importData[$field])) {
654: unset($importData[$field]);
655: }
656: }
657:
658: if ($store->getId() != 0) {
659: $websiteIds = $product->getWebsiteIds();
660: if (!is_array($websiteIds)) {
661: $websiteIds = array();
662: }
663: if (!in_array($store->getWebsiteId(), $websiteIds)) {
664: $websiteIds[] = $store->getWebsiteId();
665: }
666: $product->setWebsiteIds($websiteIds);
667: }
668:
669: if (isset($importData['websites'])) {
670: $websiteIds = $product->getWebsiteIds();
671: if (!is_array($websiteIds) || !$store->getId()) {
672: $websiteIds = array();
673: }
674: $websiteCodes = explode(',', $importData['websites']);
675: foreach ($websiteCodes as $websiteCode) {
676: try {
677: $website = Mage::app()->getWebsite(trim($websiteCode));
678: if (!in_array($website->getId(), $websiteIds)) {
679: $websiteIds[] = $website->getId();
680: }
681: } catch (Exception $e) {}
682: }
683: $product->setWebsiteIds($websiteIds);
684: unset($websiteIds);
685: }
686:
687: foreach ($importData as $field => $value) {
688: if (in_array($field, $this->_inventoryFields)) {
689: continue;
690: }
691: if (is_null($value)) {
692: continue;
693: }
694:
695: $attribute = $this->getAttribute($field);
696: if (!$attribute) {
697: continue;
698: }
699:
700: $isArray = false;
701: $setValue = $value;
702:
703: if ($attribute->getFrontendInput() == 'multiselect') {
704: $value = explode(self::MULTI_DELIMITER, $value);
705: $isArray = true;
706: $setValue = array();
707: }
708:
709: if ($value && $attribute->getBackendType() == 'decimal') {
710: $setValue = $this->getNumber($value);
711: }
712:
713: if ($attribute->usesSource()) {
714: $options = $attribute->getSource()->getAllOptions(false);
715:
716: if ($isArray) {
717: foreach ($options as $item) {
718: if (in_array($item['label'], $value)) {
719: $setValue[] = $item['value'];
720: }
721: }
722: } else {
723: $setValue = false;
724: foreach ($options as $item) {
725: if (is_array($item['value'])) {
726: foreach ($item['value'] as $subValue) {
727: if (isset($subValue['value']) && $subValue['value'] == $value) {
728: $setValue = $value;
729: }
730: }
731: } else if ($item['label'] == $value) {
732: $setValue = $item['value'];
733: }
734: }
735: }
736: }
737:
738: $product->setData($field, $setValue);
739: }
740:
741: if (!$product->getVisibility()) {
742: $product->setVisibility(Mage_Catalog_Model_Product_Visibility::VISIBILITY_NOT_VISIBLE);
743: }
744:
745: $stockData = array();
746: $inventoryFields = isset($this->_inventoryFieldsProductTypes[$product->getTypeId()])
747: ? $this->_inventoryFieldsProductTypes[$product->getTypeId()]
748: : array();
749: foreach ($inventoryFields as $field) {
750: if (isset($importData[$field])) {
751: if (in_array($field, $this->_toNumber)) {
752: $stockData[$field] = $this->getNumber($importData[$field]);
753: } else {
754: $stockData[$field] = $importData[$field];
755: }
756: }
757: }
758: $product->setStockData($stockData);
759:
760: $mediaGalleryBackendModel = $this->getAttribute('media_gallery')->getBackend();
761:
762: $arrayToMassAdd = array();
763:
764: foreach ($product->getMediaAttributes() as $mediaAttributeCode => $mediaAttribute) {
765: if (isset($importData[$mediaAttributeCode])) {
766: $file = trim($importData[$mediaAttributeCode]);
767: if (!empty($file) && !$mediaGalleryBackendModel->getImage($product, $file)) {
768: $arrayToMassAdd[] = array('file' => trim($file), 'mediaAttribute' => $mediaAttributeCode);
769: }
770: }
771: }
772:
773: $addedFilesCorrespondence = $mediaGalleryBackendModel->addImagesWithDifferentMediaAttributes(
774: $product,
775: $arrayToMassAdd, Mage::getBaseDir('media') . DS . 'import',
776: false,
777: false
778: );
779:
780: foreach ($product->getMediaAttributes() as $mediaAttributeCode => $mediaAttribute) {
781: $addedFile = '';
782: if (isset($importData[$mediaAttributeCode . '_label'])) {
783: $fileLabel = trim($importData[$mediaAttributeCode . '_label']);
784: if (isset($importData[$mediaAttributeCode])) {
785: $keyInAddedFile = array_search($importData[$mediaAttributeCode],
786: $addedFilesCorrespondence['alreadyAddedFiles']);
787: if ($keyInAddedFile !== false) {
788: $addedFile = $addedFilesCorrespondence['alreadyAddedFilesNames'][$keyInAddedFile];
789: }
790: }
791:
792: if (!$addedFile) {
793: $addedFile = $product->getData($mediaAttributeCode);
794: }
795: if ($fileLabel && $addedFile) {
796: $mediaGalleryBackendModel->updateImage($product, $addedFile, array('label' => $fileLabel));
797: }
798: }
799: }
800:
801: $product->setIsMassupdate(true);
802: $product->setExcludeUrlRewrite(true);
803:
804: $product->save();
805:
806:
807: $this->_addAffectedEntityIds($product->getId());
808:
809: return true;
810: }
811:
812: 813: 814: 815: 816: 817:
818: public function saveRowSilently(array $importData)
819: {
820: try {
821: $result = $this->saveRow($importData);
822: return $result;
823: } catch (Exception $e) {
824: return false;
825: }
826: }
827:
828: 829: 830: 831:
832: public function finish()
833: {
834: 835: 836:
837: Mage::dispatchEvent($this->_eventPrefix . '_after', array());
838:
839: $entity = new Varien_Object();
840: Mage::getSingleton('index/indexer')->processEntityAction(
841: $entity, self::ENTITY, Mage_Index_Model_Event::TYPE_SAVE
842: );
843: }
844: }
845: