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: class Mage_ImportExport_Model_Import_Entity_Product extends Mage_ImportExport_Model_Import_Entity_Abstract
35: {
36: const CONFIG_KEY_PRODUCT_TYPES = 'global/importexport/import_product_types';
37:
38: 39: 40:
41: const BUNCH_SIZE = 20;
42:
43: 44: 45:
46: const VALUE_ALL = 'all';
47:
48: 49: 50:
51: const SCOPE_DEFAULT = 1;
52: const SCOPE_WEBSITE = 2;
53: const SCOPE_STORE = 0;
54: const SCOPE_NULL = -1;
55:
56: 57: 58: 59: 60: 61:
62: const COL_STORE = '_store';
63: const COL_ATTR_SET = '_attribute_set';
64: const COL_TYPE = '_type';
65: const COL_CATEGORY = '_category';
66: const COL_ROOT_CATEGORY = '_root_category';
67: const COL_SKU = 'sku';
68:
69: 70: 71:
72: const ERROR_INVALID_SCOPE = 'invalidScope';
73: const ERROR_INVALID_WEBSITE = 'invalidWebsite';
74: const ERROR_INVALID_STORE = 'invalidStore';
75: const ERROR_INVALID_ATTR_SET = 'invalidAttrSet';
76: const ERROR_INVALID_TYPE = 'invalidType';
77: const ERROR_INVALID_CATEGORY = 'invalidCategory';
78: const ERROR_VALUE_IS_REQUIRED = 'isRequired';
79: const ERROR_TYPE_CHANGED = 'typeChanged';
80: const ERROR_SKU_IS_EMPTY = 'skuEmpty';
81: const ERROR_NO_DEFAULT_ROW = 'noDefaultRow';
82: const ERROR_CHANGE_TYPE = 'changeProductType';
83: const ERROR_DUPLICATE_SCOPE = 'duplicateScope';
84: const ERROR_DUPLICATE_SKU = 'duplicateSKU';
85: const ERROR_CHANGE_ATTR_SET = 'changeAttrSet';
86: const ERROR_TYPE_UNSUPPORTED = 'productTypeUnsupported';
87: const ERROR_ROW_IS_ORPHAN = 'rowIsOrphan';
88: const ERROR_INVALID_TIER_PRICE_QTY = 'invalidTierPriceOrQty';
89: const ERROR_INVALID_TIER_PRICE_SITE = 'tierPriceWebsiteInvalid';
90: const ERROR_INVALID_TIER_PRICE_GROUP = 'tierPriceGroupInvalid';
91: const ERROR_TIER_DATA_INCOMPLETE = 'tierPriceDataIsIncomplete';
92: const ERROR_INVALID_GROUP_PRICE_SITE = 'groupPriceWebsiteInvalid';
93: const ERROR_INVALID_GROUP_PRICE_GROUP = 'groupPriceGroupInvalid';
94: const ERROR_GROUP_PRICE_DATA_INCOMPLETE = 'groupPriceDataIsIncomplete';
95: const ERROR_SKU_NOT_FOUND_FOR_DELETE = 'skuNotFoundToDelete';
96: const ERROR_SUPER_PRODUCTS_SKU_NOT_FOUND = 'superProductsSkuNotFound';
97:
98: 99: 100: 101: 102:
103: protected $_attrSetIdToName = array();
104:
105: 106: 107: 108: 109:
110: protected $_attrSetNameToId = array();
111:
112: 113: 114: 115: 116:
117: protected $_categories = array();
118:
119: 120: 121: 122: 123:
124: protected $_categoriesWithRoots = array();
125:
126: 127: 128: 129: 130:
131: protected $_customerGroups = array();
132:
133: 134: 135: 136: 137:
138: protected $_indexValueAttributes = array(
139: 'status',
140: 'tax_class_id',
141: 'visibility',
142: 'enable_googlecheckout',
143: 'gift_message_available',
144: 'custom_design'
145: );
146:
147: 148: 149: 150: 151:
152: protected $_linkNameToId = array(
153: '_links_related_' => Mage_Catalog_Model_Product_Link::LINK_TYPE_RELATED,
154: '_links_crosssell_' => Mage_Catalog_Model_Product_Link::LINK_TYPE_CROSSSELL,
155: '_links_upsell_' => Mage_Catalog_Model_Product_Link::LINK_TYPE_UPSELL
156: );
157:
158: 159: 160: 161: 162:
163: protected $_messageTemplates = array(
164: self::ERROR_INVALID_SCOPE => 'Invalid value in Scope column',
165: self::ERROR_INVALID_WEBSITE => 'Invalid value in Website column (website does not exists?)',
166: self::ERROR_INVALID_STORE => 'Invalid value in Store column (store does not exists?)',
167: self::ERROR_INVALID_ATTR_SET => 'Invalid value for Attribute Set column (set does not exists?)',
168: self::ERROR_INVALID_TYPE => 'Product Type is invalid or not supported',
169: self::ERROR_INVALID_CATEGORY => 'Category does not exists',
170: self::ERROR_VALUE_IS_REQUIRED => "Required attribute '%s' has an empty value",
171: self::ERROR_TYPE_CHANGED => 'Trying to change type of existing products',
172: self::ERROR_SKU_IS_EMPTY => 'SKU is empty',
173: self::ERROR_NO_DEFAULT_ROW => 'Default values row does not exists',
174: self::ERROR_CHANGE_TYPE => 'Product type change is not allowed',
175: self::ERROR_DUPLICATE_SCOPE => 'Duplicate scope',
176: self::ERROR_DUPLICATE_SKU => 'Duplicate SKU',
177: self::ERROR_CHANGE_ATTR_SET => 'Product attribute set change is not allowed',
178: self::ERROR_TYPE_UNSUPPORTED => 'Product type is not supported',
179: self::ERROR_ROW_IS_ORPHAN => 'Orphan rows that will be skipped due default row errors',
180: self::ERROR_INVALID_TIER_PRICE_QTY => 'Tier Price data price or quantity value is invalid',
181: self::ERROR_INVALID_TIER_PRICE_SITE => 'Tier Price data website is invalid',
182: self::ERROR_INVALID_TIER_PRICE_GROUP => 'Tier Price customer group ID is invalid',
183: self::ERROR_TIER_DATA_INCOMPLETE => 'Tier Price data is incomplete',
184: self::ERROR_SKU_NOT_FOUND_FOR_DELETE => 'Product with specified SKU not found',
185: self::ERROR_SUPER_PRODUCTS_SKU_NOT_FOUND => 'Product with specified super products SKU not found'
186: );
187:
188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199:
200: protected $_newSku = array();
201:
202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213:
214: protected $_oldSku = array();
215:
216: 217: 218: 219: 220:
221: protected $_particularAttributes = array(
222: '_store', '_attribute_set', '_type', self::COL_CATEGORY, self::COL_ROOT_CATEGORY, '_product_websites',
223: '_tier_price_website', '_tier_price_customer_group', '_tier_price_qty', '_tier_price_price',
224: '_links_related_sku', '_group_price_website', '_group_price_customer_group', '_group_price_price',
225: '_links_related_position', '_links_crosssell_sku', '_links_crosssell_position', '_links_upsell_sku',
226: '_links_upsell_position', '_custom_option_store', '_custom_option_type', '_custom_option_title',
227: '_custom_option_is_required', '_custom_option_price', '_custom_option_sku', '_custom_option_max_characters',
228: '_custom_option_sort_order', '_custom_option_file_extension', '_custom_option_image_size_x',
229: '_custom_option_image_size_y', '_custom_option_row_title', '_custom_option_row_price',
230: '_custom_option_row_sku', '_custom_option_row_sort', '_media_attribute_id', '_media_image', '_media_lable',
231: '_media_position', '_media_is_disabled'
232: );
233:
234: 235: 236: 237: 238:
239: protected $_imagesArrayKeys = array(
240: '_media_image', 'image', 'small_image', 'thumbnail'
241: );
242:
243: 244: 245: 246: 247:
248: protected $_permanentAttributes = array(self::COL_SKU);
249:
250: 251: 252: 253: 254:
255: protected $_productTypeModels = array();
256:
257: 258: 259: 260: 261:
262: protected $_storeCodeToId = array();
263:
264: 265: 266: 267: 268:
269: protected $_storeIdToWebsiteStoreIds = array();
270:
271: 272: 273: 274: 275:
276: protected $_websiteCodeToId = array();
277:
278: 279: 280: 281: 282:
283: protected $_websiteCodeToStoreIds = array();
284:
285: 286: 287: 288: 289:
290: protected $_fileUploader;
291:
292: 293: 294: 295:
296: public function __construct()
297: {
298: parent::__construct();
299:
300: $this->_initWebsites()
301: ->_initStores()
302: ->_initAttributeSets()
303: ->_initTypeModels()
304: ->_initCategories()
305: ->_initSkus()
306: ->_initCustomerGroups();
307: }
308:
309: 310: 311: 312: 313:
314: protected function _deleteProducts()
315: {
316: $productEntityTable = Mage::getModel('importexport/import_proxy_product_resource')->getEntityTable();
317:
318: while ($bunch = $this->_dataSourceModel->getNextBunch()) {
319: $idToDelete = array();
320:
321: foreach ($bunch as $rowNum => $rowData) {
322: if ($this->validateRow($rowData, $rowNum) && self::SCOPE_DEFAULT == $this->getRowScope($rowData)) {
323: $idToDelete[] = $this->_oldSku[$rowData[self::COL_SKU]]['entity_id'];
324: }
325: }
326: if ($idToDelete) {
327: $this->_connection->query(
328: $this->_connection->quoteInto(
329: "DELETE FROM `{$productEntityTable}` WHERE `entity_id` IN (?)", $idToDelete
330: )
331: );
332: }
333: }
334: return $this;
335: }
336:
337: 338: 339: 340: 341: 342:
343: protected function _importData()
344: {
345: if (Mage_ImportExport_Model_Import::BEHAVIOR_DELETE == $this->getBehavior()) {
346: $this->_deleteProducts();
347: } else {
348: $this->_saveProducts();
349: $this->_saveStockItem();
350: $this->_saveLinks();
351: $this->_saveCustomOptions();
352: foreach ($this->_productTypeModels as $productType => $productTypeModel) {
353: $productTypeModel->saveData();
354: }
355: }
356: Mage::dispatchEvent('catalog_product_import_finish_before', array('adapter'=>$this));
357: return true;
358: }
359:
360: 361: 362: 363: 364:
365: protected function _initAttributeSets()
366: {
367: foreach (Mage::getResourceModel('eav/entity_attribute_set_collection')
368: ->setEntityTypeFilter($this->_entityTypeId) as $attributeSet) {
369: $this->_attrSetNameToId[$attributeSet->getAttributeSetName()] = $attributeSet->getId();
370: $this->_attrSetIdToName[$attributeSet->getId()] = $attributeSet->getAttributeSetName();
371: }
372: return $this;
373: }
374:
375: 376: 377: 378: 379:
380: protected function _initCategories()
381: {
382: $collection = Mage::getResourceModel('catalog/category_collection')->addNameToResult();
383:
384: foreach ($collection as $category) {
385: $structure = explode('/', $category->getPath());
386: $pathSize = count($structure);
387: if ($pathSize > 1) {
388: $path = array();
389: for ($i = 1; $i < $pathSize; $i++) {
390: $path[] = $collection->getItemById($structure[$i])->getName();
391: }
392: $rootCategoryName = array_shift($path);
393: if (!isset($this->_categoriesWithRoots[$rootCategoryName])) {
394: $this->_categoriesWithRoots[$rootCategoryName] = array();
395: }
396: $index = implode('/', $path);
397: $this->_categoriesWithRoots[$rootCategoryName][$index] = $category->getId();
398: if ($pathSize > 2) {
399: $this->_categories[$index] = $category->getId();
400: }
401: }
402: }
403: return $this;
404: }
405:
406: 407: 408: 409: 410:
411: protected function _initCustomerGroups()
412: {
413: foreach (Mage::getResourceModel('customer/group_collection') as $customerGroup) {
414: $this->_customerGroups[$customerGroup->getId()] = true;
415: }
416: return $this;
417: }
418:
419: 420: 421: 422: 423:
424: protected function _initSkus()
425: {
426: $columns = array('entity_id', 'type_id', 'attribute_set_id', 'sku');
427: foreach (Mage::getModel('catalog/product')->getProductEntitiesInfo($columns) as $info) {
428: $typeId = $info['type_id'];
429: $sku = $info['sku'];
430: $this->_oldSku[$sku] = array(
431: 'type_id' => $typeId,
432: 'attr_set_id' => $info['attribute_set_id'],
433: 'entity_id' => $info['entity_id'],
434: 'supported_type' => isset($this->_productTypeModels[$typeId])
435: );
436: }
437: return $this;
438: }
439:
440: 441: 442: 443: 444:
445: protected function _initStores()
446: {
447: foreach (Mage::app()->getStores() as $store) {
448: $this->_storeCodeToId[$store->getCode()] = $store->getId();
449: $this->_storeIdToWebsiteStoreIds[$store->getId()] = $store->getWebsite()->getStoreIds();
450: }
451: return $this;
452: }
453:
454: 455: 456: 457: 458: 459:
460: protected function _initTypeModels()
461: {
462: $config = Mage::getConfig()->getNode(self::CONFIG_KEY_PRODUCT_TYPES)->asCanonicalArray();
463: foreach ($config as $type => $typeModel) {
464: if (!($model = Mage::getModel($typeModel, array($this, $type)))) {
465: Mage::throwException("Entity type model '{$typeModel}' is not found");
466: }
467: if (! $model instanceof Mage_ImportExport_Model_Import_Entity_Product_Type_Abstract) {
468: Mage::throwException(
469: Mage::helper('importexport')->__('Entity type model must be an instance of Mage_ImportExport_Model_Import_Entity_Product_Type_Abstract')
470: );
471: }
472: if ($model->isSuitable()) {
473: $this->_productTypeModels[$type] = $model;
474: }
475: $this->_particularAttributes = array_merge(
476: $this->_particularAttributes,
477: $model->getParticularAttributes()
478: );
479: }
480:
481: $this->_particularAttributes = array_unique($this->_particularAttributes);
482:
483: return $this;
484: }
485:
486: 487: 488: 489: 490:
491: protected function _initWebsites()
492: {
493:
494: foreach (Mage::app()->getWebsites() as $website) {
495: $this->_websiteCodeToId[$website->getCode()] = $website->getId();
496: $this->_websiteCodeToStoreIds[$website->getCode()] = array_flip($website->getStoreCodes());
497: }
498: return $this;
499: }
500:
501: 502: 503: 504: 505: 506: 507:
508: protected function _isProductCategoryValid(array $rowData, $rowNum)
509: {
510: $emptyCategory = empty($rowData[self::COL_CATEGORY]);
511: $emptyRootCategory = empty($rowData[self::COL_ROOT_CATEGORY]);
512: $hasCategory = $emptyCategory ? false : isset($this->_categories[$rowData[self::COL_CATEGORY]]);
513: $category = $emptyRootCategory ? null : $this->_categoriesWithRoots[$rowData[self::COL_ROOT_CATEGORY]];
514: if (!$emptyCategory && !$hasCategory
515: || !$emptyRootCategory && !isset($category)
516: || !$emptyRootCategory && !$emptyCategory && !isset($category[$rowData[self::COL_CATEGORY]])
517: ) {
518: $this->addRowError(self::ERROR_INVALID_CATEGORY, $rowNum);
519: return false;
520: }
521: return true;
522: }
523:
524: 525: 526: 527: 528: 529: 530:
531: protected function _isProductWebsiteValid(array $rowData, $rowNum)
532: {
533: if (!empty($rowData['_product_websites']) && !isset($this->_websiteCodeToId[$rowData['_product_websites']])) {
534: $this->addRowError(self::ERROR_INVALID_WEBSITE, $rowNum);
535: return false;
536: }
537: return true;
538: }
539:
540: 541: 542: 543: 544: 545: 546:
547: protected function _prepareRowForDb(array $rowData)
548: {
549: $rowData = parent::_prepareRowForDb($rowData);
550:
551: static $lastSku = null;
552:
553: if (Mage_ImportExport_Model_Import::BEHAVIOR_DELETE == $this->getBehavior()) {
554: return $rowData;
555: }
556: if (self::SCOPE_DEFAULT == $this->getRowScope($rowData)) {
557: $lastSku = $rowData[self::COL_SKU];
558: }
559: if (isset($this->_oldSku[$lastSku])) {
560: $rowData[self::COL_ATTR_SET] = $this->_newSku[$lastSku]['attr_set_code'];
561: $rowData[self::COL_TYPE] = $this->_newSku[$lastSku]['type_id'];
562: }
563:
564: return $rowData;
565: }
566:
567: 568: 569: 570: 571: 572: 573:
574: protected function _isTierPriceValid(array $rowData, $rowNum)
575: {
576: if ((isset($rowData['_tier_price_website']) && strlen($rowData['_tier_price_website']))
577: || (isset($rowData['_tier_price_customer_group']) && strlen($rowData['_tier_price_customer_group']))
578: || (isset($rowData['_tier_price_qty']) && strlen($rowData['_tier_price_qty']))
579: || (isset($rowData['_tier_price_price']) && strlen($rowData['_tier_price_price']))
580: ) {
581: if (!isset($rowData['_tier_price_website']) || !isset($rowData['_tier_price_customer_group'])
582: || !isset($rowData['_tier_price_qty']) || !isset($rowData['_tier_price_price'])
583: || !strlen($rowData['_tier_price_website']) || !strlen($rowData['_tier_price_customer_group'])
584: || !strlen($rowData['_tier_price_qty']) || !strlen($rowData['_tier_price_price'])
585: ) {
586: $this->addRowError(self::ERROR_TIER_DATA_INCOMPLETE, $rowNum);
587: return false;
588: } elseif ($rowData['_tier_price_website'] != self::VALUE_ALL
589: && !isset($this->_websiteCodeToId[$rowData['_tier_price_website']])) {
590: $this->addRowError(self::ERROR_INVALID_TIER_PRICE_SITE, $rowNum);
591: return false;
592: } elseif ($rowData['_tier_price_customer_group'] != self::VALUE_ALL
593: && !isset($this->_customerGroups[$rowData['_tier_price_customer_group']])) {
594: $this->addRowError(self::ERROR_INVALID_TIER_PRICE_GROUP, $rowNum);
595: return false;
596: } elseif ($rowData['_tier_price_qty'] <= 0 || $rowData['_tier_price_price'] <= 0) {
597: $this->addRowError(self::ERROR_INVALID_TIER_PRICE_QTY, $rowNum);
598: return false;
599: }
600: }
601: return true;
602: }
603:
604: 605: 606: 607: 608: 609: 610:
611: protected function _isGroupPriceValid(array $rowData, $rowNum)
612: {
613: if ((isset($rowData['_group_price_website']) && strlen($rowData['_group_price_website']))
614: || (isset($rowData['_group_price_customer_group']) && strlen($rowData['_group_price_customer_group']))
615: || (isset($rowData['_group_price_price']) && strlen($rowData['_group_price_price']))
616: ) {
617: if (!isset($rowData['_group_price_website']) || !isset($rowData['_group_price_customer_group'])
618: || !strlen($rowData['_group_price_website']) || !strlen($rowData['_group_price_customer_group'])
619: || !strlen($rowData['_group_price_price'])
620: ) {
621: $this->addRowError(self::ERROR_GROUP_PRICE_DATA_INCOMPLETE, $rowNum);
622: return false;
623: } elseif ($rowData['_group_price_website'] != self::VALUE_ALL
624: && !isset($this->_websiteCodeToId[$rowData['_group_price_website']])
625: ) {
626: $this->addRowError(self::ERROR_INVALID_GROUP_PRICE_SITE, $rowNum);
627: return false;
628: } elseif ($rowData['_group_price_customer_group'] != self::VALUE_ALL
629: && !isset($this->_customerGroups[$rowData['_group_price_customer_group']])
630: ) {
631: $this->addRowError(self::ERROR_INVALID_GROUP_PRICE_GROUP, $rowNum);
632: return false;
633: }
634: }
635: return true;
636: }
637:
638: 639: 640: 641: 642: 643: 644:
645: protected function _isSuperProductsSkuValid($rowData, $rowNum)
646: {
647: if (!empty($rowData['_super_products_sku'])
648: && (!isset($this->_oldSku[$rowData['_super_products_sku']])
649: && !isset($this->_newSku[$rowData['_super_products_sku']])
650: )
651: ) {
652: $this->addRowError(self::ERROR_SUPER_PRODUCTS_SKU_NOT_FOUND, $rowNum);
653: return false;
654: }
655: return true;
656: }
657:
658: 659: 660: 661: 662:
663: protected function _saveCustomOptions()
664: {
665:
666: $coreResource = Mage::getSingleton('core/resource');
667: $productTable = $coreResource->getTableName('catalog/product');
668: $optionTable = $coreResource->getTableName('catalog/product_option');
669: $priceTable = $coreResource->getTableName('catalog/product_option_price');
670: $titleTable = $coreResource->getTableName('catalog/product_option_title');
671: $typePriceTable = $coreResource->getTableName('catalog/product_option_type_price');
672: $typeTitleTable = $coreResource->getTableName('catalog/product_option_type_title');
673: $typeValueTable = $coreResource->getTableName('catalog/product_option_type_value');
674: $nextOptionId = Mage::getResourceHelper('importexport')->getNextAutoincrement($optionTable);
675: $nextValueId = Mage::getResourceHelper('importexport')->getNextAutoincrement($typeValueTable);
676: $priceIsGlobal = Mage::helper('catalog')->isPriceGlobal();
677: $type = null;
678: $typeSpecific = array(
679: 'date' => array('price', 'sku'),
680: 'date_time' => array('price', 'sku'),
681: 'time' => array('price', 'sku'),
682: 'field' => array('price', 'sku', 'max_characters'),
683: 'area' => array('price', 'sku', 'max_characters'),
684:
685: 'drop_down' => true,
686: 'radio' => true,
687: 'checkbox' => true,
688: 'multiple' => true
689: );
690:
691: while ($bunch = $this->_dataSourceModel->getNextBunch()) {
692: $customOptions = array(
693: 'product_id' => array(),
694: $optionTable => array(),
695: $priceTable => array(),
696: $titleTable => array(),
697: $typePriceTable => array(),
698: $typeTitleTable => array(),
699: $typeValueTable => array()
700: );
701:
702: foreach ($bunch as $rowNum => $rowData) {
703: if (!$this->isRowAllowedToImport($rowData, $rowNum)) {
704: continue;
705: }
706: if (self::SCOPE_DEFAULT == $this->getRowScope($rowData)) {
707: $productId = $this->_newSku[$rowData[self::COL_SKU]]['entity_id'];
708: } elseif (!isset($productId)) {
709: continue;
710: }
711: if (!empty($rowData['_custom_option_store'])) {
712: if (!isset($this->_storeCodeToId[$rowData['_custom_option_store']])) {
713: continue;
714: }
715: $storeId = $this->_storeCodeToId[$rowData['_custom_option_store']];
716: } else {
717: $storeId = Mage_Catalog_Model_Abstract::DEFAULT_STORE_ID;
718: }
719: if (!empty($rowData['_custom_option_type'])) {
720: if (!isset($typeSpecific[$rowData['_custom_option_type']])) {
721: $type = null;
722: continue;
723: }
724: $type = $rowData['_custom_option_type'];
725: $rowIsMain = true;
726: } else {
727: if (null === $type) {
728: continue;
729: }
730: $rowIsMain = false;
731: }
732: if (!isset($customOptions['product_id'][$productId])) {
733: $customOptions['product_id'][$productId] = array(
734: 'entity_id' => $productId,
735: 'has_options' => 0,
736: 'required_options' => 0,
737: 'updated_at' => now()
738: );
739: }
740: if ($rowIsMain) {
741: $solidParams = array(
742: 'option_id' => $nextOptionId,
743: 'sku' => '',
744: 'max_characters' => 0,
745: 'file_extension' => null,
746: 'image_size_x' => 0,
747: 'image_size_y' => 0,
748: 'product_id' => $productId,
749: 'type' => $type,
750: 'is_require' => empty($rowData['_custom_option_is_required']) ? 0 : 1,
751: 'sort_order' => empty($rowData['_custom_option_sort_order'])
752: ? 0 : abs($rowData['_custom_option_sort_order'])
753: );
754:
755: if (true !== $typeSpecific[$type]) {
756: $priceTableRow = array(
757: 'option_id' => $nextOptionId,
758: 'store_id' => Mage_Catalog_Model_Abstract::DEFAULT_STORE_ID,
759: 'price' => 0,
760: 'price_type' => 'fixed'
761: );
762:
763: foreach ($typeSpecific[$type] as $paramSuffix) {
764: if (isset($rowData['_custom_option_' . $paramSuffix])) {
765: $data = $rowData['_custom_option_' . $paramSuffix];
766:
767: if (array_key_exists($paramSuffix, $solidParams)) {
768: $solidParams[$paramSuffix] = $data;
769: } elseif ('price' == $paramSuffix) {
770: if ('%' == substr($data, -1)) {
771: $priceTableRow['price_type'] = 'percent';
772: }
773: $priceTableRow['price'] = (float) rtrim($data, '%');
774: }
775: }
776: }
777: $customOptions[$priceTable][] = $priceTableRow;
778: }
779: $customOptions[$optionTable][] = $solidParams;
780: $customOptions['product_id'][$productId]['has_options'] = 1;
781:
782: if (!empty($rowData['_custom_option_is_required'])) {
783: $customOptions['product_id'][$productId]['required_options'] = 1;
784: }
785: $prevOptionId = $nextOptionId++;
786: }
787: if ($typeSpecific[$type] === true && !empty($rowData['_custom_option_row_title'])
788: && empty($rowData['_custom_option_store'])) {
789:
790: $customOptions[$typeValueTable][$prevOptionId][] = array(
791: 'option_type_id' => $nextValueId,
792: 'sort_order' => empty($rowData['_custom_option_row_sort'])
793: ? 0 : abs($rowData['_custom_option_row_sort']),
794: 'sku' => !empty($rowData['_custom_option_row_sku'])
795: ? $rowData['_custom_option_row_sku'] : ''
796: );
797: if (!isset($customOptions[$typeTitleTable][$nextValueId][0])) {
798: $customOptions[$typeTitleTable][$nextValueId][0] = $rowData['_custom_option_row_title'];
799: }
800: $customOptions[$typeTitleTable][$nextValueId][$storeId] = $rowData['_custom_option_row_title'];
801:
802: if (!empty($rowData['_custom_option_row_price'])) {
803: $typePriceRow = array(
804: 'price' => (float) rtrim($rowData['_custom_option_row_price'], '%'),
805: 'price_type' => 'fixed'
806: );
807: if ('%' == substr($rowData['_custom_option_row_price'], -1)) {
808: $typePriceRow['price_type'] = 'percent';
809: }
810: if ($priceIsGlobal) {
811: $customOptions[$typePriceTable][$nextValueId][0] = $typePriceRow;
812: } else {
813:
814: if (!isset($customOptions[$typePriceTable][$nextValueId][0])) {
815: $customOptions[$typePriceTable][$nextValueId][0] = $typePriceRow;
816: }
817: $customOptions[$typePriceTable][$nextValueId][$storeId] = $typePriceRow;
818: }
819: }
820: $nextValueId++;
821: }
822: if (!empty($rowData['_custom_option_title'])) {
823: if (!isset($customOptions[$titleTable][$prevOptionId][0])) {
824: $customOptions[$titleTable][$prevOptionId][0] = $rowData['_custom_option_title'];
825: }
826: $customOptions[$titleTable][$prevOptionId][$storeId] = $rowData['_custom_option_title'];
827: }
828: }
829: if ($this->getBehavior() != Mage_ImportExport_Model_Import::BEHAVIOR_APPEND) {
830: $this->_connection->delete(
831: $optionTable,
832: $this->_connection->quoteInto('product_id IN (?)', array_keys($customOptions['product_id']))
833: );
834: }
835:
836: foreach ($customOptions[$optionTable] as $key => $optionData) {
837: if ($typeSpecific[$optionData['type']] === true
838: && !isset($customOptions[$typeValueTable][$optionData['option_id']])
839: ) {
840: unset($customOptions[$optionTable][$key], $customOptions[$titleTable][$optionData['option_id']]);
841: }
842: }
843:
844: if ($customOptions[$optionTable]) {
845: $this->_connection->insertMultiple($optionTable, $customOptions[$optionTable]);
846: } else {
847: continue;
848: }
849: $titleRows = array();
850:
851: foreach ($customOptions[$titleTable] as $optionId => $storeInfo) {
852: foreach ($storeInfo as $storeId => $title) {
853: $titleRows[] = array('option_id' => $optionId, 'store_id' => $storeId, 'title' => $title);
854: }
855: }
856: if ($titleRows) {
857: $this->_connection->insertOnDuplicate($titleTable, $titleRows, array('title'));
858: }
859: if ($customOptions[$priceTable]) {
860: $this->_connection->insertOnDuplicate(
861: $priceTable,
862: $customOptions[$priceTable],
863: array('price', 'price_type')
864: );
865: }
866: $typeValueRows = array();
867:
868: foreach ($customOptions[$typeValueTable] as $optionId => $optionInfo) {
869: foreach ($optionInfo as $row) {
870: $row['option_id'] = $optionId;
871: $typeValueRows[] = $row;
872: }
873: }
874: if ($typeValueRows) {
875: $this->_connection->insertMultiple($typeValueTable, $typeValueRows);
876: }
877: $optionTypePriceRows = array();
878: $optionTypeTitleRows = array();
879:
880: foreach ($customOptions[$typePriceTable] as $optionTypeId => $storesData) {
881: foreach ($storesData as $storeId => $row) {
882: $row['option_type_id'] = $optionTypeId;
883: $row['store_id'] = $storeId;
884: $optionTypePriceRows[] = $row;
885: }
886: }
887: foreach ($customOptions[$typeTitleTable] as $optionTypeId => $storesData) {
888: foreach ($storesData as $storeId => $title) {
889: $optionTypeTitleRows[] = array(
890: 'option_type_id' => $optionTypeId,
891: 'store_id' => $storeId,
892: 'title' => $title
893: );
894: }
895: }
896: if ($optionTypePriceRows) {
897: $this->_connection->insertOnDuplicate(
898: $typePriceTable,
899: $optionTypePriceRows,
900: array('price', 'price_type')
901: );
902: }
903: if ($optionTypeTitleRows) {
904: $this->_connection->insertOnDuplicate($typeTitleTable, $optionTypeTitleRows, array('title'));
905: }
906: if ($customOptions['product_id']) {
907: $this->_connection->insertOnDuplicate(
908: $productTable,
909: $customOptions['product_id'],
910: array('has_options', 'required_options', 'updated_at')
911: );
912: }
913: }
914: return $this;
915: }
916:
917: 918: 919: 920: 921: 922:
923: protected function _saveLinks()
924: {
925: $resource = Mage::getResourceModel('catalog/product_link');
926: $mainTable = $resource->getMainTable();
927: $positionAttrId = array();
928: $nextLinkId = Mage::getResourceHelper('importexport')->getNextAutoincrement($mainTable);
929: $adapter = $this->_connection;
930:
931:
932: foreach ($this->_linkNameToId as $linkName => $linkId) {
933: $select = $adapter->select()
934: ->from(
935: $resource->getTable('catalog/product_link_attribute'),
936: array('id' => 'product_link_attribute_id')
937: )
938: ->where('link_type_id = :link_id AND product_link_attribute_code = :position');
939: $bind = array(
940: ':link_id' => $linkId,
941: ':position' => 'position'
942: );
943: $positionAttrId[$linkId] = $adapter->fetchOne($select, $bind);
944: }
945: while ($bunch = $this->_dataSourceModel->getNextBunch()) {
946: $productIds = array();
947: $linkRows = array();
948: $positionRows = array();
949:
950: foreach ($bunch as $rowNum => $rowData) {
951: if (!$this->isRowAllowedToImport($rowData, $rowNum)) {
952: continue;
953: }
954: if (self::SCOPE_DEFAULT == $this->getRowScope($rowData)) {
955: $sku = $rowData[self::COL_SKU];
956: }
957: foreach ($this->_linkNameToId as $linkName => $linkId) {
958: if (isset($rowData[$linkName . 'sku'])) {
959: $productId = $this->_newSku[$sku]['entity_id'];
960: $productIds[] = $productId;
961: $linkedSku = $rowData[$linkName . 'sku'];
962:
963: if ((isset($this->_newSku[$linkedSku]) || isset($this->_oldSku[$linkedSku]))
964: && $linkedSku != $sku) {
965: if (isset($this->_newSku[$linkedSku])) {
966: $linkedId = $this->_newSku[$linkedSku]['entity_id'];
967: } else {
968: $linkedId = $this->_oldSku[$linkedSku]['entity_id'];
969: }
970: $linkKey = "{$productId}-{$linkedId}-{$linkId}";
971:
972: if (!isset($linkRows[$linkKey])) {
973: $linkRows[$linkKey] = array(
974: 'link_id' => $nextLinkId,
975: 'product_id' => $productId,
976: 'linked_product_id' => $linkedId,
977: 'link_type_id' => $linkId
978: );
979: if (!empty($rowData[$linkName . 'position'])) {
980: $positionRows[] = array(
981: 'link_id' => $nextLinkId,
982: 'product_link_attribute_id' => $positionAttrId[$linkId],
983: 'value' => $rowData[$linkName . 'position']
984: );
985: }
986: $nextLinkId++;
987: }
988: }
989: }
990: }
991: }
992: if (Mage_ImportExport_Model_Import::BEHAVIOR_APPEND != $this->getBehavior() && $productIds) {
993: $adapter->delete(
994: $mainTable,
995: $adapter->quoteInto('product_id IN (?)', array_keys($productIds))
996: );
997: }
998: if ($linkRows) {
999: $adapter->insertOnDuplicate(
1000: $mainTable,
1001: $linkRows,
1002: array('link_id')
1003: );
1004: }
1005: if ($positionRows) {
1006: $adapter->insertOnDuplicate(
1007: $resource->getAttributeTypeTable('int'),
1008: $positionRows,
1009: array('value')
1010: );
1011: }
1012: }
1013: return $this;
1014: }
1015:
1016: 1017: 1018: 1019: 1020: 1021:
1022: protected function _saveProductAttributes(array $attributesData)
1023: {
1024: foreach ($attributesData as $tableName => $skuData) {
1025: $tableData = array();
1026:
1027: foreach ($skuData as $sku => $attributes) {
1028: $productId = $this->_newSku[$sku]['entity_id'];
1029:
1030: foreach ($attributes as $attributeId => $storeValues) {
1031: foreach ($storeValues as $storeId => $storeValue) {
1032: $tableData[] = array(
1033: 'entity_id' => $productId,
1034: 'entity_type_id' => $this->_entityTypeId,
1035: 'attribute_id' => $attributeId,
1036: 'store_id' => $storeId,
1037: 'value' => $storeValue
1038: );
1039: }
1040: }
1041: }
1042: $this->_connection->insertOnDuplicate($tableName, $tableData, array('value'));
1043: }
1044: return $this;
1045: }
1046:
1047: 1048: 1049: 1050: 1051: 1052:
1053: protected function _saveProductCategories(array $categoriesData)
1054: {
1055: static $tableName = null;
1056:
1057: if (!$tableName) {
1058: $tableName = Mage::getModel('importexport/import_proxy_product_resource')->getProductCategoryTable();
1059: }
1060: if ($categoriesData) {
1061: $categoriesIn = array();
1062: $delProductId = array();
1063:
1064: foreach ($categoriesData as $delSku => $categories) {
1065: $productId = $this->_newSku[$delSku]['entity_id'];
1066: $delProductId[] = $productId;
1067:
1068: foreach (array_keys($categories) as $categoryId) {
1069: $categoriesIn[] = array('product_id' => $productId, 'category_id' => $categoryId, 'position' => 1);
1070: }
1071: }
1072: if (Mage_ImportExport_Model_Import::BEHAVIOR_APPEND != $this->getBehavior()) {
1073: $this->_connection->delete(
1074: $tableName,
1075: $this->_connection->quoteInto('product_id IN (?)', $delProductId)
1076: );
1077: }
1078: if ($categoriesIn) {
1079: $this->_connection->insertOnDuplicate($tableName, $categoriesIn, array('position'));
1080: }
1081: }
1082: return $this;
1083: }
1084:
1085: 1086: 1087: 1088: 1089: 1090: 1091:
1092: protected function _saveProductEntity(array $entityRowsIn, array $entityRowsUp)
1093: {
1094: static $entityTable = null;
1095:
1096: if (!$entityTable) {
1097: $entityTable = Mage::getModel('importexport/import_proxy_product_resource')->getEntityTable();
1098: }
1099: if ($entityRowsUp) {
1100: $this->_connection->insertOnDuplicate(
1101: $entityTable,
1102: $entityRowsUp,
1103: array('updated_at')
1104: );
1105: }
1106: if ($entityRowsIn) {
1107: $this->_connection->insertMultiple($entityTable, $entityRowsIn);
1108:
1109: $newProducts = $this->_connection->fetchPairs($this->_connection->select()
1110: ->from($entityTable, array('sku', 'entity_id'))
1111: ->where('sku IN (?)', array_keys($entityRowsIn))
1112: );
1113: foreach ($newProducts as $sku => $newId) {
1114: $this->_newSku[$sku]['entity_id'] = $newId;
1115: }
1116: }
1117: return $this;
1118: }
1119:
1120: 1121: 1122: 1123: 1124:
1125: protected function _saveProducts()
1126: {
1127:
1128: $resource = Mage::getModel('importexport/import_proxy_product_resource');
1129: $priceIsGlobal = Mage::helper('catalog')->isPriceGlobal();
1130: $strftimeFormat = Varien_Date::convertZendToStrftime(Varien_Date::DATETIME_INTERNAL_FORMAT, true, true);
1131: $productLimit = null;
1132: $productsQty = null;
1133:
1134: while ($bunch = $this->_dataSourceModel->getNextBunch()) {
1135: $entityRowsIn = array();
1136: $entityRowsUp = array();
1137: $attributes = array();
1138: $websites = array();
1139: $categories = array();
1140: $tierPrices = array();
1141: $groupPrices = array();
1142: $mediaGallery = array();
1143: $uploadedGalleryFiles = array();
1144: $previousType = null;
1145: $previousAttributeSet = null;
1146:
1147: foreach ($bunch as $rowNum => $rowData) {
1148: if (!$this->validateRow($rowData, $rowNum)) {
1149: continue;
1150: }
1151: $rowScope = $this->getRowScope($rowData);
1152:
1153: if (self::SCOPE_DEFAULT == $rowScope) {
1154: $rowSku = $rowData[self::COL_SKU];
1155:
1156:
1157: if (isset($this->_oldSku[$rowSku])) {
1158: $entityRowsUp[] = array(
1159: 'updated_at' => now(),
1160: 'entity_id' => $this->_oldSku[$rowSku]['entity_id']
1161: );
1162: } else {
1163: if (!$productLimit || $productsQty < $productLimit) {
1164: $entityRowsIn[$rowSku] = array(
1165: 'entity_type_id' => $this->_entityTypeId,
1166: 'attribute_set_id' => $this->_newSku[$rowSku]['attr_set_id'],
1167: 'type_id' => $this->_newSku[$rowSku]['type_id'],
1168: 'sku' => $rowSku,
1169: 'created_at' => now(),
1170: 'updated_at' => now()
1171: );
1172: $productsQty++;
1173: } else {
1174: $rowSku = null;
1175: $this->_rowsToSkip[$rowNum] = true;
1176: continue;
1177: }
1178: }
1179: } elseif (null === $rowSku) {
1180: $this->_rowsToSkip[$rowNum] = true;
1181: continue;
1182: } elseif (self::SCOPE_STORE == $rowScope) {
1183: $rowData[self::COL_TYPE] = $this->_newSku[$rowSku]['type_id'];
1184: $rowData['attribute_set_id'] = $this->_newSku[$rowSku]['attr_set_id'];
1185: $rowData[self::COL_ATTR_SET] = $this->_newSku[$rowSku]['attr_set_code'];
1186: }
1187: if (!empty($rowData['_product_websites'])) {
1188: $websites[$rowSku][$this->_websiteCodeToId[$rowData['_product_websites']]] = true;
1189: }
1190:
1191:
1192: $categoryPath = empty($rowData[self::COL_CATEGORY]) ? '' : $rowData[self::COL_CATEGORY];
1193: if (!empty($rowData[self::COL_ROOT_CATEGORY])) {
1194: $categoryId = $this->_categoriesWithRoots[$rowData[self::COL_ROOT_CATEGORY]][$categoryPath];
1195: $categories[$rowSku][$categoryId] = true;
1196: } elseif (!empty($categoryPath)) {
1197: $categories[$rowSku][$this->_categories[$categoryPath]] = true;
1198: }
1199:
1200: if (!empty($rowData['_tier_price_website'])) {
1201: $tierPrices[$rowSku][] = array(
1202: 'all_groups' => $rowData['_tier_price_customer_group'] == self::VALUE_ALL,
1203: 'customer_group_id' => ($rowData['_tier_price_customer_group'] == self::VALUE_ALL)
1204: ? 0 : $rowData['_tier_price_customer_group'],
1205: 'qty' => $rowData['_tier_price_qty'],
1206: 'value' => $rowData['_tier_price_price'],
1207: 'website_id' => (self::VALUE_ALL == $rowData['_tier_price_website'] || $priceIsGlobal)
1208: ? 0 : $this->_websiteCodeToId[$rowData['_tier_price_website']]
1209: );
1210: }
1211: if (!empty($rowData['_group_price_website'])) {
1212: $groupPrices[$rowSku][] = array(
1213: 'all_groups' => $rowData['_group_price_customer_group'] == self::VALUE_ALL,
1214: 'customer_group_id' => ($rowData['_group_price_customer_group'] == self::VALUE_ALL)
1215: ? 0 : $rowData['_group_price_customer_group'],
1216: 'value' => $rowData['_group_price_price'],
1217: 'website_id' => (self::VALUE_ALL == $rowData['_group_price_website'] || $priceIsGlobal)
1218: ? 0 : $this->_websiteCodeToId[$rowData['_group_price_website']]
1219: );
1220: }
1221: foreach ($this->_imagesArrayKeys as $imageCol) {
1222: if (!empty($rowData[$imageCol])) {
1223: if (!array_key_exists($rowData[$imageCol], $uploadedGalleryFiles)) {
1224: $uploadedGalleryFiles[$rowData[$imageCol]] = $this->_uploadMediaFiles($rowData[$imageCol]);
1225: }
1226: $rowData[$imageCol] = $uploadedGalleryFiles[$rowData[$imageCol]];
1227: }
1228: }
1229: if (!empty($rowData['_media_image'])) {
1230: $mediaGallery[$rowSku][] = array(
1231: 'attribute_id' => $rowData['_media_attribute_id'],
1232: 'label' => $rowData['_media_lable'],
1233: 'position' => $rowData['_media_position'],
1234: 'disabled' => $rowData['_media_is_disabled'],
1235: 'value' => $rowData['_media_image']
1236: );
1237: }
1238:
1239: $rowStore = self::SCOPE_STORE == $rowScope ? $this->_storeCodeToId[$rowData[self::COL_STORE]] : 0;
1240: $productType = $rowData[self::COL_TYPE];
1241: if(!is_null($rowData[self::COL_TYPE])) {
1242: $previousType = $rowData[self::COL_TYPE];
1243: }
1244: if(!is_null($rowData[self::COL_ATTR_SET])) {
1245: $previousAttributeSet = $rowData[Mage_ImportExport_Model_Import_Entity_Product::COL_ATTR_SET];
1246: }
1247: if (self::SCOPE_NULL == $rowScope) {
1248:
1249: if(!is_null($previousAttributeSet)) {
1250: $rowData[Mage_ImportExport_Model_Import_Entity_Product::COL_ATTR_SET] = $previousAttributeSet;
1251: }
1252: if(is_null($productType) && !is_null($previousType)) {
1253: $productType = $previousType;
1254: }
1255: if(is_null($productType)) {
1256: continue;
1257: }
1258: }
1259: $rowData = $this->_productTypeModels[$productType]->prepareAttributesForSave($rowData);
1260: $product = Mage::getModel('importexport/import_proxy_product', $rowData);
1261:
1262: foreach ($rowData as $attrCode => $attrValue) {
1263: $attribute = $resource->getAttribute($attrCode);
1264: if('multiselect' != $attribute->getFrontendInput()
1265: && self::SCOPE_NULL == $rowScope) {
1266: continue;
1267: }
1268: $attrId = $attribute->getId();
1269: $backModel = $attribute->getBackendModel();
1270: $attrTable = $attribute->getBackend()->getTable();
1271: $storeIds = array(0);
1272:
1273: if ('datetime' == $attribute->getBackendType() && strtotime($attrValue)) {
1274: $attrValue = gmstrftime($strftimeFormat, strtotime($attrValue));
1275: } elseif ($backModel) {
1276: $attribute->getBackend()->beforeSave($product);
1277: $attrValue = $product->getData($attribute->getAttributeCode());
1278: }
1279: if (self::SCOPE_STORE == $rowScope) {
1280: if (self::SCOPE_WEBSITE == $attribute->getIsGlobal()) {
1281:
1282: if (!isset($attributes[$attrTable][$rowSku][$attrId][$rowStore])) {
1283: $storeIds = $this->_storeIdToWebsiteStoreIds[$rowStore];
1284: }
1285: } elseif (self::SCOPE_STORE == $attribute->getIsGlobal()) {
1286: $storeIds = array($rowStore);
1287: }
1288: }
1289: foreach ($storeIds as $storeId) {
1290: if('multiselect' == $attribute->getFrontendInput()) {
1291: if(!isset($attributes[$attrTable][$rowSku][$attrId][$storeId])) {
1292: $attributes[$attrTable][$rowSku][$attrId][$storeId] = '';
1293: } else {
1294: $attributes[$attrTable][$rowSku][$attrId][$storeId] .= ',';
1295: }
1296: $attributes[$attrTable][$rowSku][$attrId][$storeId] .= $attrValue;
1297: } else {
1298: $attributes[$attrTable][$rowSku][$attrId][$storeId] = $attrValue;
1299: }
1300: }
1301: $attribute->setBackendModel($backModel);
1302: }
1303: }
1304: $this->_saveProductEntity($entityRowsIn, $entityRowsUp)
1305: ->_saveProductWebsites($websites)
1306: ->_saveProductCategories($categories)
1307: ->_saveProductTierPrices($tierPrices)
1308: ->_saveProductGroupPrices($groupPrices)
1309: ->_saveMediaGallery($mediaGallery)
1310: ->_saveProductAttributes($attributes);
1311: }
1312: return $this;
1313: }
1314:
1315: 1316: 1317: 1318: 1319: 1320:
1321: protected function _saveProductTierPrices(array $tierPriceData)
1322: {
1323: static $tableName = null;
1324:
1325: if (!$tableName) {
1326: $tableName = Mage::getModel('importexport/import_proxy_product_resource')
1327: ->getTable('catalog/product_attribute_tier_price');
1328: }
1329: if ($tierPriceData) {
1330: $tierPriceIn = array();
1331: $delProductId = array();
1332:
1333: foreach ($tierPriceData as $delSku => $tierPriceRows) {
1334: $productId = $this->_newSku[$delSku]['entity_id'];
1335: $delProductId[] = $productId;
1336:
1337: foreach ($tierPriceRows as $row) {
1338: $row['entity_id'] = $productId;
1339: $tierPriceIn[] = $row;
1340: }
1341: }
1342: if (Mage_ImportExport_Model_Import::BEHAVIOR_APPEND != $this->getBehavior()) {
1343: $this->_connection->delete(
1344: $tableName,
1345: $this->_connection->quoteInto('entity_id IN (?)', $delProductId)
1346: );
1347: }
1348: if ($tierPriceIn) {
1349: $this->_connection->insertOnDuplicate($tableName, $tierPriceIn, array('value'));
1350: }
1351: }
1352: return $this;
1353: }
1354:
1355: 1356: 1357: 1358: 1359: 1360:
1361: protected function _saveProductGroupPrices(array $groupPriceData)
1362: {
1363: static $tableName = null;
1364:
1365: if (!$tableName) {
1366: $tableName = Mage::getModel('importexport/import_proxy_product_resource')
1367: ->getTable('catalog/product_attribute_group_price');
1368: }
1369: if ($groupPriceData) {
1370: $groupPriceIn = array();
1371: $delProductId = array();
1372:
1373: foreach ($groupPriceData as $delSku => $groupPriceRows) {
1374: $productId = $this->_newSku[$delSku]['entity_id'];
1375: $delProductId[] = $productId;
1376:
1377: foreach ($groupPriceRows as $row) {
1378: $row['entity_id'] = $productId;
1379: $groupPriceIn[] = $row;
1380: }
1381: }
1382: if (Mage_ImportExport_Model_Import::BEHAVIOR_APPEND != $this->getBehavior()) {
1383: $this->_connection->delete(
1384: $tableName,
1385: $this->_connection->quoteInto('entity_id IN (?)', $delProductId)
1386: );
1387: }
1388: if ($groupPriceIn) {
1389: $this->_connection->insertOnDuplicate($tableName, $groupPriceIn, array('value'));
1390: }
1391: }
1392: return $this;
1393: }
1394:
1395: 1396: 1397:
1398: protected function _getUploader()
1399: {
1400: if (is_null($this->_fileUploader)) {
1401: $this->_fileUploader = new Mage_ImportExport_Model_Import_Uploader();
1402:
1403: $this->_fileUploader->init();
1404:
1405: $tmpDir = Mage::getConfig()->getOptions()->getMediaDir() . '/import';
1406: $destDir = Mage::getConfig()->getOptions()->getMediaDir() . '/catalog/product';
1407: if (!is_writable($destDir)) {
1408: @mkdir($destDir, 0777, true);
1409: }
1410: if (!$this->_fileUploader->setTmpDir($tmpDir)) {
1411: Mage::throwException("File directory '{$tmpDir}' is not readable.");
1412: }
1413: if (!$this->_fileUploader->setDestDir($destDir)) {
1414: Mage::throwException("File directory '{$destDir}' is not writable.");
1415: }
1416: }
1417: return $this->_fileUploader;
1418: }
1419:
1420: 1421: 1422: 1423: 1424: 1425: 1426:
1427: protected function _uploadMediaFiles($fileName)
1428: {
1429: try {
1430: $res = $this->_getUploader()->move($fileName);
1431: return $res['file'];
1432: } catch (Exception $e) {
1433: return '';
1434: }
1435: }
1436:
1437: 1438: 1439: 1440: 1441: 1442:
1443: protected function _saveMediaGallery(array $mediaGalleryData)
1444: {
1445: if (empty($mediaGalleryData)) {
1446: return $this;
1447: }
1448:
1449: static $mediaGalleryTableName = null;
1450: static $mediaValueTableName = null;
1451: static $productId = null;
1452:
1453: if (!$mediaGalleryTableName) {
1454: $mediaGalleryTableName = Mage::getModel('importexport/import_proxy_product_resource')
1455: ->getTable('catalog/product_attribute_media_gallery');
1456: }
1457:
1458: if (!$mediaValueTableName) {
1459: $mediaValueTableName = Mage::getModel('importexport/import_proxy_product_resource')
1460: ->getTable('catalog/product_attribute_media_gallery_value');
1461: }
1462:
1463: foreach ($mediaGalleryData as $productSku => $mediaGalleryRows) {
1464: $productId = $this->_newSku[$productSku]['entity_id'];
1465: $insertedGalleryImgs = array();
1466:
1467: if (Mage_ImportExport_Model_Import::BEHAVIOR_APPEND != $this->getBehavior()) {
1468: $this->_connection->delete(
1469: $mediaGalleryTableName,
1470: $this->_connection->quoteInto('entity_id IN (?)', $productId)
1471: );
1472: }
1473:
1474: foreach ($mediaGalleryRows as $insertValue) {
1475:
1476: if (!in_array($insertValue['value'], $insertedGalleryImgs)) {
1477: $valueArr = array(
1478: 'attribute_id' => $insertValue['attribute_id'],
1479: 'entity_id' => $productId,
1480: 'value' => $insertValue['value']
1481: );
1482:
1483: $this->_connection
1484: ->insertOnDuplicate($mediaGalleryTableName, $valueArr, array('entity_id'));
1485:
1486: $insertedGalleryImgs[] = $insertValue['value'];
1487: }
1488:
1489: $newMediaValues = $this->_connection->fetchPairs($this->_connection->select()
1490: ->from($mediaGalleryTableName, array('value', 'value_id'))
1491: ->where('entity_id IN (?)', $productId)
1492: );
1493:
1494: if (array_key_exists($insertValue['value'], $newMediaValues)) {
1495: $insertValue['value_id'] = $newMediaValues[$insertValue['value']];
1496: }
1497:
1498: $valueArr = array(
1499: 'value_id' => $insertValue['value_id'],
1500: 'store_id' => Mage_Catalog_Model_Abstract::DEFAULT_STORE_ID,
1501: 'label' => $insertValue['label'],
1502: 'position' => $insertValue['position'],
1503: 'disabled' => $insertValue['disabled']
1504: );
1505:
1506: try {
1507: $this->_connection
1508: ->insertOnDuplicate($mediaValueTableName, $valueArr, array('value_id'));
1509: } catch (Exception $e) {
1510: $this->_connection->delete(
1511: $mediaGalleryTableName, $this->_connection->quoteInto('value_id IN (?)', $newMediaValues)
1512: );
1513: }
1514: }
1515: }
1516:
1517: return $this;
1518: }
1519:
1520: 1521: 1522: 1523: 1524: 1525:
1526: protected function _saveProductWebsites(array $websiteData)
1527: {
1528: static $tableName = null;
1529:
1530: if (!$tableName) {
1531: $tableName = Mage::getModel('importexport/import_proxy_product_resource')->getProductWebsiteTable();
1532: }
1533: if ($websiteData) {
1534: $websitesData = array();
1535: $delProductId = array();
1536:
1537: foreach ($websiteData as $delSku => $websites) {
1538: $productId = $this->_newSku[$delSku]['entity_id'];
1539: $delProductId[] = $productId;
1540:
1541: foreach (array_keys($websites) as $websiteId) {
1542: $websitesData[] = array(
1543: 'product_id' => $productId,
1544: 'website_id' => $websiteId
1545: );
1546: }
1547: }
1548: if (Mage_ImportExport_Model_Import::BEHAVIOR_APPEND != $this->getBehavior()) {
1549: $this->_connection->delete(
1550: $tableName,
1551: $this->_connection->quoteInto('product_id IN (?)', $delProductId)
1552: );
1553: }
1554: if ($websitesData) {
1555: $this->_connection->insertOnDuplicate($tableName, $websitesData);
1556: }
1557: }
1558: return $this;
1559: }
1560:
1561: 1562: 1563: 1564: 1565:
1566: protected function _saveStockItem()
1567: {
1568: $defaultStockData = array(
1569: 'manage_stock' => 1,
1570: 'use_config_manage_stock' => 1,
1571: 'qty' => 0,
1572: 'min_qty' => 0,
1573: 'use_config_min_qty' => 1,
1574: 'min_sale_qty' => 1,
1575: 'use_config_min_sale_qty' => 1,
1576: 'max_sale_qty' => 10000,
1577: 'use_config_max_sale_qty' => 1,
1578: 'is_qty_decimal' => 0,
1579: 'backorders' => 0,
1580: 'use_config_backorders' => 1,
1581: 'notify_stock_qty' => 1,
1582: 'use_config_notify_stock_qty' => 1,
1583: 'enable_qty_increments' => 0,
1584: 'use_config_enable_qty_inc' => 1,
1585: 'qty_increments' => 0,
1586: 'use_config_qty_increments' => 1,
1587: 'is_in_stock' => 0,
1588: 'low_stock_date' => null,
1589: 'stock_status_changed_auto' => 0,
1590: 'is_decimal_divided' => 0
1591: );
1592:
1593: $entityTable = Mage::getResourceModel('cataloginventory/stock_item')->getMainTable();
1594: $helper = Mage::helper('catalogInventory');
1595:
1596: while ($bunch = $this->_dataSourceModel->getNextBunch()) {
1597: $stockData = array();
1598:
1599:
1600: foreach ($bunch as $rowNum => $rowData) {
1601: if (!$this->isRowAllowedToImport($rowData, $rowNum)) {
1602: continue;
1603: }
1604:
1605: if (self::SCOPE_DEFAULT != $this->getRowScope($rowData)) {
1606: continue;
1607: }
1608:
1609: $row['product_id'] = $this->_newSku[$rowData[self::COL_SKU]]['entity_id'];
1610: $row['stock_id'] = 1;
1611:
1612:
1613: $stockItem = Mage::getModel('cataloginventory/stock_item');
1614: $stockItem->loadByProduct($row['product_id']);
1615: $existStockData = $stockItem->getData();
1616:
1617: $row = array_merge(
1618: $defaultStockData,
1619: array_intersect_key($existStockData, $defaultStockData),
1620: array_intersect_key($rowData, $defaultStockData),
1621: $row
1622: );
1623:
1624: $stockItem->setData($row);
1625:
1626: if ($helper->isQty($this->_newSku[$rowData[self::COL_SKU]]['type_id'])) {
1627: if ($stockItem->verifyNotification()) {
1628: $stockItem->setLowStockDate(Mage::app()->getLocale()
1629: ->date(null, null, null, false)
1630: ->toString(Varien_Date::DATETIME_INTERNAL_FORMAT)
1631: );
1632: }
1633: $stockItem->setStockStatusChangedAutomatically((int) !$stockItem->verifyStock());
1634: } else {
1635: $stockItem->setQty(0);
1636: }
1637: $stockData[] = $stockItem->unsetOldData()->getData();
1638: }
1639:
1640:
1641: if ($stockData) {
1642: $this->_connection->insertOnDuplicate($entityTable, $stockData);
1643: }
1644: }
1645: return $this;
1646: }
1647:
1648: 1649: 1650: 1651: 1652:
1653: public function getAttrSetIdToName()
1654: {
1655: return $this->_attrSetIdToName;
1656: }
1657:
1658: 1659: 1660: 1661: 1662:
1663: public function getConnection()
1664: {
1665: return $this->_connection;
1666: }
1667:
1668: 1669: 1670: 1671: 1672: 1673:
1674: public function getEntityTypeCode()
1675: {
1676: return 'catalog_product';
1677: }
1678:
1679: 1680: 1681: 1682: 1683:
1684: public function getNewSku()
1685: {
1686: return $this->_newSku;
1687: }
1688:
1689: 1690: 1691: 1692: 1693:
1694: public function getNextBunch()
1695: {
1696: return $this->_dataSourceModel->getNextBunch();
1697: }
1698:
1699: 1700: 1701: 1702: 1703:
1704: public function getOldSku()
1705: {
1706: return $this->_oldSku;
1707: }
1708:
1709: 1710: 1711: 1712: 1713: 1714:
1715: public function getRowScope(array $rowData)
1716: {
1717: if (strlen(trim($rowData[self::COL_SKU]))) {
1718: return self::SCOPE_DEFAULT;
1719: } elseif (empty($rowData[self::COL_STORE])) {
1720: return self::SCOPE_NULL;
1721: } else {
1722: return self::SCOPE_STORE;
1723: }
1724: }
1725:
1726: 1727: 1728: 1729: 1730:
1731: public function getWebsiteCodes()
1732: {
1733: return $this->_websiteCodeToId;
1734: }
1735:
1736: 1737: 1738: 1739: 1740: 1741: 1742:
1743: public function validateRow(array $rowData, $rowNum)
1744: {
1745: static $sku = null;
1746:
1747: if (isset($this->_validatedRows[$rowNum])) {
1748: return !isset($this->_invalidRows[$rowNum]);
1749: }
1750: $this->_validatedRows[$rowNum] = true;
1751:
1752: if (isset($this->_newSku[$rowData[self::COL_SKU]])) {
1753: $this->addRowError(self::ERROR_DUPLICATE_SKU, $rowNum);
1754: return false;
1755: }
1756: $rowScope = $this->getRowScope($rowData);
1757:
1758:
1759: if (Mage_ImportExport_Model_Import::BEHAVIOR_DELETE == $this->getBehavior()) {
1760: if (self::SCOPE_DEFAULT == $rowScope && !isset($this->_oldSku[$rowData[self::COL_SKU]])) {
1761: $this->addRowError(self::ERROR_SKU_NOT_FOUND_FOR_DELETE, $rowNum);
1762: return false;
1763: }
1764: return true;
1765: }
1766:
1767: $this->_isProductWebsiteValid($rowData, $rowNum);
1768: $this->_isProductCategoryValid($rowData, $rowNum);
1769: $this->_isTierPriceValid($rowData, $rowNum);
1770: $this->_isGroupPriceValid($rowData, $rowNum);
1771: $this->_isSuperProductsSkuValid($rowData, $rowNum);
1772:
1773: if (self::SCOPE_DEFAULT == $rowScope) {
1774: $this->_processedEntitiesCount ++;
1775:
1776: $sku = $rowData[self::COL_SKU];
1777:
1778: if (isset($this->_oldSku[$sku])) {
1779:
1780: if (isset($this->_productTypeModels[$this->_oldSku[$sku]['type_id']])) {
1781: $this->_newSku[$sku] = array(
1782: 'entity_id' => $this->_oldSku[$sku]['entity_id'],
1783: 'type_id' => $this->_oldSku[$sku]['type_id'],
1784: 'attr_set_id' => $this->_oldSku[$sku]['attr_set_id'],
1785: 'attr_set_code' => $this->_attrSetIdToName[$this->_oldSku[$sku]['attr_set_id']]
1786: );
1787: } else {
1788: $this->addRowError(self::ERROR_TYPE_UNSUPPORTED, $rowNum);
1789: $sku = false;
1790: }
1791: } else {
1792: if (!isset($rowData[self::COL_TYPE])
1793: || !isset($this->_productTypeModels[$rowData[self::COL_TYPE]])
1794: ) {
1795: $this->addRowError(self::ERROR_INVALID_TYPE, $rowNum);
1796: } elseif (!isset($rowData[self::COL_ATTR_SET])
1797: || !isset($this->_attrSetNameToId[$rowData[self::COL_ATTR_SET]])
1798: ) {
1799: $this->addRowError(self::ERROR_INVALID_ATTR_SET, $rowNum);
1800: } elseif (!isset($this->_newSku[$sku])) {
1801: $this->_newSku[$sku] = array(
1802: 'entity_id' => null,
1803: 'type_id' => $rowData[self::COL_TYPE],
1804: 'attr_set_id' => $this->_attrSetNameToId[$rowData[self::COL_ATTR_SET]],
1805: 'attr_set_code' => $rowData[self::COL_ATTR_SET]
1806: );
1807: }
1808: if (isset($this->_invalidRows[$rowNum])) {
1809:
1810: $sku = false;
1811: }
1812: }
1813: } else {
1814: if (null === $sku) {
1815: $this->addRowError(self::ERROR_SKU_IS_EMPTY, $rowNum);
1816: } elseif (false === $sku) {
1817: $this->addRowError(self::ERROR_ROW_IS_ORPHAN, $rowNum);
1818: } elseif (self::SCOPE_STORE == $rowScope && !isset($this->_storeCodeToId[$rowData[self::COL_STORE]])) {
1819: $this->addRowError(self::ERROR_INVALID_STORE, $rowNum);
1820: }
1821: }
1822: if (!isset($this->_invalidRows[$rowNum])) {
1823:
1824: $rowData[self::COL_ATTR_SET] = $this->_newSku[$sku]['attr_set_code'];
1825:
1826: $rowAttributesValid = $this->_productTypeModels[$this->_newSku[$sku]['type_id']]->isRowValid(
1827: $rowData, $rowNum, !isset($this->_oldSku[$sku])
1828: );
1829: if (!$rowAttributesValid && self::SCOPE_DEFAULT == $rowScope && !isset($this->_oldSku[$sku])) {
1830: $sku = false;
1831: }
1832: }
1833: return !isset($this->_invalidRows[$rowNum]);
1834: }
1835:
1836: 1837: 1838: 1839: 1840:
1841: public function getAffectedEntityIds()
1842: {
1843: $productIds = array();
1844: while ($bunch = $this->_dataSourceModel->getNextBunch()) {
1845: foreach ($bunch as $rowNum => $rowData) {
1846: if (!$this->isRowAllowedToImport($rowData, $rowNum)) {
1847: continue;
1848: }
1849: if (!isset($this->_newSku[$rowData[self::COL_SKU]]['entity_id'])) {
1850: continue;
1851: }
1852: $productIds[] = $this->_newSku[$rowData[self::COL_SKU]]['entity_id'];
1853: }
1854: }
1855: return $productIds;
1856: }
1857: }
1858: