Customer.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Customer\Model\ResourceModel;
  7. use Magento\Customer\Model\Customer\NotificationStorage;
  8. use Magento\Framework\App\ObjectManager;
  9. use Magento\Framework\Validator\Exception as ValidatorException;
  10. use Magento\Framework\Exception\AlreadyExistsException;
  11. /**
  12. * Customer entity resource model
  13. *
  14. * @api
  15. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  16. * @since 100.0.2
  17. */
  18. class Customer extends \Magento\Eav\Model\Entity\VersionControl\AbstractEntity
  19. {
  20. /**
  21. * @var \Magento\Framework\Validator\Factory
  22. */
  23. protected $_validatorFactory;
  24. /**
  25. * Core store config
  26. *
  27. * @var \Magento\Framework\App\Config\ScopeConfigInterface
  28. */
  29. protected $_scopeConfig;
  30. /**
  31. * @var \Magento\Framework\Stdlib\DateTime
  32. */
  33. protected $dateTime;
  34. /**
  35. * @var \Magento\Store\Model\StoreManagerInterface
  36. */
  37. protected $storeManager;
  38. /**
  39. * @var NotificationStorage
  40. */
  41. private $notificationStorage;
  42. /**
  43. * @param \Magento\Eav\Model\Entity\Context $context
  44. * @param \Magento\Framework\Model\ResourceModel\Db\VersionControl\Snapshot $entitySnapshot
  45. * @param \Magento\Framework\Model\ResourceModel\Db\VersionControl\RelationComposite $entityRelationComposite
  46. * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
  47. * @param \Magento\Framework\Validator\Factory $validatorFactory
  48. * @param \Magento\Framework\Stdlib\DateTime $dateTime
  49. * @param \Magento\Store\Model\StoreManagerInterface $storeManager
  50. * @param array $data
  51. */
  52. public function __construct(
  53. \Magento\Eav\Model\Entity\Context $context,
  54. \Magento\Framework\Model\ResourceModel\Db\VersionControl\Snapshot $entitySnapshot,
  55. \Magento\Framework\Model\ResourceModel\Db\VersionControl\RelationComposite $entityRelationComposite,
  56. \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
  57. \Magento\Framework\Validator\Factory $validatorFactory,
  58. \Magento\Framework\Stdlib\DateTime $dateTime,
  59. \Magento\Store\Model\StoreManagerInterface $storeManager,
  60. $data = []
  61. ) {
  62. parent::__construct($context, $entitySnapshot, $entityRelationComposite, $data);
  63. $this->_scopeConfig = $scopeConfig;
  64. $this->_validatorFactory = $validatorFactory;
  65. $this->dateTime = $dateTime;
  66. $this->storeManager = $storeManager;
  67. $this->setType('customer');
  68. $this->setConnection('customer_read', 'customer_write');
  69. }
  70. /**
  71. * Retrieve customer entity default attributes
  72. *
  73. * @return string[]
  74. */
  75. protected function _getDefaultAttributes()
  76. {
  77. return [
  78. 'created_at',
  79. 'updated_at',
  80. 'increment_id',
  81. 'store_id',
  82. 'website_id'
  83. ];
  84. }
  85. /**
  86. * Check customer scope, email and confirmation key before saving
  87. *
  88. * @param \Magento\Framework\DataObject $customer
  89. * @return $this
  90. * @throws \Magento\Framework\Exception\LocalizedException
  91. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  92. * @SuppressWarnings(PHPMD.NPathComplexity)
  93. */
  94. protected function _beforeSave(\Magento\Framework\DataObject $customer)
  95. {
  96. /** @var \Magento\Customer\Model\Customer $customer */
  97. if ($customer->getStoreId() === null) {
  98. $customer->setStoreId($this->storeManager->getStore()->getId());
  99. }
  100. $customer->getGroupId();
  101. parent::_beforeSave($customer);
  102. if (!$customer->getEmail()) {
  103. throw new ValidatorException(__('The customer email is missing. Enter and try again.'));
  104. }
  105. $connection = $this->getConnection();
  106. $bind = ['email' => $customer->getEmail()];
  107. $select = $connection->select()->from(
  108. $this->getEntityTable(),
  109. [$this->getEntityIdField()]
  110. )->where(
  111. 'email = :email'
  112. );
  113. if ($customer->getSharingConfig()->isWebsiteScope()) {
  114. $bind['website_id'] = (int)$customer->getWebsiteId();
  115. $select->where('website_id = :website_id');
  116. }
  117. if ($customer->getId()) {
  118. $bind['entity_id'] = (int)$customer->getId();
  119. $select->where('entity_id != :entity_id');
  120. }
  121. $result = $connection->fetchOne($select, $bind);
  122. if ($result) {
  123. throw new AlreadyExistsException(
  124. __('A customer with the same email address already exists in an associated website.')
  125. );
  126. }
  127. // set confirmation key logic
  128. if ($customer->getForceConfirmed() || $customer->getPasswordHash() == '') {
  129. $customer->setConfirmation(null);
  130. } elseif (!$customer->getId() && $customer->isConfirmationRequired()) {
  131. $customer->setConfirmation($customer->getRandomConfirmationKey());
  132. }
  133. // remove customer confirmation key from database, if empty
  134. if (!$customer->getConfirmation()) {
  135. $customer->setConfirmation(null);
  136. }
  137. if (!$customer->getData('ignore_validation_flag')) {
  138. $this->_validate($customer);
  139. }
  140. return $this;
  141. }
  142. /**
  143. * Validate customer entity
  144. *
  145. * @param \Magento\Customer\Model\Customer $customer
  146. * @return void
  147. * @throws \Magento\Framework\Validator\Exception
  148. */
  149. protected function _validate($customer)
  150. {
  151. $validator = $this->_validatorFactory->createValidator('customer', 'save');
  152. if (!$validator->isValid($customer)) {
  153. throw new ValidatorException(
  154. null,
  155. null,
  156. $validator->getMessages()
  157. );
  158. }
  159. }
  160. /**
  161. * Retrieve notification storage
  162. *
  163. * @return NotificationStorage
  164. */
  165. private function getNotificationStorage()
  166. {
  167. if ($this->notificationStorage === null) {
  168. $this->notificationStorage = ObjectManager::getInstance()->get(NotificationStorage::class);
  169. }
  170. return $this->notificationStorage;
  171. }
  172. /**
  173. * Save customer addresses and set default addresses in attributes backend
  174. *
  175. * @param \Magento\Framework\DataObject $customer
  176. * @return $this
  177. */
  178. protected function _afterSave(\Magento\Framework\DataObject $customer)
  179. {
  180. $this->getNotificationStorage()->add(
  181. NotificationStorage::UPDATE_CUSTOMER_SESSION,
  182. $customer->getId()
  183. );
  184. return parent::_afterSave($customer);
  185. }
  186. /**
  187. * Retrieve select object for loading base entity row
  188. *
  189. * @param \Magento\Framework\DataObject $object
  190. * @param string|int $rowId
  191. * @return \Magento\Framework\DB\Select
  192. */
  193. protected function _getLoadRowSelect($object, $rowId)
  194. {
  195. $select = parent::_getLoadRowSelect($object, $rowId);
  196. if ($object->getWebsiteId() && $object->getSharingConfig()->isWebsiteScope()) {
  197. $select->where('website_id =?', (int)$object->getWebsiteId());
  198. }
  199. return $select;
  200. }
  201. /**
  202. * Load customer by email
  203. *
  204. * @param \Magento\Customer\Model\Customer $customer
  205. * @param string $email
  206. * @return $this
  207. * @throws \Magento\Framework\Exception\LocalizedException
  208. */
  209. public function loadByEmail(\Magento\Customer\Model\Customer $customer, $email)
  210. {
  211. $connection = $this->getConnection();
  212. $bind = ['customer_email' => $email];
  213. $select = $connection->select()->from(
  214. $this->getEntityTable(),
  215. [$this->getEntityIdField()]
  216. )->where(
  217. 'email = :customer_email'
  218. );
  219. if ($customer->getSharingConfig()->isWebsiteScope()) {
  220. if (!$customer->hasData('website_id')) {
  221. throw new \Magento\Framework\Exception\LocalizedException(
  222. __("A customer website ID wasn't specified. The ID must be specified to use the website scope.")
  223. );
  224. }
  225. $bind['website_id'] = (int)$customer->getWebsiteId();
  226. $select->where('website_id = :website_id');
  227. }
  228. $customerId = $connection->fetchOne($select, $bind);
  229. if ($customerId) {
  230. $this->load($customer, $customerId);
  231. } else {
  232. $customer->setData([]);
  233. }
  234. return $this;
  235. }
  236. /**
  237. * Change customer password
  238. *
  239. * @param \Magento\Customer\Model\Customer $customer
  240. * @param string $newPassword
  241. * @return $this
  242. */
  243. public function changePassword(\Magento\Customer\Model\Customer $customer, $newPassword)
  244. {
  245. $customer->setPassword($newPassword);
  246. return $this;
  247. }
  248. /**
  249. * Check whether there are email duplicates of customers in global scope
  250. *
  251. * @return bool
  252. */
  253. public function findEmailDuplicates()
  254. {
  255. $connection = $this->getConnection();
  256. $select = $connection->select()->from(
  257. $this->getTable('customer_entity'),
  258. ['email', 'cnt' => 'COUNT(*)']
  259. )->group(
  260. 'email'
  261. )->order(
  262. 'cnt DESC'
  263. )->limit(
  264. 1
  265. );
  266. $lookup = $connection->fetchRow($select);
  267. if (empty($lookup)) {
  268. return false;
  269. }
  270. return $lookup['cnt'] > 1;
  271. }
  272. /**
  273. * Check customer by id
  274. *
  275. * @param int $customerId
  276. * @return bool
  277. */
  278. public function checkCustomerId($customerId)
  279. {
  280. $connection = $this->getConnection();
  281. $bind = ['entity_id' => (int)$customerId];
  282. $select = $connection->select()->from(
  283. $this->getTable('customer_entity'),
  284. 'entity_id'
  285. )->where(
  286. 'entity_id = :entity_id'
  287. )->limit(
  288. 1
  289. );
  290. $result = $connection->fetchOne($select, $bind);
  291. if ($result) {
  292. return true;
  293. }
  294. return false;
  295. }
  296. /**
  297. * Get customer website id
  298. *
  299. * @param int $customerId
  300. * @return int
  301. */
  302. public function getWebsiteId($customerId)
  303. {
  304. $connection = $this->getConnection();
  305. $bind = ['entity_id' => (int)$customerId];
  306. $select = $connection->select()->from(
  307. $this->getTable('customer_entity'),
  308. 'website_id'
  309. )->where(
  310. 'entity_id = :entity_id'
  311. );
  312. return $connection->fetchOne($select, $bind);
  313. }
  314. /**
  315. * Custom setter of increment ID if its needed
  316. *
  317. * @param \Magento\Framework\DataObject $object
  318. * @return $this
  319. */
  320. public function setNewIncrementId(\Magento\Framework\DataObject $object)
  321. {
  322. if ($this->_scopeConfig->getValue(
  323. \Magento\Customer\Model\Customer::XML_PATH_GENERATE_HUMAN_FRIENDLY_ID,
  324. \Magento\Store\Model\ScopeInterface::SCOPE_STORE
  325. )
  326. ) {
  327. parent::setNewIncrementId($object);
  328. }
  329. return $this;
  330. }
  331. /**
  332. * Change reset password link token
  333. *
  334. * Stores new reset password link token and its creation time
  335. *
  336. * @param \Magento\Customer\Model\Customer $customer
  337. * @param string $passwordLinkToken
  338. * @return $this
  339. */
  340. public function changeResetPasswordLinkToken(\Magento\Customer\Model\Customer $customer, $passwordLinkToken)
  341. {
  342. if (is_string($passwordLinkToken) && !empty($passwordLinkToken)) {
  343. $customer->setRpToken($passwordLinkToken);
  344. $customer->setRpTokenCreatedAt(
  345. (new \DateTime())->format(\Magento\Framework\Stdlib\DateTime::DATETIME_PHP_FORMAT)
  346. );
  347. }
  348. return $this;
  349. }
  350. }