Magento 2 Documentation  2.3
Documentation for Magento 2 CMS v2.3 (December 2018)
Customer.php
Go to the documentation of this file.
1 <?php
7 
12 
22 {
26  const ATTRIBUTE_COLLECTION_NAME = \Magento\Customer\Model\ResourceModel\Attribute\Collection::class;
27 
34  const COLUMN_EMAIL = 'email';
35 
36  const COLUMN_STORE = '_store';
37 
38  const COLUMN_PASSWORD = 'password';
39 
45  const ERROR_DUPLICATE_EMAIL_SITE = 'duplicateEmailSite';
46 
47  const ERROR_ROW_IS_ORPHAN = 'rowIsOrphan';
48 
49  const ERROR_INVALID_STORE = 'invalidStore';
50 
51  const ERROR_EMAIL_SITE_NOT_FOUND = 'emailSiteNotFound';
52 
53  const ERROR_PASSWORD_LENGTH = 'passwordLength';
54 
60  const ENTITIES_TO_CREATE_KEY = 'entities_to_create';
61 
62  const ENTITIES_TO_UPDATE_KEY = 'entities_to_update';
63 
64  const ATTRIBUTES_TO_SAVE_KEY = 'attributes_to_save';
65 
72 
76  const DEFAULT_GROUP_ID = 1;
77 
83  protected $_newCustomers = [];
84 
92  protected $_ignoredAttributes = ['website_id', 'store_id'];
93 
99  protected $_entityTable;
100 
106  protected $_customerModel;
107 
113  protected $_nextEntityId;
114 
121 
125  protected $_resourceHelper;
126 
130  protected $masterAttributeCode = 'email';
131 
137  protected $validColumnNames = [
141  ];
142 
146  protected $customerFields = [
158  'password_hash',
162  'rp_token',
163  'rp_token_created_at',
164  'failures_num',
165  'first_failure',
166  'lock_expires',
167  ];
168 
185  public function __construct(
186  \Magento\Framework\Stdlib\StringUtils $string,
187  \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
188  \Magento\ImportExport\Model\ImportFactory $importFactory,
189  \Magento\ImportExport\Model\ResourceModel\Helper $resourceHelper,
192  \Magento\Store\Model\StoreManagerInterface $storeManager,
193  \Magento\ImportExport\Model\Export\Factory $collectionFactory,
194  \Magento\Eav\Model\Config $eavConfig,
195  \Magento\CustomerImportExport\Model\ResourceModel\Import\Customer\StorageFactory $storageFactory,
196  \Magento\Customer\Model\ResourceModel\Attribute\CollectionFactory $attrCollectionFactory,
197  \Magento\Customer\Model\CustomerFactory $customerFactory,
198  array $data = []
199  ) {
200  $this->_resourceHelper = $resourceHelper;
201 
202  if (isset($data['attribute_collection'])) {
203  $this->_attributeCollection = $data['attribute_collection'];
204  unset($data['attribute_collection']);
205  } else {
206  $this->_attributeCollection = $attrCollectionFactory->create();
207  $this->_attributeCollection->addSystemHiddenFilterWithPasswordHash();
208  $data['attribute_collection'] = $this->_attributeCollection;
209  }
210 
211  parent::__construct(
212  $string,
213  $scopeConfig,
214  $importFactory,
215  $resourceHelper,
216  $resource,
219  $collectionFactory,
220  $eavConfig,
221  $storageFactory,
222  $data
223  );
224 
225  $this->_specialAttributes[] = self::COLUMN_WEBSITE;
226  $this->_specialAttributes[] = self::COLUMN_STORE;
227  $this->_permanentAttributes[] = self::COLUMN_EMAIL;
228  $this->_permanentAttributes[] = self::COLUMN_WEBSITE;
229  $this->_indexValueAttributes[] = 'group_id';
230 
231  $this->addMessageTemplate(
232  self::ERROR_DUPLICATE_EMAIL_SITE,
233  __('This email is found more than once in the import file.')
234  );
235  $this->addMessageTemplate(
236  self::ERROR_ROW_IS_ORPHAN,
237  __('Orphan rows that will be skipped due default row errors')
238  );
239  $this->addMessageTemplate(
240  self::ERROR_INVALID_STORE,
241  __('Invalid value in Store column (store does not exists?)')
242  );
243  $this->addMessageTemplate(
244  self::ERROR_EMAIL_SITE_NOT_FOUND,
245  __('We can\'t find that email and website combination.')
246  );
247  $this->addMessageTemplate(self::ERROR_PASSWORD_LENGTH, __('Please enter a password with a valid length.'));
248 
249  $this->_initStores(true)->_initAttributes();
250 
251  $this->_customerModel = $customerFactory->create();
253  $customerResource = $this->_customerModel->getResource();
254  $this->_entityTable = $customerResource->getEntityTable();
255  }
256 
264  protected function _saveCustomerEntities(array $entitiesToCreate, array $entitiesToUpdate)
265  {
266  if ($entitiesToCreate) {
267  $this->_connection->insertMultiple($this->_entityTable, $entitiesToCreate);
268  }
269 
270  if ($entitiesToUpdate) {
271  $this->_connection->insertOnDuplicate(
272  $this->_entityTable,
273  $entitiesToUpdate,
274  $this->getCustomerEntityFieldsToUpdate($entitiesToUpdate)
275  );
276  }
277 
278  return $this;
279  }
280 
287  private function getCustomerEntityFieldsToUpdate(array $entitiesToUpdate): array
288  {
289  $firstCustomer = reset($entitiesToUpdate);
290  $columnsToUpdate = array_keys($firstCustomer);
291  $customerFieldsToUpdate = array_filter($this->customerFields, function ($field) use ($columnsToUpdate) {
292  return in_array($field, $columnsToUpdate);
293  });
294  return $customerFieldsToUpdate;
295  }
296 
303  protected function _saveCustomerAttributes(array $attributesData)
304  {
305  foreach ($attributesData as $tableName => $data) {
306  $tableData = [];
307 
308  foreach ($data as $customerId => $attributeData) {
309  foreach ($attributeData as $attributeId => $value) {
310  $tableData[] = [
311  'entity_id' => $customerId,
312  'attribute_id' => $attributeId,
313  'value' => $value,
314  ];
315  }
316  }
317  $this->_connection->insertOnDuplicate($tableName, $tableData, ['value']);
318  }
319  return $this;
320  }
321 
328  protected function _deleteCustomerEntities(array $entitiesToDelete)
329  {
330  $condition = $this->_connection->quoteInto('entity_id IN (?)', $entitiesToDelete);
331  $this->_connection->delete($this->_entityTable, $condition);
332 
333  return $this;
334  }
335 
341  protected function _getNextEntityId()
342  {
343  if (!$this->_nextEntityId) {
344  $this->_nextEntityId = $this->_resourceHelper->getNextAutoincrement($this->_entityTable);
345  }
346  return $this->_nextEntityId++;
347  }
348 
357  public function prepareCustomerData($rows): void
358  {
359  $customersPresent = [];
360  foreach ($rows as $rowData) {
361  $email = $rowData[static::COLUMN_EMAIL] ?? null;
362  $websiteId = isset($rowData[static::COLUMN_WEBSITE])
363  ? $this->getWebsiteId($rowData[static::COLUMN_WEBSITE]) : false;
364  if ($email && $websiteId !== false) {
365  $customersPresent[] = [
366  'email' => $email,
367  'website_id' => $websiteId,
368  ];
369  }
370  }
371  $this->getCustomerStorage()->prepareCustomers($customersPresent);
372  }
373 
378  public function validateData()
379  {
380  $this->prepareCustomerData($this->getSource());
381 
382  return parent::validateData();
383  }
384 
393  protected function _prepareDataForUpdate(array $rowData)
394  {
395  $multiSeparator = $this->getMultipleValueSeparator();
396  $entitiesToCreate = [];
397  $entitiesToUpdate = [];
398  $attributesToSave = [];
399 
400  // entity table data
401  $now = new \DateTime();
402  if (empty($rowData['created_at'])) {
403  $createdAt = $now;
404  } else {
405  $createdAt = (new \DateTime())->setTimestamp(strtotime($rowData['created_at']));
406  }
407 
408  $emailInLowercase = strtolower($rowData[self::COLUMN_EMAIL]);
409  $newCustomer = false;
410  $entityId = $this->_getCustomerId($emailInLowercase, $rowData[self::COLUMN_WEBSITE]);
411  if (!$entityId) {
412  // create
413  $newCustomer = true;
414  $entityId = $this->_getNextEntityId();
415  $this->_newCustomers[$emailInLowercase][$rowData[self::COLUMN_WEBSITE]] = $entityId;
416  }
417 
418  // password change/set
419  if (isset($rowData['password']) && strlen($rowData['password'])) {
420  $rowData['password_hash'] = $this->_customerModel->hashPassword($rowData['password']);
421  }
422  $entityRow = ['entity_id' => $entityId];
423  // attribute values
424  foreach (array_intersect_key($rowData, $this->_attributes) as $attributeCode => $value) {
425  $attributeParameters = $this->_attributes[$attributeCode];
426  if (in_array($attributeParameters['type'], ['select', 'boolean'])) {
427  $value = $this->getSelectAttrIdByValue($attributeParameters, $value);
428  } elseif ('multiselect' == $attributeParameters['type']) {
429  $ids = [];
430  foreach (explode($multiSeparator, mb_strtolower($value)) as $subValue) {
431  $ids[] = $this->getSelectAttrIdByValue($attributeParameters, $subValue);
432  }
433  $value = implode(',', $ids);
434  } elseif ('datetime' == $attributeParameters['type'] && !empty($value)) {
435  $value = (new \DateTime())->setTimestamp(strtotime($value));
436  $value = $value->format(\Magento\Framework\Stdlib\DateTime::DATETIME_PHP_FORMAT);
437  }
438 
439  if (!$this->_attributes[$attributeCode]['is_static']) {
441  $attribute = $this->_customerModel->getAttribute($attributeCode);
442  $backendModel = $attribute->getBackendModel();
443  if ($backendModel
444  && $attribute->getFrontendInput() != 'select'
445  && $attribute->getFrontendInput() != 'datetime') {
446  $attribute->getBackend()->beforeSave($this->_customerModel->setData($attributeCode, $value));
447  $value = $this->_customerModel->getData($attributeCode);
448  }
449  $attributesToSave[$attribute->getBackend()
450  ->getTable()][$entityId][$attributeParameters['id']] = $value;
451 
452  // restore 'backend_model' to avoid default setting
453  $attribute->setBackendModel($backendModel);
454  } else {
455  $entityRow[$attributeCode] = $value;
456  }
457  }
458 
459  if ($newCustomer) {
460  // create
461  $entityRow['group_id'] = empty($rowData['group_id']) ? self::DEFAULT_GROUP_ID : $rowData['group_id'];
462  $entityRow['store_id'] = empty($rowData[self::COLUMN_STORE])
463  ? \Magento\Store\Model\Store::DEFAULT_STORE_ID : $this->_storeCodeToId[$rowData[self::COLUMN_STORE]];
464  $entityRow['created_at'] = $createdAt->format(\Magento\Framework\Stdlib\DateTime::DATETIME_PHP_FORMAT);
465  $entityRow['updated_at'] = $now->format(\Magento\Framework\Stdlib\DateTime::DATETIME_PHP_FORMAT);
466  $entityRow['website_id'] = $this->_websiteCodeToId[$rowData[self::COLUMN_WEBSITE]];
467  $entityRow['email'] = $emailInLowercase;
468  $entityRow['is_active'] = 1;
469  $entitiesToCreate[] = $entityRow;
470  } else {
471  // edit
472  $entityRow['updated_at'] = $now->format(\Magento\Framework\Stdlib\DateTime::DATETIME_PHP_FORMAT);
473  if (!empty($rowData[self::COLUMN_STORE])) {
474  $entityRow['store_id'] = $this->_storeCodeToId[$rowData[self::COLUMN_STORE]];
475  }
476  $entitiesToUpdate[] = $entityRow;
477  }
478 
479  return [
480  self::ENTITIES_TO_CREATE_KEY => $entitiesToCreate,
481  self::ENTITIES_TO_UPDATE_KEY => $entitiesToUpdate,
482  self::ATTRIBUTES_TO_SAVE_KEY => $attributesToSave
483  ];
484  }
485 
493  protected function _importData()
494  {
495  while ($bunch = $this->_dataSourceModel->getNextBunch()) {
496  $this->prepareCustomerData($bunch);
497  $entitiesToCreate = [];
498  $entitiesToUpdate = [];
499  $entitiesToDelete = [];
500  $attributesToSave = [];
501 
502  foreach ($bunch as $rowNumber => $rowData) {
503  if (!$this->validateRow($rowData, $rowNumber)) {
504  continue;
505  }
506  if ($this->getErrorAggregator()->hasToBeTerminated()) {
507  $this->getErrorAggregator()->addRowToSkip($rowNumber);
508  continue;
509  }
510 
511  if ($this->getBehavior($rowData) == \Magento\ImportExport\Model\Import::BEHAVIOR_DELETE) {
512  $entitiesToDelete[] = $this->_getCustomerId(
513  $rowData[self::COLUMN_EMAIL],
514  $rowData[self::COLUMN_WEBSITE]
515  );
516  } elseif ($this->getBehavior($rowData) == \Magento\ImportExport\Model\Import::BEHAVIOR_ADD_UPDATE) {
517  $processedData = $this->_prepareDataForUpdate($rowData);
518  $entitiesToCreate = array_merge($entitiesToCreate, $processedData[self::ENTITIES_TO_CREATE_KEY]);
519  $entitiesToUpdate = array_merge($entitiesToUpdate, $processedData[self::ENTITIES_TO_UPDATE_KEY]);
520  foreach ($processedData[self::ATTRIBUTES_TO_SAVE_KEY] as $tableName => $customerAttributes) {
521  if (!isset($attributesToSave[$tableName])) {
522  $attributesToSave[$tableName] = [];
523  }
524  $attributesToSave[$tableName] = array_diff_key(
525  $attributesToSave[$tableName],
526  $customerAttributes
527  ) + $customerAttributes;
528  }
529  }
530  }
531  $this->updateItemsCounterStats($entitiesToCreate, $entitiesToUpdate, $entitiesToDelete);
535  if ($entitiesToCreate || $entitiesToUpdate) {
536  $this->_saveCustomerEntities($entitiesToCreate, $entitiesToUpdate);
537  }
538  if ($attributesToSave) {
539  $this->_saveCustomerAttributes($attributesToSave);
540  }
541  if ($entitiesToDelete) {
542  $this->_deleteCustomerEntities($entitiesToDelete);
543  }
544  }
545 
546  return true;
547  }
548 
554  public function getEntityTypeCode()
555  {
556  return $this->_attributeCollection->getEntityTypeCode();
557  }
558 
568  protected function _validateRowForUpdate(array $rowData, $rowNumber)
569  {
570  if ($this->_checkUniqueKey($rowData, $rowNumber)) {
571  $email = strtolower($rowData[self::COLUMN_EMAIL]);
572  $website = $rowData[self::COLUMN_WEBSITE];
573 
574  if (isset($this->_newCustomers[strtolower($rowData[self::COLUMN_EMAIL])][$website])) {
575  $this->addRowError(self::ERROR_DUPLICATE_EMAIL_SITE, $rowNumber);
576  }
577  $this->_newCustomers[$email][$website] = false;
578 
579  if (!empty($rowData[self::COLUMN_STORE]) && !isset($this->_storeCodeToId[$rowData[self::COLUMN_STORE]])) {
580  $this->addRowError(self::ERROR_INVALID_STORE, $rowNumber);
581  }
582  // check password
583  if (isset(
584  $rowData['password']
585  ) && strlen(
586  $rowData['password']
587  ) && $this->string->strlen(
588  $rowData['password']
590  ) {
591  $this->addRowError(self::ERROR_PASSWORD_LENGTH, $rowNumber);
592  }
593  // check simple attributes
594  foreach ($this->_attributes as $attributeCode => $attributeParams) {
595  if (in_array($attributeCode, $this->_ignoredAttributes)) {
596  continue;
597  }
598 
599  $isFieldRequired = $attributeParams['is_required'];
600  $isFieldNotSetAndCustomerDoesNotExist =
601  !isset($rowData[$attributeCode]) && !$this->_getCustomerId($email, $website);
602  $isFieldSetAndTrimmedValueIsEmpty
603  = isset($rowData[$attributeCode]) && '' === trim($rowData[$attributeCode]);
604 
605  if ($isFieldRequired && ($isFieldNotSetAndCustomerDoesNotExist || $isFieldSetAndTrimmedValueIsEmpty)) {
606  $this->addRowError(self::ERROR_VALUE_IS_REQUIRED, $rowNumber, $attributeCode);
607  continue;
608  }
609 
610  if (isset($rowData[$attributeCode]) && strlen($rowData[$attributeCode])) {
611  $this->isAttributeValid(
612  $attributeCode,
613  $attributeParams,
614  $rowData,
615  $rowNumber,
616  isset($this->_parameters[Import::FIELD_FIELD_MULTIPLE_VALUE_SEPARATOR])
619  );
620  }
621  }
622  }
623  }
624 
632  protected function _validateRowForDelete(array $rowData, $rowNumber)
633  {
634  if ($this->_checkUniqueKey($rowData, $rowNumber)) {
635  if (!$this->_getCustomerId($rowData[self::COLUMN_EMAIL], $rowData[self::COLUMN_WEBSITE])) {
636  $this->addRowError(self::ERROR_CUSTOMER_NOT_FOUND, $rowNumber);
637  }
638  }
639  }
640 
646  public function getEntityTable()
647  {
648  return $this->_entityTable;
649  }
650 
654  public function getValidColumnNames()
655  {
656  return array_unique(
657  array_merge(
658  $this->validColumnNames,
659  $this->customerFields
660  )
661  );
662  }
663 }
$tableName
Definition: trigger.php:13
isAttributeValid( $attributeCode, array $attributeParams, array $rowData, $rowNumber, $multiSeparator=Import::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR)
elseif(isset( $params[ 'redirect_parent']))
Definition: iframe.phtml:17
$email
Definition: details.phtml:13
addRowError( $errorCode, $errorRowNum, $colName=null, $errorMessage=null, $errorLevel=ProcessingError::ERROR_LEVEL_CRITICAL, $errorDescription=null)
_validateRowForUpdate(array $rowData, $rowNumber)
Definition: Customer.php:568
$storeManager
__()
Definition: __.php:13
$resource
Definition: bulk.php:12
$attributesData
$value
Definition: gender.phtml:16
$attributeCode
Definition: extend.phtml:12
getSelectAttrIdByValue(array $attributeParameters, $value)
updateItemsCounterStats(array $created=[], array $updated=[], array $deleted=[])
__construct(\Magento\Framework\Stdlib\StringUtils $string, \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, \Magento\ImportExport\Model\ImportFactory $importFactory, \Magento\ImportExport\Model\ResourceModel\Helper $resourceHelper, \Magento\Framework\App\ResourceConnection $resource, ProcessingErrorAggregatorInterface $errorAggregator, \Magento\Store\Model\StoreManagerInterface $storeManager, \Magento\ImportExport\Model\Export\Factory $collectionFactory, \Magento\Eav\Model\Config $eavConfig, \Magento\CustomerImportExport\Model\ResourceModel\Import\Customer\StorageFactory $storageFactory, array $data=[])
_validateRowForDelete(array $rowData, $rowNumber)
Definition: Customer.php:632
_saveCustomerEntities(array $entitiesToCreate, array $entitiesToUpdate)
Definition: Customer.php:264
_deleteCustomerEntities(array $entitiesToDelete)
Definition: Customer.php:328