Customer.php 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Customer\Model;
  7. use Magento\Customer\Api\CustomerMetadataInterface;
  8. use Magento\Customer\Api\Data\CustomerInterfaceFactory;
  9. use Magento\Customer\Api\GroupRepositoryInterface;
  10. use Magento\Customer\Model\Config\Share;
  11. use Magento\Customer\Model\ResourceModel\Address\CollectionFactory;
  12. use Magento\Customer\Model\ResourceModel\Customer as ResourceCustomer;
  13. use Magento\Framework\App\Config\ScopeConfigInterface;
  14. use Magento\Framework\Exception\AuthenticationException;
  15. use Magento\Framework\Exception\EmailNotConfirmedException;
  16. use Magento\Framework\Exception\InvalidEmailOrPasswordException;
  17. use Magento\Framework\Indexer\StateInterface;
  18. use Magento\Framework\Reflection\DataObjectProcessor;
  19. use Magento\Store\Model\ScopeInterface;
  20. use Magento\Framework\App\ObjectManager;
  21. /**
  22. * Customer model
  23. *
  24. * @api
  25. * @method int getWebsiteId() getWebsiteId()
  26. * @method Customer setWebsiteId($value)
  27. * @method int getStoreId() getStoreId()
  28. * @method string getEmail() getEmail()
  29. * @method mixed getDisableAutoGroupChange()
  30. * @method Customer setDisableAutoGroupChange($value)
  31. * @method Customer setGroupId($value)
  32. * @method Customer setDefaultBilling($value)
  33. * @method Customer setDefaultShipping($value)
  34. * @method Customer setPasswordHash($string)
  35. * @method string getPasswordHash()
  36. * @method string getConfirmation()
  37. * @SuppressWarnings(PHPMD.ExcessivePublicCount)
  38. * @SuppressWarnings(PHPMD.TooManyFields)
  39. * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
  40. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  41. * @since 100.0.2
  42. */
  43. class Customer extends \Magento\Framework\Model\AbstractModel
  44. {
  45. /**
  46. * Configuration paths for email templates and identities
  47. */
  48. const XML_PATH_REGISTER_EMAIL_TEMPLATE = 'customer/create_account/email_template';
  49. const XML_PATH_REGISTER_EMAIL_IDENTITY = 'customer/create_account/email_identity';
  50. const XML_PATH_REMIND_EMAIL_TEMPLATE = 'customer/password/remind_email_template';
  51. const XML_PATH_FORGOT_EMAIL_TEMPLATE = 'customer/password/forgot_email_template';
  52. const XML_PATH_FORGOT_EMAIL_IDENTITY = 'customer/password/forgot_email_identity';
  53. const XML_PATH_RESET_PASSWORD_TEMPLATE = 'customer/password/reset_password_template';
  54. /**
  55. * @deprecated
  56. * @see AccountConfirmation::XML_PATH_IS_CONFIRM
  57. */
  58. const XML_PATH_IS_CONFIRM = 'customer/create_account/confirm';
  59. const XML_PATH_CONFIRM_EMAIL_TEMPLATE = 'customer/create_account/email_confirmation_template';
  60. const XML_PATH_CONFIRMED_EMAIL_TEMPLATE = 'customer/create_account/email_confirmed_template';
  61. const XML_PATH_GENERATE_HUMAN_FRIENDLY_ID = 'customer/create_account/generate_human_friendly_id';
  62. const SUBSCRIBED_YES = 'yes';
  63. const SUBSCRIBED_NO = 'no';
  64. const ENTITY = 'customer';
  65. const CUSTOMER_GRID_INDEXER_ID = 'customer_grid';
  66. /**
  67. * Configuration path to expiration period of reset password link
  68. */
  69. const XML_PATH_CUSTOMER_RESET_PASSWORD_LINK_EXPIRATION_PERIOD = 'customer/password/reset_link_expiration_period';
  70. /**
  71. * Model event prefix
  72. *
  73. * @var string
  74. */
  75. protected $_eventPrefix = 'customer';
  76. /**
  77. * Name of the event object
  78. *
  79. * @var string
  80. */
  81. protected $_eventObject = 'customer';
  82. /**
  83. * List of errors
  84. *
  85. * @var array
  86. */
  87. protected $_errors = [];
  88. /**
  89. * Assoc array of customer attributes
  90. *
  91. * @var array
  92. */
  93. protected $_attributes;
  94. /**
  95. * Customer addresses collection
  96. *
  97. * @var \Magento\Customer\Model\ResourceModel\Address\Collection
  98. */
  99. protected $_addressesCollection;
  100. /**
  101. * Is model deletable
  102. *
  103. * @var boolean
  104. */
  105. protected $_isDeleteable = true;
  106. /**
  107. * Is model readonly
  108. *
  109. * @var boolean
  110. */
  111. protected $_isReadonly = false;
  112. /**
  113. * @var \Magento\Store\Model\StoreManagerInterface
  114. */
  115. protected $_storeManager;
  116. /**
  117. * @var \Magento\Eav\Model\Config
  118. */
  119. protected $_config;
  120. /**
  121. * @var \Magento\Framework\App\Config\ScopeConfigInterface
  122. */
  123. protected $_scopeConfig;
  124. /**
  125. * @var Share
  126. */
  127. protected $_configShare;
  128. /**
  129. * @var AddressFactory
  130. */
  131. protected $_addressFactory;
  132. /**
  133. * @var CollectionFactory
  134. */
  135. protected $_addressesFactory;
  136. /**
  137. * @var \Magento\Framework\Mail\Template\TransportBuilder
  138. */
  139. protected $_transportBuilder;
  140. /**
  141. * @var GroupRepositoryInterface
  142. */
  143. protected $_groupRepository;
  144. /**
  145. * @var \Magento\Framework\Encryption\EncryptorInterface
  146. */
  147. protected $_encryptor;
  148. /**
  149. * @var \Magento\Framework\Math\Random
  150. */
  151. protected $mathRandom;
  152. /**
  153. * @var \Magento\Framework\Stdlib\DateTime
  154. */
  155. protected $dateTime;
  156. /**
  157. * @var CustomerInterfaceFactory
  158. */
  159. protected $customerDataFactory;
  160. /**
  161. * @var DataObjectProcessor
  162. */
  163. protected $dataObjectProcessor;
  164. /**
  165. * @var \Magento\Framework\Api\DataObjectHelper
  166. */
  167. protected $dataObjectHelper;
  168. /**
  169. * @var \Magento\Customer\Api\CustomerMetadataInterface
  170. */
  171. protected $metadataService;
  172. /**
  173. * @var \Magento\Framework\Indexer\IndexerRegistry
  174. */
  175. protected $indexerRegistry;
  176. /**
  177. * @var AccountConfirmation
  178. */
  179. private $accountConfirmation;
  180. /**
  181. * Caching property to store customer address data models by the address ID.
  182. *
  183. * @var array
  184. */
  185. private $storedAddress;
  186. /**
  187. * @param \Magento\Framework\Model\Context $context
  188. * @param \Magento\Framework\Registry $registry
  189. * @param \Magento\Store\Model\StoreManagerInterface $storeManager
  190. * @param \Magento\Eav\Model\Config $config
  191. * @param ScopeConfigInterface $scopeConfig
  192. * @param ResourceCustomer $resource
  193. * @param Share $configShare
  194. * @param AddressFactory $addressFactory
  195. * @param CollectionFactory $addressesFactory
  196. * @param \Magento\Framework\Mail\Template\TransportBuilder $transportBuilder
  197. * @param GroupRepositoryInterface $groupRepository
  198. * @param \Magento\Framework\Encryption\EncryptorInterface $encryptor
  199. * @param \Magento\Framework\Stdlib\DateTime $dateTime
  200. * @param CustomerInterfaceFactory $customerDataFactory
  201. * @param DataObjectProcessor $dataObjectProcessor
  202. * @param \Magento\Framework\Api\DataObjectHelper $dataObjectHelper
  203. * @param CustomerMetadataInterface $metadataService
  204. * @param \Magento\Framework\Indexer\IndexerRegistry $indexerRegistry
  205. * @param \Magento\Framework\Data\Collection\AbstractDb|null $resourceCollection
  206. * @param array $data
  207. * @param AccountConfirmation|null $accountConfirmation
  208. *
  209. * @SuppressWarnings(PHPMD.ExcessiveParameterList)
  210. */
  211. public function __construct(
  212. \Magento\Framework\Model\Context $context,
  213. \Magento\Framework\Registry $registry,
  214. \Magento\Store\Model\StoreManagerInterface $storeManager,
  215. \Magento\Eav\Model\Config $config,
  216. \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
  217. \Magento\Customer\Model\ResourceModel\Customer $resource,
  218. \Magento\Customer\Model\Config\Share $configShare,
  219. \Magento\Customer\Model\AddressFactory $addressFactory,
  220. \Magento\Customer\Model\ResourceModel\Address\CollectionFactory $addressesFactory,
  221. \Magento\Framework\Mail\Template\TransportBuilder $transportBuilder,
  222. GroupRepositoryInterface $groupRepository,
  223. \Magento\Framework\Encryption\EncryptorInterface $encryptor,
  224. \Magento\Framework\Stdlib\DateTime $dateTime,
  225. CustomerInterfaceFactory $customerDataFactory,
  226. DataObjectProcessor $dataObjectProcessor,
  227. \Magento\Framework\Api\DataObjectHelper $dataObjectHelper,
  228. \Magento\Customer\Api\CustomerMetadataInterface $metadataService,
  229. \Magento\Framework\Indexer\IndexerRegistry $indexerRegistry,
  230. \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
  231. array $data = [],
  232. AccountConfirmation $accountConfirmation = null
  233. ) {
  234. $this->metadataService = $metadataService;
  235. $this->_scopeConfig = $scopeConfig;
  236. $this->_storeManager = $storeManager;
  237. $this->_config = $config;
  238. $this->_configShare = $configShare;
  239. $this->_addressFactory = $addressFactory;
  240. $this->_addressesFactory = $addressesFactory;
  241. $this->_transportBuilder = $transportBuilder;
  242. $this->_groupRepository = $groupRepository;
  243. $this->_encryptor = $encryptor;
  244. $this->dateTime = $dateTime;
  245. $this->customerDataFactory = $customerDataFactory;
  246. $this->dataObjectProcessor = $dataObjectProcessor;
  247. $this->dataObjectHelper = $dataObjectHelper;
  248. $this->indexerRegistry = $indexerRegistry;
  249. $this->accountConfirmation = $accountConfirmation ?: ObjectManager::getInstance()
  250. ->get(AccountConfirmation::class);
  251. parent::__construct(
  252. $context,
  253. $registry,
  254. $resource,
  255. $resourceCollection,
  256. $data
  257. );
  258. }
  259. /**
  260. * Initialize customer model
  261. *
  262. * @return void
  263. */
  264. public function _construct()
  265. {
  266. $this->_init(\Magento\Customer\Model\ResourceModel\Customer::class);
  267. }
  268. /**
  269. * Retrieve customer model with customer data
  270. *
  271. * @return \Magento\Customer\Api\Data\CustomerInterface
  272. */
  273. public function getDataModel()
  274. {
  275. $customerData = $this->getData();
  276. $addressesData = [];
  277. /** @var \Magento\Customer\Model\Address $address */
  278. foreach ($this->getAddresses() as $address) {
  279. if (!isset($this->storedAddress[$address->getId()])) {
  280. $this->storedAddress[$address->getId()] = $address->getDataModel();
  281. }
  282. $addressesData[] = $this->storedAddress[$address->getId()];
  283. }
  284. $customerDataObject = $this->customerDataFactory->create();
  285. $this->dataObjectHelper->populateWithArray(
  286. $customerDataObject,
  287. $customerData,
  288. \Magento\Customer\Api\Data\CustomerInterface::class
  289. );
  290. $customerDataObject->setAddresses($addressesData)
  291. ->setId($this->getId());
  292. return $customerDataObject;
  293. }
  294. /**
  295. * Update customer data
  296. *
  297. * @param \Magento\Customer\Api\Data\CustomerInterface $customer
  298. * @return $this
  299. */
  300. public function updateData($customer)
  301. {
  302. $customerDataAttributes = $this->dataObjectProcessor->buildOutputDataArray(
  303. $customer,
  304. \Magento\Customer\Api\Data\CustomerInterface::class
  305. );
  306. foreach ($customerDataAttributes as $attributeCode => $attributeData) {
  307. if ($attributeCode == 'password') {
  308. continue;
  309. }
  310. $this->setDataUsingMethod($attributeCode, $attributeData);
  311. }
  312. $customAttributes = $customer->getCustomAttributes();
  313. if ($customAttributes !== null) {
  314. foreach ($customAttributes as $attribute) {
  315. $this->setData($attribute->getAttributeCode(), $attribute->getValue());
  316. }
  317. }
  318. $customerId = $customer->getId();
  319. if ($customerId) {
  320. $this->setId($customerId);
  321. }
  322. return $this;
  323. }
  324. /**
  325. * Retrieve customer sharing configuration model
  326. *
  327. * @return Share
  328. */
  329. public function getSharingConfig()
  330. {
  331. return $this->_configShare;
  332. }
  333. /**
  334. * Authenticate customer
  335. *
  336. * @param string $login
  337. * @param string $password
  338. * @return bool
  339. * @throws \Magento\Framework\Exception\LocalizedException
  340. * Use \Magento\Customer\Api\AccountManagementInterface::authenticate
  341. */
  342. public function authenticate($login, $password)
  343. {
  344. $this->loadByEmail($login);
  345. if ($this->getConfirmation() && $this->isConfirmationRequired()) {
  346. throw new EmailNotConfirmedException(
  347. __("This account isn't confirmed. Verify and try again.")
  348. );
  349. }
  350. if (!$this->validatePassword($password)) {
  351. throw new InvalidEmailOrPasswordException(
  352. __('Invalid login or password.')
  353. );
  354. }
  355. $this->_eventManager->dispatch(
  356. 'customer_customer_authenticated',
  357. ['model' => $this, 'password' => $password]
  358. );
  359. return true;
  360. }
  361. /**
  362. * Load customer by email
  363. *
  364. * @param string $customerEmail
  365. * @return $this
  366. */
  367. public function loadByEmail($customerEmail)
  368. {
  369. $this->_getResource()->loadByEmail($this, $customerEmail);
  370. return $this;
  371. }
  372. /**
  373. * Change customer password
  374. *
  375. * @param string $newPassword
  376. * @return $this
  377. */
  378. public function changePassword($newPassword)
  379. {
  380. $this->_getResource()->changePassword($this, $newPassword);
  381. return $this;
  382. }
  383. /**
  384. * Get full customer name
  385. *
  386. * @return string
  387. */
  388. public function getName()
  389. {
  390. $name = '';
  391. if ($this->_config->getAttribute('customer', 'prefix')->getIsVisible() && $this->getPrefix()) {
  392. $name .= $this->getPrefix() . ' ';
  393. }
  394. $name .= $this->getFirstname();
  395. if ($this->_config->getAttribute('customer', 'middlename')->getIsVisible() && $this->getMiddlename()) {
  396. $name .= ' ' . $this->getMiddlename();
  397. }
  398. $name .= ' ' . $this->getLastname();
  399. if ($this->_config->getAttribute('customer', 'suffix')->getIsVisible() && $this->getSuffix()) {
  400. $name .= ' ' . $this->getSuffix();
  401. }
  402. return $name;
  403. }
  404. /**
  405. * Add address to address collection
  406. *
  407. * @param Address $address
  408. * @return $this
  409. */
  410. public function addAddress(Address $address)
  411. {
  412. $this->getAddressesCollection()->addItem($address);
  413. return $this;
  414. }
  415. /**
  416. * Retrieve customer address by address id
  417. *
  418. * @param int $addressId
  419. * @return Address
  420. */
  421. public function getAddressById($addressId)
  422. {
  423. return $this->_createAddressInstance()->load($addressId);
  424. }
  425. /**
  426. * Getting customer address object from collection by identifier
  427. *
  428. * @param int $addressId
  429. * @return Address
  430. */
  431. public function getAddressItemById($addressId)
  432. {
  433. return $this->getAddressesCollection()->getItemById($addressId);
  434. }
  435. /**
  436. * Retrieve not loaded address collection
  437. *
  438. * @return \Magento\Customer\Model\ResourceModel\Address\Collection
  439. */
  440. public function getAddressCollection()
  441. {
  442. return $this->_createAddressCollection();
  443. }
  444. /**
  445. * Customer addresses collection
  446. *
  447. * @return \Magento\Customer\Model\ResourceModel\Address\Collection
  448. */
  449. public function getAddressesCollection()
  450. {
  451. if ($this->_addressesCollection === null) {
  452. $this->_addressesCollection = $this->getAddressCollection()->setCustomerFilter(
  453. $this
  454. )->addAttributeToSelect(
  455. '*'
  456. );
  457. foreach ($this->_addressesCollection as $address) {
  458. $address->setCustomer($this);
  459. }
  460. }
  461. return $this->_addressesCollection;
  462. }
  463. /**
  464. * Retrieve customer address array
  465. *
  466. * @return \Magento\Framework\DataObject[]
  467. */
  468. public function getAddresses()
  469. {
  470. return $this->getAddressesCollection()->getItems();
  471. }
  472. /**
  473. * Retrieve all customer attributes
  474. *
  475. * @return Attribute[]
  476. */
  477. public function getAttributes()
  478. {
  479. if ($this->_attributes === null) {
  480. $this->_attributes = $this->_getResource()->loadAllAttributes($this)->getSortedAttributes();
  481. }
  482. return $this->_attributes;
  483. }
  484. /**
  485. * Get customer attribute model object
  486. *
  487. * @param string $attributeCode
  488. * @return \Magento\Customer\Model\ResourceModel\Attribute | null
  489. */
  490. public function getAttribute($attributeCode)
  491. {
  492. $this->getAttributes();
  493. if (isset($this->_attributes[$attributeCode])) {
  494. return $this->_attributes[$attributeCode];
  495. }
  496. return null;
  497. }
  498. /**
  499. * Set plain and hashed password
  500. *
  501. * @param string $password
  502. * @return $this
  503. */
  504. public function setPassword($password)
  505. {
  506. $this->setData('password', $password);
  507. $this->setPasswordHash($this->hashPassword($password));
  508. return $this;
  509. }
  510. /**
  511. * Hash customer password
  512. *
  513. * @param string $password
  514. * @param bool|int|string $salt
  515. * @return string
  516. */
  517. public function hashPassword($password, $salt = true)
  518. {
  519. return $this->_encryptor->getHash($password, $salt);
  520. }
  521. /**
  522. * Validate password with salted hash
  523. *
  524. * @param string $password
  525. * @return boolean
  526. */
  527. public function validatePassword($password)
  528. {
  529. $hash = $this->getPasswordHash();
  530. if (!$hash) {
  531. return false;
  532. }
  533. return $this->_encryptor->validateHash($password, $hash);
  534. }
  535. /**
  536. * Encrypt password
  537. *
  538. * @param string $password
  539. * @return string
  540. */
  541. public function encryptPassword($password)
  542. {
  543. return $this->_encryptor->encrypt($password);
  544. }
  545. /**
  546. * Decrypt password
  547. *
  548. * @param string $password
  549. * @return string
  550. */
  551. public function decryptPassword($password)
  552. {
  553. return $this->_encryptor->decrypt($password);
  554. }
  555. /**
  556. * Retrieve default address by type(attribute)
  557. *
  558. * @param string $attributeCode address type attribute code
  559. * @return Address|false
  560. */
  561. public function getPrimaryAddress($attributeCode)
  562. {
  563. $primaryAddress = $this->getAddressesCollection()->getItemById($this->getData($attributeCode));
  564. return $primaryAddress ? $primaryAddress : false;
  565. }
  566. /**
  567. * Get customer default billing address
  568. *
  569. * @return Address
  570. */
  571. public function getPrimaryBillingAddress()
  572. {
  573. return $this->getPrimaryAddress('default_billing');
  574. }
  575. /**
  576. * Get customer default billing address
  577. *
  578. * @return Address
  579. */
  580. public function getDefaultBillingAddress()
  581. {
  582. return $this->getPrimaryBillingAddress();
  583. }
  584. /**
  585. * Get default customer shipping address
  586. *
  587. * @return Address
  588. */
  589. public function getPrimaryShippingAddress()
  590. {
  591. return $this->getPrimaryAddress('default_shipping');
  592. }
  593. /**
  594. * Get default customer shipping address
  595. *
  596. * @return Address
  597. */
  598. public function getDefaultShippingAddress()
  599. {
  600. return $this->getPrimaryShippingAddress();
  601. }
  602. /**
  603. * Retrieve ids of default addresses
  604. *
  605. * @return array
  606. */
  607. public function getPrimaryAddressIds()
  608. {
  609. $ids = [];
  610. if ($this->getDefaultBilling()) {
  611. $ids[] = $this->getDefaultBilling();
  612. }
  613. if ($this->getDefaultShipping()) {
  614. $ids[] = $this->getDefaultShipping();
  615. }
  616. return $ids;
  617. }
  618. /**
  619. * Retrieve all customer default addresses
  620. *
  621. * @return Address[]
  622. */
  623. public function getPrimaryAddresses()
  624. {
  625. $addresses = [];
  626. $primaryBilling = $this->getPrimaryBillingAddress();
  627. if ($primaryBilling) {
  628. $addresses[] = $primaryBilling;
  629. $primaryBilling->setIsPrimaryBilling(true);
  630. }
  631. $primaryShipping = $this->getPrimaryShippingAddress();
  632. if ($primaryShipping) {
  633. if ($primaryBilling && $primaryBilling->getId() == $primaryShipping->getId()) {
  634. $primaryBilling->setIsPrimaryShipping(true);
  635. } else {
  636. $primaryShipping->setIsPrimaryShipping(true);
  637. $addresses[] = $primaryShipping;
  638. }
  639. }
  640. return $addresses;
  641. }
  642. /**
  643. * Retrieve not default addresses
  644. *
  645. * @return Address[]
  646. */
  647. public function getAdditionalAddresses()
  648. {
  649. $addresses = [];
  650. $primatyIds = $this->getPrimaryAddressIds();
  651. foreach ($this->getAddressesCollection() as $address) {
  652. if (!in_array($address->getId(), $primatyIds)) {
  653. $addresses[] = $address;
  654. }
  655. }
  656. return $addresses;
  657. }
  658. /**
  659. * Check if address is primary
  660. *
  661. * @param Address $address
  662. * @return boolean
  663. */
  664. public function isAddressPrimary(Address $address)
  665. {
  666. if (!$address->getId()) {
  667. return false;
  668. }
  669. return $address->getId() == $this->getDefaultBilling() || $address->getId() == $this->getDefaultShipping();
  670. }
  671. /**
  672. * Send email with new account related information
  673. *
  674. * @param string $type
  675. * @param string $backUrl
  676. * @param string $storeId
  677. * @return $this
  678. * @throws \Magento\Framework\Exception\LocalizedException
  679. */
  680. public function sendNewAccountEmail($type = 'registered', $backUrl = '', $storeId = '0')
  681. {
  682. $types = $this->getTemplateTypes();
  683. if (!isset($types[$type])) {
  684. throw new \Magento\Framework\Exception\LocalizedException(
  685. __('The transactional account email type is incorrect. Verify and try again.')
  686. );
  687. }
  688. if (!$storeId) {
  689. $storeId = $this->_getWebsiteStoreId($this->getSendemailStoreId());
  690. }
  691. $this->_sendEmailTemplate(
  692. $types[$type],
  693. self::XML_PATH_REGISTER_EMAIL_IDENTITY,
  694. ['customer' => $this, 'back_url' => $backUrl, 'store' => $this->getStore()],
  695. $storeId
  696. );
  697. return $this;
  698. }
  699. /**
  700. * Check if accounts confirmation is required in config
  701. *
  702. * @return bool
  703. * @deprecated 101.0.4
  704. * @see AccountConfirmation::isConfirmationRequired
  705. */
  706. public function isConfirmationRequired()
  707. {
  708. $websiteId = $this->getWebsiteId() ? $this->getWebsiteId() : null;
  709. return $this->accountConfirmation->isConfirmationRequired($websiteId, $this->getId(), $this->getEmail());
  710. }
  711. /**
  712. * Generate random confirmation key
  713. *
  714. * @return string
  715. */
  716. public function getRandomConfirmationKey()
  717. {
  718. return md5(uniqid());
  719. }
  720. /**
  721. * Send email with new customer password
  722. *
  723. * @return $this
  724. */
  725. public function sendPasswordReminderEmail()
  726. {
  727. $this->_sendEmailTemplate(
  728. self::XML_PATH_REMIND_EMAIL_TEMPLATE,
  729. self::XML_PATH_FORGOT_EMAIL_IDENTITY,
  730. ['customer' => $this, 'store' => $this->getStore()],
  731. $this->getStoreId()
  732. );
  733. return $this;
  734. }
  735. /**
  736. * Send corresponding email template
  737. *
  738. * @param string $template configuration path of email template
  739. * @param string $sender configuration path of email identity
  740. * @param array $templateParams
  741. * @param int|null $storeId
  742. * @return $this
  743. */
  744. protected function _sendEmailTemplate($template, $sender, $templateParams = [], $storeId = null)
  745. {
  746. /** @var \Magento\Framework\Mail\TransportInterface $transport */
  747. $transport = $this->_transportBuilder->setTemplateIdentifier(
  748. $this->_scopeConfig->getValue($template, ScopeInterface::SCOPE_STORE, $storeId)
  749. )->setTemplateOptions(
  750. ['area' => \Magento\Framework\App\Area::AREA_FRONTEND, 'store' => $storeId]
  751. )->setTemplateVars(
  752. $templateParams
  753. )->setFrom(
  754. $this->_scopeConfig->getValue($sender, ScopeInterface::SCOPE_STORE, $storeId)
  755. )->addTo(
  756. $this->getEmail(),
  757. $this->getName()
  758. )->getTransport();
  759. $transport->sendMessage();
  760. return $this;
  761. }
  762. /**
  763. * Send email with reset password confirmation link
  764. *
  765. * @return $this
  766. */
  767. public function sendPasswordResetConfirmationEmail()
  768. {
  769. $storeId = $this->getStoreId();
  770. if (!$storeId) {
  771. $storeId = $this->_getWebsiteStoreId();
  772. }
  773. $this->_sendEmailTemplate(
  774. self::XML_PATH_FORGOT_EMAIL_TEMPLATE,
  775. self::XML_PATH_FORGOT_EMAIL_IDENTITY,
  776. ['customer' => $this, 'store' => $this->getStore()],
  777. $storeId
  778. );
  779. return $this;
  780. }
  781. /**
  782. * Retrieve customer group identifier
  783. *
  784. * @return int
  785. */
  786. public function getGroupId()
  787. {
  788. if (!$this->hasData('group_id')) {
  789. $storeId = $this->getStoreId() ? $this->getStoreId() : $this->_storeManager->getStore()->getId();
  790. $groupId = $this->_scopeConfig->getValue(
  791. GroupManagement::XML_PATH_DEFAULT_ID,
  792. ScopeInterface::SCOPE_STORE,
  793. $storeId
  794. );
  795. $this->setData('group_id', $groupId);
  796. }
  797. return $this->getData('group_id');
  798. }
  799. /**
  800. * Retrieve customer tax class identifier
  801. *
  802. * @return int
  803. */
  804. public function getTaxClassId()
  805. {
  806. if (!$this->getData('tax_class_id')) {
  807. $groupTaxClassId = $this->_groupRepository->getById($this->getGroupId())->getTaxClassId();
  808. $this->setData('tax_class_id', $groupTaxClassId);
  809. }
  810. return $this->getData('tax_class_id');
  811. }
  812. /**
  813. * Retrieve store where customer was created
  814. *
  815. * @return \Magento\Store\Model\Store
  816. */
  817. public function getStore()
  818. {
  819. return $this->_storeManager->getStore($this->getStoreId());
  820. }
  821. /**
  822. * Retrieve shared store ids
  823. *
  824. * @return array
  825. */
  826. public function getSharedStoreIds()
  827. {
  828. $ids = $this->_getData('shared_store_ids');
  829. if ($ids === null) {
  830. $ids = [];
  831. if ((bool)$this->getSharingConfig()->isWebsiteScope()) {
  832. $ids = $this->_storeManager->getWebsite($this->getWebsiteId())->getStoreIds();
  833. } else {
  834. foreach ($this->_storeManager->getStores() as $store) {
  835. $ids[] = $store->getId();
  836. }
  837. }
  838. $this->setData('shared_store_ids', $ids);
  839. }
  840. return $ids;
  841. }
  842. /**
  843. * Retrieve shared website ids
  844. *
  845. * @return int[]
  846. */
  847. public function getSharedWebsiteIds()
  848. {
  849. $ids = $this->_getData('shared_website_ids');
  850. if ($ids === null) {
  851. $ids = [];
  852. if ((bool)$this->getSharingConfig()->isWebsiteScope()) {
  853. $ids[] = $this->getWebsiteId();
  854. } else {
  855. foreach ($this->_storeManager->getWebsites() as $website) {
  856. $ids[] = $website->getId();
  857. }
  858. }
  859. $this->setData('shared_website_ids', $ids);
  860. }
  861. return $ids;
  862. }
  863. /**
  864. * Retrieve attribute set id for customer.
  865. *
  866. * @return int
  867. * @since 102.0.1
  868. */
  869. public function getAttributeSetId()
  870. {
  871. return parent::getAttributeSetId() ?: CustomerMetadataInterface::ATTRIBUTE_SET_ID_CUSTOMER;
  872. }
  873. /**
  874. * Set store to customer
  875. *
  876. * @param \Magento\Store\Model\Store $store
  877. * @return $this
  878. */
  879. public function setStore(\Magento\Store\Model\Store $store)
  880. {
  881. $this->setStoreId($store->getId());
  882. $this->setWebsiteId($store->getWebsite()->getId());
  883. return $this;
  884. }
  885. /**
  886. * Validate customer attribute values.
  887. *
  888. * @deprecated 100.1.0
  889. * @return bool
  890. */
  891. public function validate()
  892. {
  893. return true;
  894. }
  895. /**
  896. * Unset subscription
  897. *
  898. * @return $this
  899. */
  900. public function unsetSubscription()
  901. {
  902. if (isset($this->_isSubscribed)) {
  903. unset($this->_isSubscribed);
  904. }
  905. return $this;
  906. }
  907. /**
  908. * Clean all addresses
  909. *
  910. * @return void
  911. */
  912. public function cleanAllAddresses()
  913. {
  914. $this->_addressesCollection = null;
  915. }
  916. /**
  917. * Add error
  918. *
  919. * @param mixed $error
  920. * @return $this
  921. */
  922. public function addError($error)
  923. {
  924. $this->_errors[] = $error;
  925. return $this;
  926. }
  927. /**
  928. * Retrieve errors
  929. *
  930. * @return array
  931. */
  932. public function getErrors()
  933. {
  934. return $this->_errors;
  935. }
  936. /**
  937. * Reset errors array
  938. *
  939. * @return $this
  940. */
  941. public function resetErrors()
  942. {
  943. $this->_errors = [];
  944. return $this;
  945. }
  946. /**
  947. * Prepare customer for delete
  948. *
  949. * @return $this
  950. */
  951. public function beforeDelete()
  952. {
  953. //TODO : Revisit and figure handling permissions in MAGETWO-11084 Implementation: Service Context Provider
  954. return parent::beforeDelete();
  955. }
  956. /**
  957. * Processing object after save data
  958. *
  959. * @return $this
  960. */
  961. public function afterSave()
  962. {
  963. $indexer = $this->indexerRegistry->get(self::CUSTOMER_GRID_INDEXER_ID);
  964. if ($indexer->getState()->getStatus() == StateInterface::STATUS_VALID) {
  965. $this->_getResource()->addCommitCallback([$this, 'reindex']);
  966. }
  967. return parent::afterSave();
  968. }
  969. /**
  970. * Init indexing process after customer delete
  971. *
  972. * @return \Magento\Framework\Model\AbstractModel
  973. */
  974. public function afterDeleteCommit()
  975. {
  976. $this->reindex();
  977. return parent::afterDeleteCommit();
  978. }
  979. /**
  980. * Init indexing process after customer save
  981. *
  982. * @return void
  983. */
  984. public function reindex()
  985. {
  986. /** @var \Magento\Framework\Indexer\IndexerInterface $indexer */
  987. $indexer = $this->indexerRegistry->get(self::CUSTOMER_GRID_INDEXER_ID);
  988. $indexer->reindexRow($this->getId());
  989. }
  990. /**
  991. * Get customer created at date timestamp
  992. *
  993. * @return int|null
  994. */
  995. public function getCreatedAtTimestamp()
  996. {
  997. $date = $this->getCreatedAt();
  998. if ($date) {
  999. return (new \DateTime($date))->getTimestamp();
  1000. }
  1001. return null;
  1002. }
  1003. /**
  1004. * Reset all model data
  1005. *
  1006. * @return $this
  1007. */
  1008. public function reset()
  1009. {
  1010. $this->setData([]);
  1011. $this->setOrigData();
  1012. $this->_attributes = null;
  1013. return $this;
  1014. }
  1015. /**
  1016. * Checks model is deletable
  1017. *
  1018. * @return boolean
  1019. */
  1020. public function isDeleteable()
  1021. {
  1022. return $this->_isDeleteable;
  1023. }
  1024. /**
  1025. * Set is deletable flag
  1026. *
  1027. * @param boolean $value
  1028. * @return $this
  1029. */
  1030. public function setIsDeleteable($value)
  1031. {
  1032. $this->_isDeleteable = (bool)$value;
  1033. return $this;
  1034. }
  1035. /**
  1036. * Checks model is readonly
  1037. *
  1038. * @return boolean
  1039. */
  1040. public function isReadonly()
  1041. {
  1042. return $this->_isReadonly;
  1043. }
  1044. /**
  1045. * Set is readonly flag
  1046. *
  1047. * @param boolean $value
  1048. * @return $this
  1049. */
  1050. public function setIsReadonly($value)
  1051. {
  1052. $this->_isReadonly = (bool)$value;
  1053. return $this;
  1054. }
  1055. /**
  1056. * Check whether confirmation may be skipped when registering using certain email address
  1057. *
  1058. * @return bool
  1059. * @deprecated 101.0.4
  1060. * @see AccountConfirmation::isConfirmationRequired
  1061. */
  1062. protected function canSkipConfirmation()
  1063. {
  1064. if (!$this->getId()) {
  1065. return false;
  1066. }
  1067. /* If an email was used to start the registration process and it is the same email as the one
  1068. used to register, then this can skip confirmation.
  1069. */
  1070. $skipConfirmationIfEmail = $this->_registry->registry("skip_confirmation_if_email");
  1071. if (!$skipConfirmationIfEmail) {
  1072. return false;
  1073. }
  1074. return strtolower($skipConfirmationIfEmail) === strtolower($this->getEmail());
  1075. }
  1076. /**
  1077. * Clone current object
  1078. *
  1079. * @return void
  1080. */
  1081. public function __clone()
  1082. {
  1083. $newAddressCollection = $this->getPrimaryAddresses();
  1084. $newAddressCollection = array_merge($newAddressCollection, $this->getAdditionalAddresses());
  1085. $this->setId(null);
  1086. $this->cleanAllAddresses();
  1087. foreach ($newAddressCollection as $address) {
  1088. $this->addAddress(clone $address);
  1089. }
  1090. }
  1091. /**
  1092. * Return Entity Type instance
  1093. *
  1094. * @return \Magento\Eav\Model\Entity\Type
  1095. */
  1096. public function getEntityType()
  1097. {
  1098. return $this->_getResource()->getEntityType();
  1099. }
  1100. /**
  1101. * Get either first store ID from a set website or the provided as default
  1102. *
  1103. * @param int|string|null $defaultStoreId
  1104. *
  1105. * @return int
  1106. */
  1107. protected function _getWebsiteStoreId($defaultStoreId = null)
  1108. {
  1109. if ($this->getWebsiteId() != 0 && empty($defaultStoreId)) {
  1110. $storeIds = $this->_storeManager->getWebsite($this->getWebsiteId())->getStoreIds();
  1111. reset($storeIds);
  1112. $defaultStoreId = current($storeIds);
  1113. }
  1114. return $defaultStoreId;
  1115. }
  1116. /**
  1117. * Change reset password link token
  1118. *
  1119. * Stores new reset password link token
  1120. *
  1121. * @param string $passwordLinkToken
  1122. * @return $this
  1123. * @throws \Magento\Framework\Exception\AuthenticationException
  1124. */
  1125. public function changeResetPasswordLinkToken($passwordLinkToken)
  1126. {
  1127. if (!is_string($passwordLinkToken) || empty($passwordLinkToken)) {
  1128. throw new AuthenticationException(
  1129. __('A valid password reset token is missing. Enter and try again.')
  1130. );
  1131. }
  1132. $this->_getResource()->changeResetPasswordLinkToken($this, $passwordLinkToken);
  1133. return $this;
  1134. }
  1135. /**
  1136. * Check if current reset password link token is expired
  1137. *
  1138. * @return boolean
  1139. */
  1140. public function isResetPasswordLinkTokenExpired()
  1141. {
  1142. $linkToken = $this->getRpToken();
  1143. $linkTokenCreatedAt = $this->getRpTokenCreatedAt();
  1144. if (empty($linkToken) || empty($linkTokenCreatedAt)) {
  1145. return true;
  1146. }
  1147. $expirationPeriod = $this->getResetPasswordLinkExpirationPeriod();
  1148. $currentTimestamp = (new \DateTime())->getTimestamp();
  1149. $tokenTimestamp = (new \DateTime($linkTokenCreatedAt))->getTimestamp();
  1150. if ($tokenTimestamp > $currentTimestamp) {
  1151. return true;
  1152. }
  1153. $dayDifference = floor(($currentTimestamp - $tokenTimestamp) / (24 * 60 * 60));
  1154. if ($dayDifference >= $expirationPeriod) {
  1155. return true;
  1156. }
  1157. return false;
  1158. }
  1159. /**
  1160. * Retrieve customer reset password link expiration period in days
  1161. *
  1162. * @return int
  1163. */
  1164. public function getResetPasswordLinkExpirationPeriod()
  1165. {
  1166. return (int)$this->_scopeConfig->getValue(
  1167. self::XML_PATH_CUSTOMER_RESET_PASSWORD_LINK_EXPIRATION_PERIOD,
  1168. ScopeConfigInterface::SCOPE_TYPE_DEFAULT
  1169. );
  1170. }
  1171. /**
  1172. * Create address instance
  1173. *
  1174. * @return Address
  1175. */
  1176. protected function _createAddressInstance()
  1177. {
  1178. return $this->_addressFactory->create();
  1179. }
  1180. /**
  1181. * Create address collection instance
  1182. *
  1183. * @return \Magento\Customer\Model\ResourceModel\Address\Collection
  1184. */
  1185. protected function _createAddressCollection()
  1186. {
  1187. return $this->_addressesFactory->create();
  1188. }
  1189. /**
  1190. * Returns templates types
  1191. *
  1192. * @return array
  1193. */
  1194. protected function getTemplateTypes()
  1195. {
  1196. /**
  1197. * 'registered' welcome email, when confirmation is disabled
  1198. * 'confirmed' welcome email, when confirmation is enabled
  1199. * 'confirmation' email with confirmation link
  1200. */
  1201. $types = [
  1202. 'registered' => self::XML_PATH_REGISTER_EMAIL_TEMPLATE,
  1203. 'confirmed' => self::XML_PATH_CONFIRMED_EMAIL_TEMPLATE,
  1204. 'confirmation' => self::XML_PATH_CONFIRM_EMAIL_TEMPLATE,
  1205. ];
  1206. return $types;
  1207. }
  1208. /**
  1209. * Check if customer is locked
  1210. *
  1211. * @return boolean
  1212. * @since 100.1.0
  1213. */
  1214. public function isCustomerLocked()
  1215. {
  1216. if ($this->getLockExpires()) {
  1217. $lockExpires = new \DateTime($this->getLockExpires());
  1218. if ($lockExpires > new \DateTime()) {
  1219. return true;
  1220. }
  1221. }
  1222. return false;
  1223. }
  1224. /**
  1225. * Return Password Confirmation
  1226. *
  1227. * @return string
  1228. * @since 100.1.0
  1229. */
  1230. public function getPasswordConfirm()
  1231. {
  1232. return (string) $this->getData('password_confirm');
  1233. }
  1234. /**
  1235. * Return Password
  1236. *
  1237. * @return string
  1238. * @since 100.1.0
  1239. */
  1240. public function getPassword()
  1241. {
  1242. return (string) $this->getData('password');
  1243. }
  1244. }