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_Customer extends Mage_ImportExport_Model_Import_Entity_Abstract
35: {
36: 37: 38:
39: const BUNCH_SIZE = 20;
40:
41: 42: 43:
44: const SCOPE_DEFAULT = 1;
45: const SCOPE_ADDRESS = -1;
46:
47: 48: 49: 50: 51: 52:
53: const COL_EMAIL = 'email';
54: const COL_WEBSITE = '_website';
55: const COL_STORE = '_store';
56:
57: 58: 59:
60: const ERROR_INVALID_WEBSITE = 'invalidWebsite';
61: const ERROR_INVALID_EMAIL = 'invalidEmail';
62: const ERROR_DUPLICATE_EMAIL_SITE = 'duplicateEmailSite';
63: const ERROR_EMAIL_IS_EMPTY = 'emailIsEmpty';
64: const ERROR_ROW_IS_ORPHAN = 'rowIsOrphan';
65: const ERROR_VALUE_IS_REQUIRED = 'valueIsRequired';
66: const ERROR_INVALID_STORE = 'invalidStore';
67: const ERROR_EMAIL_SITE_NOT_FOUND = 'emailSiteNotFound';
68: const ERROR_PASSWORD_LENGTH = 'passwordLength';
69:
70: 71: 72: 73:
74: const DEFAULT_GROUP_ID = 1;
75: const MAX_PASSWD_LENGTH = 6;
76:
77: 78: 79: 80: 81:
82: protected $_addressEntity;
83:
84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95:
96: protected $_attributes = array();
97:
98: 99: 100: 101: 102:
103: protected $_customerGlobal;
104:
105: 106: 107: 108: 109:
110: protected $_customerGroups = array();
111:
112: 113: 114: 115: 116:
117: protected $_entityTable;
118:
119: 120: 121: 122: 123: 124: 125:
126: protected $_ignoredAttributes = array('website_id', 'store_id', 'default_billing', 'default_shipping');
127:
128: 129: 130: 131: 132:
133: protected $_indexValueAttributes = array('group_id');
134:
135: 136: 137: 138: 139:
140: protected $_messageTemplates = array(
141: self::ERROR_INVALID_WEBSITE => 'Invalid value in Website column (website does not exists?)',
142: self::ERROR_INVALID_EMAIL => 'E-mail is invalid',
143: self::ERROR_DUPLICATE_EMAIL_SITE => 'E-mail is duplicated in import file',
144: self::ERROR_EMAIL_IS_EMPTY => 'E-mail is not specified',
145: self::ERROR_ROW_IS_ORPHAN => 'Orphan rows that will be skipped due default row errors',
146: self::ERROR_VALUE_IS_REQUIRED => "Required attribute '%s' has an empty value",
147: self::ERROR_INVALID_STORE => 'Invalid value in Store column (store does not exists?)',
148: self::ERROR_EMAIL_SITE_NOT_FOUND => 'E-mail and website combination is not found',
149: self::ERROR_PASSWORD_LENGTH => 'Invalid password length'
150: );
151:
152: 153: 154: 155: 156:
157: protected $_newCustomers = array();
158:
159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170:
171: protected $_oldCustomers = array();
172:
173: 174: 175: 176: 177:
178: protected $_particularAttributes = array(self::COL_WEBSITE, self::COL_STORE);
179:
180: 181: 182: 183: 184:
185: protected $_permanentAttributes = array(self::COL_EMAIL, self::COL_WEBSITE);
186:
187: 188: 189: 190: 191:
192: protected $_storeCodeToId = array();
193:
194: 195: 196: 197: 198:
199: protected $_websiteCodeToId = array();
200:
201: 202: 203: 204: 205:
206: protected $_websiteIdToCode = array();
207:
208: 209: 210: 211: 212:
213: public function __construct()
214: {
215: parent::__construct();
216:
217: $this->_initWebsites()
218: ->_initStores()
219: ->_initCustomerGroups()
220: ->_initAttributes()
221: ->_initCustomers();
222:
223: $this->_entityTable = Mage::getModel('customer/customer')->getResource()->getEntityTable();
224: $this->_addressEntity = Mage::getModel('importexport/import_entity_customer_address', $this);
225: }
226:
227: 228: 229: 230: 231:
232: protected function _deleteCustomers()
233: {
234: while ($bunch = $this->_dataSourceModel->getNextBunch()) {
235: $idToDelete = array();
236:
237: foreach ($bunch as $rowNum => $rowData) {
238: if (self::SCOPE_DEFAULT == $this->getRowScope($rowData) && $this->validateRow($rowData, $rowNum)) {
239: $idToDelete[] = $this->_oldCustomers[$rowData[self::COL_EMAIL]][$rowData[self::COL_WEBSITE]];
240: }
241: }
242: if ($idToDelete) {
243: $this->_connection->query(
244: $this->_connection->quoteInto(
245: "DELETE FROM `{$this->_entityTable}` WHERE `entity_id` IN (?)", $idToDelete
246: )
247: );
248: }
249: }
250: return $this;
251: }
252:
253: 254: 255: 256: 257: 258:
259: protected function _importData()
260: {
261: if (Mage_ImportExport_Model_Import::BEHAVIOR_DELETE == $this->getBehavior()) {
262: $this->_deleteCustomers();
263: } else {
264: $this->_saveCustomers();
265: $this->_addressEntity->importData();
266: }
267: return true;
268: }
269:
270: 271: 272: 273: 274:
275: protected function _initAttributes()
276: {
277: $collection = Mage::getResourceModel('customer/attribute_collection')->addSystemHiddenFilterWithPasswordHash();
278: foreach ($collection as $attribute) {
279: $this->_attributes[$attribute->getAttributeCode()] = array(
280: 'id' => $attribute->getId(),
281: 'is_required' => $attribute->getIsRequired(),
282: 'is_static' => $attribute->isStatic(),
283: 'rules' => $attribute->getValidateRules() ? unserialize($attribute->getValidateRules()) : null,
284: 'type' => Mage_ImportExport_Model_Import::getAttributeType($attribute),
285: 'options' => $this->getAttributeOptions($attribute)
286: );
287: }
288: return $this;
289: }
290:
291: 292: 293: 294: 295:
296: protected function _initCustomerGroups()
297: {
298: foreach (Mage::getResourceModel('customer/group_collection') as $customerGroup) {
299: $this->_customerGroups[$customerGroup->getId()] = true;
300: }
301: return $this;
302: }
303:
304: 305: 306: 307: 308:
309: protected function _initCustomers()
310: {
311: foreach (Mage::getResourceModel('customer/customer_collection') as $customer) {
312: $email = $customer->getEmail();
313:
314: if (!isset($this->_oldCustomers[$email])) {
315: $this->_oldCustomers[$email] = array();
316: }
317: $this->_oldCustomers[$email][$this->_websiteIdToCode[$customer->getWebsiteId()]] = $customer->getId();
318: }
319: $this->_customerGlobal = Mage::getModel('customer/customer')->getSharingConfig()->isGlobalScope();
320:
321: return $this;
322: }
323:
324: 325: 326: 327: 328:
329: protected function _initStores()
330: {
331: foreach (Mage::app()->getStores(true) as $store) {
332: $this->_storeCodeToId[$store->getCode()] = $store->getId();
333: }
334: return $this;
335: }
336:
337: 338: 339: 340: 341:
342: protected function _initWebsites()
343: {
344:
345: foreach (Mage::app()->getWebsites(true) as $website) {
346: $this->_websiteCodeToId[$website->getCode()] = $website->getId();
347: $this->_websiteIdToCode[$website->getId()] = $website->getCode();
348: }
349: return $this;
350: }
351:
352: 353: 354: 355: 356:
357: protected function _saveCustomers()
358: {
359:
360: $resource = Mage::getModel('customer/customer');
361: $strftimeFormat = Varien_Date::convertZendToStrftime(Varien_Date::DATETIME_INTERNAL_FORMAT, true, true);
362: $table = $resource->getResource()->getEntityTable();
363: $nextEntityId = Mage::getResourceHelper('importexport')->getNextAutoincrement($table);
364: $passId = $resource->getAttribute('password_hash')->getId();
365: $passTable = $resource->getAttribute('password_hash')->getBackend()->getTable();
366:
367: while ($bunch = $this->_dataSourceModel->getNextBunch()) {
368: $entityRowsIn = array();
369: $entityRowsUp = array();
370: $attributes = array();
371:
372: foreach ($bunch as $rowNum => $rowData) {
373: if (!$this->validateRow($rowData, $rowNum)) {
374: continue;
375: }
376: if (self::SCOPE_DEFAULT == $this->getRowScope($rowData)) {
377:
378: $entityRow = array(
379: 'group_id' => empty($rowData['group_id']) ? self::DEFAULT_GROUP_ID : $rowData['group_id'],
380: 'store_id' => empty($rowData[self::COL_STORE])
381: ? 0 : $this->_storeCodeToId[$rowData[self::COL_STORE]],
382: 'created_at' => empty($rowData['created_at'])
383: ? now() : gmstrftime($strftimeFormat, strtotime($rowData['created_at'])),
384: 'updated_at' => now()
385: );
386: if (isset($this->_oldCustomers[$rowData[self::COL_EMAIL]][$rowData[self::COL_WEBSITE]])) {
387: $entityId = $this->_oldCustomers[$rowData[self::COL_EMAIL]][$rowData[self::COL_WEBSITE]];
388: $entityRow['entity_id'] = $entityId;
389: $entityRowsUp[] = $entityRow;
390: } else {
391: $entityId = $nextEntityId++;
392: $entityRow['entity_id'] = $entityId;
393: $entityRow['entity_type_id'] = $this->_entityTypeId;
394: $entityRow['attribute_set_id'] = 0;
395: $entityRow['website_id'] = $this->_websiteCodeToId[$rowData[self::COL_WEBSITE]];
396: $entityRow['email'] = $rowData[self::COL_EMAIL];
397: $entityRow['is_active'] = 1;
398: $entityRowsIn[] = $entityRow;
399:
400: $this->_newCustomers[$rowData[self::COL_EMAIL]][$rowData[self::COL_WEBSITE]] = $entityId;
401: }
402:
403: foreach (array_intersect_key($rowData, $this->_attributes) as $attrCode => $value) {
404: if (!$this->_attributes[$attrCode]['is_static'] && strlen($value)) {
405:
406: $attribute = $resource->getAttribute($attrCode);
407: $backModel = $attribute->getBackendModel();
408: $attrParams = $this->_attributes[$attrCode];
409:
410: if ('select' == $attrParams['type']) {
411: $value = $attrParams['options'][strtolower($value)];
412: } elseif ('datetime' == $attrParams['type']) {
413: $value = gmstrftime($strftimeFormat, strtotime($value));
414: } elseif ($backModel) {
415: $attribute->getBackend()->beforeSave($resource->setData($attrCode, $value));
416: $value = $resource->getData($attrCode);
417: }
418: $attributes[$attribute->getBackend()->getTable()][$entityId][$attrParams['id']] = $value;
419:
420:
421: $attribute->setBackendModel($backModel);
422: }
423: }
424:
425: if (isset($rowData['password']) && strlen($rowData['password'])) {
426: $attributes[$passTable][$entityId][$passId] = $resource->hashPassword($rowData['password']);
427: }
428: }
429: }
430: $this->_saveCustomerEntity($entityRowsIn, $entityRowsUp)->_saveCustomerAttributes($attributes);
431: }
432: return $this;
433: }
434:
435: 436: 437: 438: 439: 440:
441: protected function _saveCustomerAttributes(array $attributesData)
442: {
443: foreach ($attributesData as $tableName => $data) {
444: $tableData = array();
445:
446: foreach ($data as $customerId => $attrData) {
447: foreach ($attrData as $attributeId => $value) {
448: $tableData[] = array(
449: 'entity_id' => $customerId,
450: 'entity_type_id' => $this->_entityTypeId,
451: 'attribute_id' => $attributeId,
452: 'value' => $value
453: );
454: }
455: }
456: $this->_connection->insertOnDuplicate($tableName, $tableData, array('value'));
457: }
458: return $this;
459: }
460:
461: 462: 463: 464: 465: 466: 467:
468: protected function _saveCustomerEntity(array $entityRowsIn, array $entityRowsUp)
469: {
470: if ($entityRowsIn) {
471: $this->_connection->insertMultiple($this->_entityTable, $entityRowsIn);
472: }
473: if ($entityRowsUp) {
474: $this->_connection->insertOnDuplicate(
475: $this->_entityTable,
476: $entityRowsUp,
477: array('group_id', 'store_id', 'updated_at', 'created_at')
478: );
479: }
480: return $this;
481: }
482:
483: 484: 485: 486: 487: 488: 489:
490: public function getCustomerId($email, $websiteCode)
491: {
492: if (isset($this->_oldCustomers[$email][$websiteCode])) {
493: return $this->_oldCustomers[$email][$websiteCode];
494: } elseif (isset($this->_newCustomers[$email][$websiteCode])) {
495: return $this->_newCustomers[$email][$websiteCode];
496: } else {
497: return null;
498: }
499: }
500:
501: 502: 503: 504: 505: 506:
507: public function getEntityTypeCode()
508: {
509: return 'customer';
510: }
511:
512: 513: 514: 515: 516: 517:
518: public function getRowScope(array $rowData)
519: {
520: return strlen(trim($rowData[self::COL_EMAIL])) ? self::SCOPE_DEFAULT : self::SCOPE_ADDRESS;
521: }
522:
523: 524: 525: 526: 527: 528:
529: public function isAttributeParticular($attrCode)
530: {
531: return parent::isAttributeParticular($attrCode) || $this->_addressEntity->isAttributeParticular($attrCode);
532: }
533:
534: 535: 536: 537: 538: 539: 540:
541: public function validateRow(array $rowData, $rowNum)
542: {
543: static $email = null;
544: static $website = null;
545:
546: if (isset($this->_validatedRows[$rowNum])) {
547: return !isset($this->_invalidRows[$rowNum]);
548: }
549: $this->_validatedRows[$rowNum] = true;
550:
551: $rowScope = $this->getRowScope($rowData);
552:
553: if (self::SCOPE_DEFAULT == $rowScope) {
554: $this->_processedEntitiesCount ++;
555: }
556:
557: if (Mage_ImportExport_Model_Import::BEHAVIOR_DELETE == $this->getBehavior()) {
558: if (self::SCOPE_DEFAULT == $rowScope
559: && !isset($this->_oldCustomers[$rowData[self::COL_EMAIL]][$rowData[self::COL_WEBSITE]])) {
560: $this->addRowError(self::ERROR_EMAIL_SITE_NOT_FOUND, $rowNum);
561: }
562: } elseif (self::SCOPE_DEFAULT == $rowScope) {
563: $email = $rowData[self::COL_EMAIL];
564: $website = $rowData[self::COL_WEBSITE];
565:
566: if (!Zend_Validate::is($email, 'EmailAddress')) {
567: $this->addRowError(self::ERROR_INVALID_EMAIL, $rowNum);
568: } elseif (!isset($this->_websiteCodeToId[$website])) {
569: $this->addRowError(self::ERROR_INVALID_WEBSITE, $rowNum);
570: } else {
571: if (isset($this->_newCustomers[$email][$website])) {
572: $this->addRowError(self::ERROR_DUPLICATE_EMAIL_SITE, $rowNum);
573: }
574: $this->_newCustomers[$email][$website] = false;
575:
576: if (!empty($rowData[self::COL_STORE]) && !isset($this->_storeCodeToId[$rowData[self::COL_STORE]])) {
577: $this->addRowError(self::ERROR_INVALID_STORE, $rowNum);
578: }
579:
580: if (isset($rowData['password']) && strlen($rowData['password'])
581: && Mage::helper('core/string')->strlen($rowData['password']) < self::MAX_PASSWD_LENGTH
582: ) {
583: $this->addRowError(self::ERROR_PASSWORD_LENGTH, $rowNum);
584: }
585:
586: foreach ($this->_attributes as $attrCode => $attrParams) {
587: if (in_array($attrCode, $this->_ignoredAttributes)) {
588: continue;
589: }
590: if (isset($rowData[$attrCode]) && strlen($rowData[$attrCode])) {
591: $this->isAttributeValid($attrCode, $attrParams, $rowData, $rowNum);
592: } elseif ($attrParams['is_required'] && !isset($this->_oldCustomers[$email][$website])) {
593: $this->addRowError(self::ERROR_VALUE_IS_REQUIRED, $rowNum, $attrCode);
594: }
595: }
596: }
597: if (isset($this->_invalidRows[$rowNum])) {
598: $email = false;
599: }
600: } else {
601: if (null === $email) {
602: $this->addRowError(self::ERROR_EMAIL_IS_EMPTY, $rowNum);
603: } elseif (false === $email) {
604: $this->addRowError(self::ERROR_ROW_IS_ORPHAN, $rowNum);
605: }
606: }
607:
608: $this->_addressEntity->validateRow($rowData, $rowNum);
609:
610: return !isset($this->_invalidRows[$rowNum]);
611: }
612: }
613: