metadataService = $metadataService; $this->_scopeConfig = $scopeConfig; $this->_storeManager = $storeManager; $this->_config = $config; $this->_configShare = $configShare; $this->_addressFactory = $addressFactory; $this->_addressesFactory = $addressesFactory; $this->_transportBuilder = $transportBuilder; $this->_groupRepository = $groupRepository; $this->_encryptor = $encryptor; $this->dateTime = $dateTime; $this->customerDataFactory = $customerDataFactory; $this->dataObjectProcessor = $dataObjectProcessor; $this->dataObjectHelper = $dataObjectHelper; $this->indexerRegistry = $indexerRegistry; $this->accountConfirmation = $accountConfirmation ?: ObjectManager::getInstance() ->get(AccountConfirmation::class); parent::__construct( $context, $registry, $resource, $resourceCollection, $data ); } /** * Initialize customer model * * @return void */ public function _construct() { $this->_init(\Magento\Customer\Model\ResourceModel\Customer::class); } /** * Retrieve customer model with customer data * * @return \Magento\Customer\Api\Data\CustomerInterface */ public function getDataModel() { $customerData = $this->getData(); $addressesData = []; /** @var \Magento\Customer\Model\Address $address */ foreach ($this->getAddresses() as $address) { if (!isset($this->storedAddress[$address->getId()])) { $this->storedAddress[$address->getId()] = $address->getDataModel(); } $addressesData[] = $this->storedAddress[$address->getId()]; } $customerDataObject = $this->customerDataFactory->create(); $this->dataObjectHelper->populateWithArray( $customerDataObject, $customerData, \Magento\Customer\Api\Data\CustomerInterface::class ); $customerDataObject->setAddresses($addressesData) ->setId($this->getId()); return $customerDataObject; } /** * Update customer data * * @param \Magento\Customer\Api\Data\CustomerInterface $customer * @return $this */ public function updateData($customer) { $customerDataAttributes = $this->dataObjectProcessor->buildOutputDataArray( $customer, \Magento\Customer\Api\Data\CustomerInterface::class ); foreach ($customerDataAttributes as $attributeCode => $attributeData) { if ($attributeCode == 'password') { continue; } $this->setDataUsingMethod($attributeCode, $attributeData); } $customAttributes = $customer->getCustomAttributes(); if ($customAttributes !== null) { foreach ($customAttributes as $attribute) { $this->setData($attribute->getAttributeCode(), $attribute->getValue()); } } $customerId = $customer->getId(); if ($customerId) { $this->setId($customerId); } return $this; } /** * Retrieve customer sharing configuration model * * @return Share */ public function getSharingConfig() { return $this->_configShare; } /** * Authenticate customer * * @param string $login * @param string $password * @return bool * @throws \Magento\Framework\Exception\LocalizedException * Use \Magento\Customer\Api\AccountManagementInterface::authenticate */ public function authenticate($login, $password) { $this->loadByEmail($login); if ($this->getConfirmation() && $this->isConfirmationRequired()) { throw new EmailNotConfirmedException( __("This account isn't confirmed. Verify and try again.") ); } if (!$this->validatePassword($password)) { throw new InvalidEmailOrPasswordException( __('Invalid login or password.') ); } $this->_eventManager->dispatch( 'customer_customer_authenticated', ['model' => $this, 'password' => $password] ); return true; } /** * Load customer by email * * @param string $customerEmail * @return $this */ public function loadByEmail($customerEmail) { $this->_getResource()->loadByEmail($this, $customerEmail); return $this; } /** * Change customer password * * @param string $newPassword * @return $this */ public function changePassword($newPassword) { $this->_getResource()->changePassword($this, $newPassword); return $this; } /** * Get full customer name * * @return string */ public function getName() { $name = ''; if ($this->_config->getAttribute('customer', 'prefix')->getIsVisible() && $this->getPrefix()) { $name .= $this->getPrefix() . ' '; } $name .= $this->getFirstname(); if ($this->_config->getAttribute('customer', 'middlename')->getIsVisible() && $this->getMiddlename()) { $name .= ' ' . $this->getMiddlename(); } $name .= ' ' . $this->getLastname(); if ($this->_config->getAttribute('customer', 'suffix')->getIsVisible() && $this->getSuffix()) { $name .= ' ' . $this->getSuffix(); } return $name; } /** * Add address to address collection * * @param Address $address * @return $this */ public function addAddress(Address $address) { $this->getAddressesCollection()->addItem($address); return $this; } /** * Retrieve customer address by address id * * @param int $addressId * @return Address */ public function getAddressById($addressId) { return $this->_createAddressInstance()->load($addressId); } /** * Getting customer address object from collection by identifier * * @param int $addressId * @return Address */ public function getAddressItemById($addressId) { return $this->getAddressesCollection()->getItemById($addressId); } /** * Retrieve not loaded address collection * * @return \Magento\Customer\Model\ResourceModel\Address\Collection */ public function getAddressCollection() { return $this->_createAddressCollection(); } /** * Customer addresses collection * * @return \Magento\Customer\Model\ResourceModel\Address\Collection */ public function getAddressesCollection() { if ($this->_addressesCollection === null) { $this->_addressesCollection = $this->getAddressCollection()->setCustomerFilter( $this )->addAttributeToSelect( '*' ); foreach ($this->_addressesCollection as $address) { $address->setCustomer($this); } } return $this->_addressesCollection; } /** * Retrieve customer address array * * @return \Magento\Framework\DataObject[] */ public function getAddresses() { return $this->getAddressesCollection()->getItems(); } /** * Retrieve all customer attributes * * @return Attribute[] */ public function getAttributes() { if ($this->_attributes === null) { $this->_attributes = $this->_getResource()->loadAllAttributes($this)->getSortedAttributes(); } return $this->_attributes; } /** * Get customer attribute model object * * @param string $attributeCode * @return \Magento\Customer\Model\ResourceModel\Attribute | null */ public function getAttribute($attributeCode) { $this->getAttributes(); if (isset($this->_attributes[$attributeCode])) { return $this->_attributes[$attributeCode]; } return null; } /** * Set plain and hashed password * * @param string $password * @return $this */ public function setPassword($password) { $this->setData('password', $password); $this->setPasswordHash($this->hashPassword($password)); return $this; } /** * Hash customer password * * @param string $password * @param bool|int|string $salt * @return string */ public function hashPassword($password, $salt = true) { return $this->_encryptor->getHash($password, $salt); } /** * Validate password with salted hash * * @param string $password * @return boolean */ public function validatePassword($password) { $hash = $this->getPasswordHash(); if (!$hash) { return false; } return $this->_encryptor->validateHash($password, $hash); } /** * Encrypt password * * @param string $password * @return string */ public function encryptPassword($password) { return $this->_encryptor->encrypt($password); } /** * Decrypt password * * @param string $password * @return string */ public function decryptPassword($password) { return $this->_encryptor->decrypt($password); } /** * Retrieve default address by type(attribute) * * @param string $attributeCode address type attribute code * @return Address|false */ public function getPrimaryAddress($attributeCode) { $primaryAddress = $this->getAddressesCollection()->getItemById($this->getData($attributeCode)); return $primaryAddress ? $primaryAddress : false; } /** * Get customer default billing address * * @return Address */ public function getPrimaryBillingAddress() { return $this->getPrimaryAddress('default_billing'); } /** * Get customer default billing address * * @return Address */ public function getDefaultBillingAddress() { return $this->getPrimaryBillingAddress(); } /** * Get default customer shipping address * * @return Address */ public function getPrimaryShippingAddress() { return $this->getPrimaryAddress('default_shipping'); } /** * Get default customer shipping address * * @return Address */ public function getDefaultShippingAddress() { return $this->getPrimaryShippingAddress(); } /** * Retrieve ids of default addresses * * @return array */ public function getPrimaryAddressIds() { $ids = []; if ($this->getDefaultBilling()) { $ids[] = $this->getDefaultBilling(); } if ($this->getDefaultShipping()) { $ids[] = $this->getDefaultShipping(); } return $ids; } /** * Retrieve all customer default addresses * * @return Address[] */ public function getPrimaryAddresses() { $addresses = []; $primaryBilling = $this->getPrimaryBillingAddress(); if ($primaryBilling) { $addresses[] = $primaryBilling; $primaryBilling->setIsPrimaryBilling(true); } $primaryShipping = $this->getPrimaryShippingAddress(); if ($primaryShipping) { if ($primaryBilling && $primaryBilling->getId() == $primaryShipping->getId()) { $primaryBilling->setIsPrimaryShipping(true); } else { $primaryShipping->setIsPrimaryShipping(true); $addresses[] = $primaryShipping; } } return $addresses; } /** * Retrieve not default addresses * * @return Address[] */ public function getAdditionalAddresses() { $addresses = []; $primatyIds = $this->getPrimaryAddressIds(); foreach ($this->getAddressesCollection() as $address) { if (!in_array($address->getId(), $primatyIds)) { $addresses[] = $address; } } return $addresses; } /** * Check if address is primary * * @param Address $address * @return boolean */ public function isAddressPrimary(Address $address) { if (!$address->getId()) { return false; } return $address->getId() == $this->getDefaultBilling() || $address->getId() == $this->getDefaultShipping(); } /** * Send email with new account related information * * @param string $type * @param string $backUrl * @param string $storeId * @return $this * @throws \Magento\Framework\Exception\LocalizedException */ public function sendNewAccountEmail($type = 'registered', $backUrl = '', $storeId = '0') { $types = $this->getTemplateTypes(); if (!isset($types[$type])) { throw new \Magento\Framework\Exception\LocalizedException( __('The transactional account email type is incorrect. Verify and try again.') ); } if (!$storeId) { $storeId = $this->_getWebsiteStoreId($this->getSendemailStoreId()); } $this->_sendEmailTemplate( $types[$type], self::XML_PATH_REGISTER_EMAIL_IDENTITY, ['customer' => $this, 'back_url' => $backUrl, 'store' => $this->getStore()], $storeId ); return $this; } /** * Check if accounts confirmation is required in config * * @return bool * @deprecated 101.0.4 * @see AccountConfirmation::isConfirmationRequired */ public function isConfirmationRequired() { $websiteId = $this->getWebsiteId() ? $this->getWebsiteId() : null; return $this->accountConfirmation->isConfirmationRequired($websiteId, $this->getId(), $this->getEmail()); } /** * Generate random confirmation key * * @return string */ public function getRandomConfirmationKey() { return md5(uniqid()); } /** * Send email with new customer password * * @return $this */ public function sendPasswordReminderEmail() { $this->_sendEmailTemplate( self::XML_PATH_REMIND_EMAIL_TEMPLATE, self::XML_PATH_FORGOT_EMAIL_IDENTITY, ['customer' => $this, 'store' => $this->getStore()], $this->getStoreId() ); return $this; } /** * Send corresponding email template * * @param string $template configuration path of email template * @param string $sender configuration path of email identity * @param array $templateParams * @param int|null $storeId * @return $this */ protected function _sendEmailTemplate($template, $sender, $templateParams = [], $storeId = null) { /** @var \Magento\Framework\Mail\TransportInterface $transport */ $transport = $this->_transportBuilder->setTemplateIdentifier( $this->_scopeConfig->getValue($template, ScopeInterface::SCOPE_STORE, $storeId) )->setTemplateOptions( ['area' => \Magento\Framework\App\Area::AREA_FRONTEND, 'store' => $storeId] )->setTemplateVars( $templateParams )->setFrom( $this->_scopeConfig->getValue($sender, ScopeInterface::SCOPE_STORE, $storeId) )->addTo( $this->getEmail(), $this->getName() )->getTransport(); $transport->sendMessage(); return $this; } /** * Send email with reset password confirmation link * * @return $this */ public function sendPasswordResetConfirmationEmail() { $storeId = $this->getStoreId(); if (!$storeId) { $storeId = $this->_getWebsiteStoreId(); } $this->_sendEmailTemplate( self::XML_PATH_FORGOT_EMAIL_TEMPLATE, self::XML_PATH_FORGOT_EMAIL_IDENTITY, ['customer' => $this, 'store' => $this->getStore()], $storeId ); return $this; } /** * Retrieve customer group identifier * * @return int */ public function getGroupId() { if (!$this->hasData('group_id')) { $storeId = $this->getStoreId() ? $this->getStoreId() : $this->_storeManager->getStore()->getId(); $groupId = $this->_scopeConfig->getValue( GroupManagement::XML_PATH_DEFAULT_ID, ScopeInterface::SCOPE_STORE, $storeId ); $this->setData('group_id', $groupId); } return $this->getData('group_id'); } /** * Retrieve customer tax class identifier * * @return int */ public function getTaxClassId() { if (!$this->getData('tax_class_id')) { $groupTaxClassId = $this->_groupRepository->getById($this->getGroupId())->getTaxClassId(); $this->setData('tax_class_id', $groupTaxClassId); } return $this->getData('tax_class_id'); } /** * Retrieve store where customer was created * * @return \Magento\Store\Model\Store */ public function getStore() { return $this->_storeManager->getStore($this->getStoreId()); } /** * Retrieve shared store ids * * @return array */ public function getSharedStoreIds() { $ids = $this->_getData('shared_store_ids'); if ($ids === null) { $ids = []; if ((bool)$this->getSharingConfig()->isWebsiteScope()) { $ids = $this->_storeManager->getWebsite($this->getWebsiteId())->getStoreIds(); } else { foreach ($this->_storeManager->getStores() as $store) { $ids[] = $store->getId(); } } $this->setData('shared_store_ids', $ids); } return $ids; } /** * Retrieve shared website ids * * @return int[] */ public function getSharedWebsiteIds() { $ids = $this->_getData('shared_website_ids'); if ($ids === null) { $ids = []; if ((bool)$this->getSharingConfig()->isWebsiteScope()) { $ids[] = $this->getWebsiteId(); } else { foreach ($this->_storeManager->getWebsites() as $website) { $ids[] = $website->getId(); } } $this->setData('shared_website_ids', $ids); } return $ids; } /** * Retrieve attribute set id for customer. * * @return int * @since 102.0.1 */ public function getAttributeSetId() { return parent::getAttributeSetId() ?: CustomerMetadataInterface::ATTRIBUTE_SET_ID_CUSTOMER; } /** * Set store to customer * * @param \Magento\Store\Model\Store $store * @return $this */ public function setStore(\Magento\Store\Model\Store $store) { $this->setStoreId($store->getId()); $this->setWebsiteId($store->getWebsite()->getId()); return $this; } /** * Validate customer attribute values. * * @deprecated 100.1.0 * @return bool */ public function validate() { return true; } /** * Unset subscription * * @return $this */ public function unsetSubscription() { if (isset($this->_isSubscribed)) { unset($this->_isSubscribed); } return $this; } /** * Clean all addresses * * @return void */ public function cleanAllAddresses() { $this->_addressesCollection = null; } /** * Add error * * @param mixed $error * @return $this */ public function addError($error) { $this->_errors[] = $error; return $this; } /** * Retrieve errors * * @return array */ public function getErrors() { return $this->_errors; } /** * Reset errors array * * @return $this */ public function resetErrors() { $this->_errors = []; return $this; } /** * Prepare customer for delete * * @return $this */ public function beforeDelete() { //TODO : Revisit and figure handling permissions in MAGETWO-11084 Implementation: Service Context Provider return parent::beforeDelete(); } /** * Processing object after save data * * @return $this */ public function afterSave() { $indexer = $this->indexerRegistry->get(self::CUSTOMER_GRID_INDEXER_ID); if ($indexer->getState()->getStatus() == StateInterface::STATUS_VALID) { $this->_getResource()->addCommitCallback([$this, 'reindex']); } return parent::afterSave(); } /** * Init indexing process after customer delete * * @return \Magento\Framework\Model\AbstractModel */ public function afterDeleteCommit() { $this->reindex(); return parent::afterDeleteCommit(); } /** * Init indexing process after customer save * * @return void */ public function reindex() { /** @var \Magento\Framework\Indexer\IndexerInterface $indexer */ $indexer = $this->indexerRegistry->get(self::CUSTOMER_GRID_INDEXER_ID); $indexer->reindexRow($this->getId()); } /** * Get customer created at date timestamp * * @return int|null */ public function getCreatedAtTimestamp() { $date = $this->getCreatedAt(); if ($date) { return (new \DateTime($date))->getTimestamp(); } return null; } /** * Reset all model data * * @return $this */ public function reset() { $this->setData([]); $this->setOrigData(); $this->_attributes = null; return $this; } /** * Checks model is deletable * * @return boolean */ public function isDeleteable() { return $this->_isDeleteable; } /** * Set is deletable flag * * @param boolean $value * @return $this */ public function setIsDeleteable($value) { $this->_isDeleteable = (bool)$value; return $this; } /** * Checks model is readonly * * @return boolean */ public function isReadonly() { return $this->_isReadonly; } /** * Set is readonly flag * * @param boolean $value * @return $this */ public function setIsReadonly($value) { $this->_isReadonly = (bool)$value; return $this; } /** * Check whether confirmation may be skipped when registering using certain email address * * @return bool * @deprecated 101.0.4 * @see AccountConfirmation::isConfirmationRequired */ protected function canSkipConfirmation() { if (!$this->getId()) { return false; } /* If an email was used to start the registration process and it is the same email as the one used to register, then this can skip confirmation. */ $skipConfirmationIfEmail = $this->_registry->registry("skip_confirmation_if_email"); if (!$skipConfirmationIfEmail) { return false; } return strtolower($skipConfirmationIfEmail) === strtolower($this->getEmail()); } /** * Clone current object * * @return void */ public function __clone() { $newAddressCollection = $this->getPrimaryAddresses(); $newAddressCollection = array_merge($newAddressCollection, $this->getAdditionalAddresses()); $this->setId(null); $this->cleanAllAddresses(); foreach ($newAddressCollection as $address) { $this->addAddress(clone $address); } } /** * Return Entity Type instance * * @return \Magento\Eav\Model\Entity\Type */ public function getEntityType() { return $this->_getResource()->getEntityType(); } /** * Get either first store ID from a set website or the provided as default * * @param int|string|null $defaultStoreId * * @return int */ protected function _getWebsiteStoreId($defaultStoreId = null) { if ($this->getWebsiteId() != 0 && empty($defaultStoreId)) { $storeIds = $this->_storeManager->getWebsite($this->getWebsiteId())->getStoreIds(); reset($storeIds); $defaultStoreId = current($storeIds); } return $defaultStoreId; } /** * Change reset password link token * * Stores new reset password link token * * @param string $passwordLinkToken * @return $this * @throws \Magento\Framework\Exception\AuthenticationException */ public function changeResetPasswordLinkToken($passwordLinkToken) { if (!is_string($passwordLinkToken) || empty($passwordLinkToken)) { throw new AuthenticationException( __('A valid password reset token is missing. Enter and try again.') ); } $this->_getResource()->changeResetPasswordLinkToken($this, $passwordLinkToken); return $this; } /** * Check if current reset password link token is expired * * @return boolean */ public function isResetPasswordLinkTokenExpired() { $linkToken = $this->getRpToken(); $linkTokenCreatedAt = $this->getRpTokenCreatedAt(); if (empty($linkToken) || empty($linkTokenCreatedAt)) { return true; } $expirationPeriod = $this->getResetPasswordLinkExpirationPeriod(); $currentTimestamp = (new \DateTime())->getTimestamp(); $tokenTimestamp = (new \DateTime($linkTokenCreatedAt))->getTimestamp(); if ($tokenTimestamp > $currentTimestamp) { return true; } $dayDifference = floor(($currentTimestamp - $tokenTimestamp) / (24 * 60 * 60)); if ($dayDifference >= $expirationPeriod) { return true; } return false; } /** * Retrieve customer reset password link expiration period in days * * @return int */ public function getResetPasswordLinkExpirationPeriod() { return (int)$this->_scopeConfig->getValue( self::XML_PATH_CUSTOMER_RESET_PASSWORD_LINK_EXPIRATION_PERIOD, ScopeConfigInterface::SCOPE_TYPE_DEFAULT ); } /** * Create address instance * * @return Address */ protected function _createAddressInstance() { return $this->_addressFactory->create(); } /** * Create address collection instance * * @return \Magento\Customer\Model\ResourceModel\Address\Collection */ protected function _createAddressCollection() { return $this->_addressesFactory->create(); } /** * Returns templates types * * @return array */ protected function getTemplateTypes() { /** * 'registered' welcome email, when confirmation is disabled * 'confirmed' welcome email, when confirmation is enabled * 'confirmation' email with confirmation link */ $types = [ 'registered' => self::XML_PATH_REGISTER_EMAIL_TEMPLATE, 'confirmed' => self::XML_PATH_CONFIRMED_EMAIL_TEMPLATE, 'confirmation' => self::XML_PATH_CONFIRM_EMAIL_TEMPLATE, ]; return $types; } /** * Check if customer is locked * * @return boolean * @since 100.1.0 */ public function isCustomerLocked() { if ($this->getLockExpires()) { $lockExpires = new \DateTime($this->getLockExpires()); if ($lockExpires > new \DateTime()) { return true; } } return false; } /** * Return Password Confirmation * * @return string * @since 100.1.0 */ public function getPasswordConfirm() { return (string) $this->getData('password_confirm'); } /** * Return Password * * @return string * @since 100.1.0 */ public function getPassword() { return (string) $this->getData('password'); } }