Customer::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 14
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 0
c 0
b 0
f 0
nc 1
nop 12
dl 0
loc 14
rs 10

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/**
4
 * Copyright © 2016-present Spryker Systems GmbH. All rights reserved.
5
 * Use of this software requires acceptance of the Evaluation License Agreement. See LICENSE file.
6
 */
7
8
namespace Spryker\Zed\Customer\Business\Customer;
9
10
use DateTime;
11
use Generated\Shared\Transfer\AddressesTransfer;
12
use Generated\Shared\Transfer\AddressTransfer;
13
use Generated\Shared\Transfer\CustomerCollectionTransfer;
14
use Generated\Shared\Transfer\CustomerErrorTransfer;
15
use Generated\Shared\Transfer\CustomerResponseTransfer;
16
use Generated\Shared\Transfer\CustomerTransfer;
17
use Generated\Shared\Transfer\LocaleTransfer;
18
use Generated\Shared\Transfer\MailTransfer;
19
use Generated\Shared\Transfer\MessageTransfer;
20
use Orm\Zed\Customer\Persistence\SpyCustomer;
21
use Orm\Zed\Customer\Persistence\SpyCustomerAddress;
22
use Orm\Zed\Locale\Persistence\SpyLocaleQuery;
23
use Propel\Runtime\Collection\ObjectCollection;
24
use Spryker\Service\UtilText\UtilTextService;
25
use Spryker\Shared\Customer\Code\Messages;
26
use Spryker\Zed\Customer\Business\Customer\Checker\PasswordResetExpirationCheckerInterface;
27
use Spryker\Zed\Customer\Business\CustomerExpander\CustomerExpanderInterface;
28
use Spryker\Zed\Customer\Business\CustomerPasswordPolicy\CustomerPasswordPolicyValidatorInterface;
29
use Spryker\Zed\Customer\Business\Exception\CustomerNotFoundException;
30
use Spryker\Zed\Customer\Business\Executor\CustomerPluginExecutorInterface;
31
use Spryker\Zed\Customer\Business\ReferenceGenerator\CustomerReferenceGeneratorInterface;
32
use Spryker\Zed\Customer\Communication\Plugin\Mail\CustomerRestoredPasswordConfirmationMailTypePlugin;
33
use Spryker\Zed\Customer\Communication\Plugin\Mail\CustomerRestorePasswordMailTypePlugin;
34
use Spryker\Zed\Customer\CustomerConfig;
35
use Spryker\Zed\Customer\Dependency\Facade\CustomerToLocaleInterface;
36
use Spryker\Zed\Customer\Dependency\Facade\CustomerToMailInterface;
37
use Spryker\Zed\Customer\Persistence\CustomerQueryContainerInterface;
38
use Symfony\Component\Console\Output\OutputInterface;
39
use Symfony\Component\PasswordHasher\Hasher\NativePasswordHasher;
40
use Symfony\Component\PasswordHasher\PasswordHasherInterface;
41
use Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager;
42
use Symfony\Component\Security\Core\Encoder\NativePasswordEncoder;
43
use Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface;
44
45
class Customer implements CustomerInterface
46
{
47
    /**
48
     * @var int
49
     */
50
    protected const BCRYPT_FACTOR = 12;
51
52
    /**
53
     * @var string
54
     */
55
    protected const BCRYPT_SALT = '';
56
57
    /**
58
     * @var string
59
     */
60
    protected const GLOSSARY_KEY_CUSTOMER_AUTHORIZATION_VALIDATE_EMAIL_ADDRESS = 'customer.authorization.validate_email_address';
61
62
    /**
63
     * @var string
64
     */
65
    protected const GLOSSARY_KEY_CUSTOMER_REGISTRATION_SUCCESS = 'customer.registration.success';
66
67
    /**
68
     * @param \Spryker\Zed\Customer\Persistence\CustomerQueryContainerInterface $queryContainer
69
     * @param \Spryker\Zed\Customer\Business\ReferenceGenerator\CustomerReferenceGeneratorInterface $customerReferenceGenerator
70
     * @param \Spryker\Zed\Customer\CustomerConfig $customerConfig
71
     * @param \Spryker\Zed\Customer\Business\Customer\EmailValidatorInterface $emailValidator
72
     * @param \Spryker\Zed\Customer\Dependency\Facade\CustomerToMailInterface $mailFacade
73
     * @param \Orm\Zed\Locale\Persistence\SpyLocaleQuery $localePropelQuery $localePropelQuery
74
     * @param \Spryker\Zed\Customer\Dependency\Facade\CustomerToLocaleInterface $localeFacade
75
     * @param \Spryker\Zed\Customer\Business\CustomerExpander\CustomerExpanderInterface $customerExpander
76
     * @param \Spryker\Zed\Customer\Business\CustomerPasswordPolicy\CustomerPasswordPolicyValidatorInterface $customerPasswordPolicyValidator
77
     * @param \Spryker\Zed\Customer\Business\Customer\Checker\PasswordResetExpirationCheckerInterface $passwordResetExpirationChecker
78
     * @param \Spryker\Zed\Customer\Business\Executor\CustomerPluginExecutorInterface $customerPluginExecutor
79
     * @param array<\Spryker\Zed\CustomerExtension\Dependency\Plugin\CustomerPreUpdatePluginInterface> $customerPreUpdatePlugins
80
     */
81
    public function __construct(
82
        protected CustomerQueryContainerInterface $queryContainer,
83
        protected CustomerReferenceGeneratorInterface $customerReferenceGenerator,
84
        protected CustomerConfig $customerConfig,
85
        protected EmailValidatorInterface $emailValidator,
86
        protected CustomerToMailInterface $mailFacade,
87
        protected SpyLocaleQuery $localePropelQuery,
88
        protected CustomerToLocaleInterface $localeFacade,
89
        protected CustomerExpanderInterface $customerExpander,
90
        protected CustomerPasswordPolicyValidatorInterface $customerPasswordPolicyValidator,
91
        protected PasswordResetExpirationCheckerInterface $passwordResetExpirationChecker,
92
        protected CustomerPluginExecutorInterface $customerPluginExecutor,
93
        protected array $customerPreUpdatePlugins
94
    ) {
95
    }
96
97
    /**
98
     * @param string $email
99
     *
100
     * @return bool
101
     */
102
    public function hasEmail($email)
103
    {
104
        $customerCount = $this->queryContainer
105
            ->queryCustomerByEmail($email)
106
            ->count();
107
108
        return ($customerCount > 0);
109
    }
110
111
    /**
112
     * @param \Generated\Shared\Transfer\CustomerTransfer $customerTransfer
113
     *
114
     * @return \Generated\Shared\Transfer\CustomerTransfer
115
     */
116
    public function get(CustomerTransfer $customerTransfer)
117
    {
118
        $customerEntity = $this->getCustomer($customerTransfer);
119
        $customerTransfer->fromArray($customerEntity->toArray(), true);
120
121
        $customerTransfer = $this->attachAddresses($customerTransfer, $customerEntity);
122
        $customerTransfer = $this->attachLocale($customerTransfer, $customerEntity);
123
        $customerTransfer = $this->customerExpander->expand($customerTransfer);
124
125
        return $customerTransfer;
126
    }
127
128
    /**
129
     * @param \Generated\Shared\Transfer\CustomerTransfer $customerTransfer
130
     * @param \Orm\Zed\Customer\Persistence\SpyCustomer $customerEntity
131
     *
132
     * @return \Generated\Shared\Transfer\CustomerTransfer
133
     */
134
    protected function attachAddresses(CustomerTransfer $customerTransfer, SpyCustomer $customerEntity)
135
    {
136
        $addresses = $customerEntity->getAddresses();
137
        $addressesTransfer = $this->entityCollectionToTransferCollection($addresses, $customerEntity);
138
        $customerTransfer->setAddresses($addressesTransfer);
139
140
        $customerTransfer = $this->attachAddressesTransfer($customerTransfer, $addressesTransfer);
141
142
        return $customerTransfer;
143
    }
144
145
    /**
146
     * @param \Generated\Shared\Transfer\CustomerTransfer $customerTransfer
147
     *
148
     * @return \Generated\Shared\Transfer\CustomerResponseTransfer
149
     */
150
    public function add($customerTransfer)
151
    {
152
        if ($customerTransfer->getPassword()) {
153
            $customerResponseTransfer = $this->customerPasswordPolicyValidator->validatePassword($customerTransfer->getPassword());
154
            if (!$customerResponseTransfer->getIsSuccess()) {
155
                return $customerResponseTransfer;
156
            }
157
        }
158
159
        $customerTransfer = $this->encryptPassword($customerTransfer);
160
161
        $customerEntity = new SpyCustomer();
162
        $customerEntity->fromArray($customerTransfer->toArray());
163
164
        if ($customerTransfer->getLocale() !== null) {
165
            $this->addLocaleByLocaleName($customerEntity, $customerTransfer->getLocale()->getLocaleName());
166
        }
167
168
        $this->addLocale($customerEntity);
169
170
        $customerResponseTransfer = $this->createCustomerResponseTransfer();
171
        $customerResponseTransfer = $this->validateCustomerEmail($customerResponseTransfer, $customerEntity);
172
        if ($customerResponseTransfer->getIsSuccess() !== true) {
173
            return $customerResponseTransfer;
174
        }
175
176
        $customerEntity->setCustomerReference($this->customerReferenceGenerator->generateCustomerReference($customerTransfer));
177
        $customerEntity->setRegistrationKey($this->generateKey());
178
179
        $customerEntity->save();
180
181
        $customerTransfer->setIdCustomer($customerEntity->getPrimaryKey());
182
        $customerTransfer->setCustomerReference($customerEntity->getCustomerReference());
183
        $customerTransfer->setRegistrationKey($customerEntity->getRegistrationKey());
184
        $customerTransfer->setCreatedAt($customerEntity->getCreatedAt()->format('Y-m-d H:i:s.u'));
185
        $customerTransfer->setUpdatedAt($customerEntity->getUpdatedAt()->format('Y-m-d H:i:s.u'));
186
187
        $customerResponseTransfer
188
            ->setIsSuccess(true)
189
            ->setCustomerTransfer($customerTransfer);
190
191
        return $customerResponseTransfer;
192
    }
193
194
    /**
195
     * @param \Generated\Shared\Transfer\CustomerTransfer $customerTransfer
196
     *
197
     * @return \Generated\Shared\Transfer\CustomerResponseTransfer
198
     */
199
    public function register(CustomerTransfer $customerTransfer)
200
    {
201
        $customerResponseTransfer = $this->add($customerTransfer);
202
203
        if (!$customerResponseTransfer->getIsSuccess()) {
204
            return $customerResponseTransfer;
205
        }
206
207
        $this->customerPluginExecutor->executePostCustomerRegistrationPlugins($customerTransfer);
208
        $customerTransfer = $this->customerExpander->expand($customerTransfer);
209
210
        $this->sendRegistrationToken($customerTransfer);
211
212
        if ($customerTransfer->getSendPasswordToken()) {
213
            $this->sendPasswordRestoreMail($customerTransfer);
214
        }
215
216
        $message = static::GLOSSARY_KEY_CUSTOMER_REGISTRATION_SUCCESS;
217
        if ($this->customerConfig->isDoubleOptInEnabled()) {
218
            $message = static::GLOSSARY_KEY_CUSTOMER_AUTHORIZATION_VALIDATE_EMAIL_ADDRESS;
219
        }
220
        $messageTransfer = (new MessageTransfer())
221
            ->setValue($message);
222
223
        $customerResponseTransfer->setMessage($messageTransfer);
224
225
        return $customerResponseTransfer;
226
    }
227
228
    /**
229
     * @param \Orm\Zed\Customer\Persistence\SpyCustomer $customerEntity
230
     *
231
     * @return void
232
     */
233
    protected function addLocale(SpyCustomer $customerEntity)
234
    {
235
        if ($customerEntity->getLocale()) {
236
            return;
237
        }
238
239
        $localeName = $this->localeFacade->getCurrentLocaleName();
240
        $localeEntity = $this->localePropelQuery->findByLocaleName($localeName)->getFirst();
241
242
        if ($localeEntity) {
243
            $customerEntity->setLocale($localeEntity);
244
        }
245
    }
246
247
    /**
248
     * @param \Orm\Zed\Customer\Persistence\SpyCustomer $customerEntity
249
     * @param string $localeName
250
     *
251
     * @return void
252
     */
253
    protected function addLocaleByLocaleName(SpyCustomer $customerEntity, $localeName)
254
    {
255
        $localeEntity = $this->localePropelQuery->findByLocaleName($localeName)->getFirst();
256
257
        if ($localeEntity) {
258
            $customerEntity->setLocale($localeEntity);
259
        }
260
    }
261
262
    /**
263
     * @return string
264
     */
265
    protected function generateKey()
266
    {
267
        $utilTextService = new UtilTextService();
268
269
        return $utilTextService->generateRandomString(32);
270
    }
271
272
    /**
273
     * @param \Generated\Shared\Transfer\CustomerTransfer $customerTransfer
274
     *
275
     * @return void
276
     */
277
    protected function sendPasswordRestoreToken(CustomerTransfer $customerTransfer)
278
    {
279
        $customerTransfer = $this->get($customerTransfer);
280
        $restorePasswordLink = $this->customerConfig
281
            ->getCustomerPasswordRestoreTokenUrl(
282
                $customerTransfer->getRestorePasswordKey(),
283
                $customerTransfer->getStoreName(),
284
            );
285
286
        $customerTransfer->setRestorePasswordLink($restorePasswordLink);
287
288
        $mailTransfer = new MailTransfer();
289
        $mailTransfer->setType(CustomerRestorePasswordMailTypePlugin::MAIL_TYPE);
290
        $mailTransfer->setCustomer($customerTransfer);
291
        $mailTransfer->setLocale($customerTransfer->getLocale());
292
        $mailTransfer->setStoreName($customerTransfer->getStoreName());
293
294
        $this->mailFacade->handleMail($mailTransfer);
295
    }
296
297
    /**
298
     * @param \Generated\Shared\Transfer\CustomerTransfer $customerTransfer
299
     *
300
     * @return bool
301
     */
302
    protected function sendRegistrationToken(CustomerTransfer $customerTransfer)
303
    {
304
        $confirmationLink = $this->customerConfig
305
            ->getRegisterConfirmTokenUrl(
306
                $customerTransfer->getRegistrationKey(),
307
                $customerTransfer->getStoreName(),
308
            );
309
310
        $customerTransfer->setConfirmationLink($confirmationLink);
311
312
        $mailType = $this->customerConfig->isDoubleOptInEnabled()
313
            ? CustomerConfig::CUSTOMER_REGISTRATION_WITH_CONFIRMATION_MAIL_TYPE
314
            : CustomerConfig::CUSTOMER_REGISTRATION_MAIL_TYPE;
315
316
        $mailTransfer = new MailTransfer();
317
        $mailTransfer->setType($mailType);
318
        $mailTransfer->setCustomer($customerTransfer);
319
        $mailTransfer->setLocale($customerTransfer->getLocale());
320
        $mailTransfer->setStoreName($customerTransfer->getStoreName());
321
322
        $this->mailFacade->handleMail($mailTransfer);
323
324
        return true;
325
    }
326
327
    /**
328
     * @param \Generated\Shared\Transfer\CustomerTransfer $customerTransfer
329
     *
330
     * @return void
331
     */
332
    protected function sendPasswordRestoreConfirmation(CustomerTransfer $customerTransfer)
333
    {
334
        $customerTransfer = $this->get($customerTransfer);
335
336
        $mailTransfer = new MailTransfer();
337
        $mailTransfer->setType(CustomerRestoredPasswordConfirmationMailTypePlugin::MAIL_TYPE);
338
        $mailTransfer->setCustomer($customerTransfer);
339
        $mailTransfer->setLocale($customerTransfer->getLocale());
340
        $mailTransfer->setStoreName($customerTransfer->getStoreName());
341
342
        $this->mailFacade->handleMail($mailTransfer);
343
    }
344
345
    /**
346
     * @deprecated Use {@link \Spryker\Zed\Customer\Business\Customer\Customer::confirmCustomerRegistration()} instead.
347
     *
348
     * @param \Generated\Shared\Transfer\CustomerTransfer $customerTransfer
349
     *
350
     * @throws \Spryker\Zed\Customer\Business\Exception\CustomerNotFoundException
351
     *
352
     * @return \Generated\Shared\Transfer\CustomerTransfer
353
     */
354
    public function confirmRegistration(CustomerTransfer $customerTransfer)
355
    {
356
        $customerResponseTransfer = $this->confirmCustomerRegistration($customerTransfer);
357
        if (!$customerResponseTransfer->getIsSuccess()) {
358
            throw new CustomerNotFoundException(sprintf('Customer for registration key `%s` not found', $customerTransfer->getRegistrationKey()));
359
        }
360
361
        return $customerResponseTransfer->getCustomerTransfer();
362
    }
363
364
    /**
365
     * @param \Generated\Shared\Transfer\CustomerTransfer $customerTransfer
366
     *
367
     * @return \Generated\Shared\Transfer\CustomerResponseTransfer
368
     */
369
    public function confirmCustomerRegistration(CustomerTransfer $customerTransfer): CustomerResponseTransfer
370
    {
371
        $customerResponseTransfer = (new CustomerResponseTransfer())
372
            ->setCustomerTransfer($customerTransfer)
373
            ->setIsSuccess(true);
374
375
        $customerEntity = $this->queryContainer->queryCustomerByRegistrationKey($customerTransfer->getRegistrationKey())->findOne();
376
377
        if (!$customerEntity) {
378
            return $customerResponseTransfer
379
                ->setIsSuccess(false)
380
                ->addError((new CustomerErrorTransfer())->setMessage(CustomerConfig::GLOSSARY_KEY_CONFIRM_EMAIL_LINK_INVALID_OR_USED));
381
        }
382
383
        $customerEntity->setRegistered(new DateTime());
384
        $customerEntity->setRegistrationKey(null);
385
        $customerEntity->save();
386
387
        $customerTransfer = $customerTransfer->fromArray($customerEntity->toArray(), true);
388
389
        return $customerResponseTransfer->setCustomerTransfer($customerTransfer);
390
    }
391
392
    /**
393
     * @param \Generated\Shared\Transfer\CustomerTransfer $customerTransfer
394
     *
395
     * @return \Generated\Shared\Transfer\CustomerResponseTransfer
396
     */
397
    public function sendPasswordRestoreMail(CustomerTransfer $customerTransfer)
398
    {
399
        $customerResponseTransfer = $this->createCustomerResponseTransfer();
400
401
        try {
402
            $customerEntity = $this->getCustomer($customerTransfer);
403
        } catch (CustomerNotFoundException $e) {
404
            return $customerResponseTransfer;
405
        }
406
407
        $customerEntity->setRestorePasswordDate(new DateTime());
408
        $customerEntity->setRestorePasswordKey($this->generateKey());
409
410
        $customerEntity->save();
411
412
        $customerTransfer->fromArray($customerEntity->toArray(), true);
413
        $this->sendPasswordRestoreToken($customerTransfer);
414
415
        $customerResponseTransfer->setCustomerTransfer($customerTransfer);
416
417
        return $customerResponseTransfer;
418
    }
419
420
    /**
421
     * @param \Generated\Shared\Transfer\CustomerTransfer $customerTransfer
422
     *
423
     * @return \Generated\Shared\Transfer\CustomerResponseTransfer
424
     */
425
    public function restorePassword(CustomerTransfer $customerTransfer)
426
    {
427
        if ($this->customerConfig->isRestorePasswordValidationEnabled()) {
428
            $customerResponseTransfer = $this->customerPasswordPolicyValidator->validatePassword($customerTransfer->getPassword());
429
            if (!$customerResponseTransfer->getIsSuccess()) {
430
                return $customerResponseTransfer;
431
            }
432
        }
433
434
        $customerTransfer = $this->encryptPassword($customerTransfer);
435
436
        $customerResponseTransfer = $this->createCustomerResponseTransfer();
437
438
        try {
439
            $customerEntity = $this->getCustomer($customerTransfer);
440
        } catch (CustomerNotFoundException $e) {
441
            $customerError = new CustomerErrorTransfer();
442
            $customerError->setMessage(Messages::CUSTOMER_TOKEN_INVALID);
443
444
            $customerResponseTransfer
445
                ->setIsSuccess(false)
446
                ->addError($customerError);
447
448
            return $customerResponseTransfer;
449
        }
450
451
        $customerResponseTransfer = $this
452
            ->passwordResetExpirationChecker
453
            ->checkPasswordResetExpiration($customerEntity, $customerResponseTransfer);
454
455
        if (!$customerResponseTransfer->getIsSuccess()) {
456
             return $customerResponseTransfer;
457
        }
458
459
        $customerEntity->setRestorePasswordDate(null);
460
        $customerEntity->setRestorePasswordKey(null);
461
462
        $customerEntity->setPassword($customerTransfer->getPassword());
463
464
        $customerEntity->save();
465
        $customerTransfer->fromArray($customerEntity->toArray(), true);
466
        $this->sendPasswordRestoreConfirmation($customerTransfer);
467
468
        $customerResponseTransfer->setCustomerTransfer($customerTransfer);
469
470
        return $customerResponseTransfer;
471
    }
472
473
    /**
474
     * @param \Generated\Shared\Transfer\CustomerTransfer $customerTransfer
475
     *
476
     * @return bool
477
     */
478
    public function delete(CustomerTransfer $customerTransfer)
479
    {
480
        $customerEntity = $this->getCustomer($customerTransfer);
481
        $customerEntity->delete();
482
483
        $this->customerPluginExecutor->executeCustomerPostDeletePlugins($customerTransfer);
484
485
        return true;
486
    }
487
488
    /**
489
     * @param \Generated\Shared\Transfer\CustomerTransfer $customerTransfer
490
     *
491
     * @return \Generated\Shared\Transfer\CustomerResponseTransfer
492
     */
493
    public function update(CustomerTransfer $customerTransfer)
494
    {
495
        $customerResponseTransfer = $this->createCustomerResponseTransfer();
496
        if ($this->preValidateCustomerEmail($customerTransfer, $customerResponseTransfer)->getIsSuccess() === false) {
497
            return $customerResponseTransfer;
498
        }
499
        $customerTransfer = $this->executePreUpdatePlugins($customerTransfer, $customerResponseTransfer);
500
501
        if ($customerTransfer->getNewPassword()) {
502
            $customerResponseTransfer = $this->updatePassword(clone $customerTransfer);
503
504
            if ($customerResponseTransfer->getIsSuccess() === false) {
505
                return $customerResponseTransfer;
506
            }
507
508
            $updatedPasswordCustomerTransfer = $customerResponseTransfer->getCustomerTransfer();
509
            $customerTransfer->setNewPassword($updatedPasswordCustomerTransfer->getNewPassword())
510
                ->setPassword($updatedPasswordCustomerTransfer->getPassword());
511
        }
512
513
        $customerResponseTransfer->setCustomerTransfer($customerTransfer);
514
515
        $customerEntity = $this->getCustomer($customerTransfer);
516
        $customerEntity->fromArray($customerTransfer->modifiedToArray());
517
518
        if ($customerTransfer->getLocale() !== null) {
519
            $this->addLocaleByLocaleName($customerEntity, $customerTransfer->getLocale()->getLocaleName());
520
        }
521
522
        $customerResponseTransfer = $this->validateCustomerEmail($customerResponseTransfer, $customerEntity);
523
        if (!$customerResponseTransfer->getIsSuccess()) {
524
            return $customerResponseTransfer;
525
        }
526
527
        if ($customerTransfer->getSendPasswordToken()) {
528
            $this->sendPasswordRestoreMail($customerTransfer);
529
        }
530
531
        if (!$customerEntity->isModified()) {
532
            return $customerResponseTransfer;
533
        }
534
535
        $customerEntity->save();
536
537
        return $customerResponseTransfer;
538
    }
539
540
    /**
541
     * @param \Generated\Shared\Transfer\CustomerTransfer $customerTransfer
542
     * @param \Generated\Shared\Transfer\CustomerResponseTransfer $customerResponseTransfer
543
     *
544
     * @return \Generated\Shared\Transfer\CustomerResponseTransfer
545
     */
546
    protected function preValidateCustomerEmail(
547
        CustomerTransfer $customerTransfer,
548
        CustomerResponseTransfer $customerResponseTransfer
549
    ): CustomerResponseTransfer {
550
        if (!$this->emailValidator->isEmailAvailableForCustomer($customerTransfer->getEmailOrFail(), $customerTransfer->getIdCustomerOrFail())) {
551
            return $this->addErrorToCustomerResponseTransfer(
552
                $customerResponseTransfer,
553
                Messages::CUSTOMER_EMAIL_ALREADY_USED,
554
            );
555
        }
556
557
        return $customerResponseTransfer;
558
    }
559
560
    /**
561
     * @param bool $isSuccess
562
     *
563
     * @return \Generated\Shared\Transfer\CustomerResponseTransfer
564
     */
565
    protected function createCustomerResponseTransfer($isSuccess = true)
566
    {
567
        $customerResponseTransfer = new CustomerResponseTransfer();
568
        $customerResponseTransfer->setIsSuccess($isSuccess);
569
570
        return $customerResponseTransfer;
571
    }
572
573
    /**
574
     * @param \Generated\Shared\Transfer\CustomerResponseTransfer $customerResponseTransfer
575
     * @param \Orm\Zed\Customer\Persistence\SpyCustomer $customerEntity
576
     *
577
     * @return \Generated\Shared\Transfer\CustomerResponseTransfer
578
     */
579
    protected function validateCustomerEmail(CustomerResponseTransfer $customerResponseTransfer, SpyCustomer $customerEntity)
580
    {
581
        if (!$this->emailValidator->isFormatValid($customerEntity->getEmail())) {
582
            $customerResponseTransfer = $this->addErrorToCustomerResponseTransfer(
583
                $customerResponseTransfer,
584
                Messages::CUSTOMER_EMAIL_FORMAT_INVALID,
585
            );
586
        }
587
588
        if (!$this->emailValidator->isEmailAvailableForCustomer($customerEntity->getEmail(), $customerEntity->getIdCustomer())) {
589
            $customerResponseTransfer = $this->addErrorToCustomerResponseTransfer(
590
                $customerResponseTransfer,
591
                Messages::CUSTOMER_EMAIL_ALREADY_USED,
592
            );
593
        }
594
595
        if (!$this->emailValidator->isEmailLengthValid($customerEntity->getEmail())) {
596
            $customerResponseTransfer = $this->addErrorToCustomerResponseTransfer(
597
                $customerResponseTransfer,
598
                Messages::CUSTOMER_EMAIL_TOO_LONG,
599
            );
600
        }
601
602
        return $customerResponseTransfer;
603
    }
604
605
    /**
606
     * @param string $message
607
     *
608
     * @return \Generated\Shared\Transfer\CustomerErrorTransfer
609
     */
610
    protected function createErrorCustomerResponseTransfer($message)
611
    {
612
        $customerErrorTransfer = new CustomerErrorTransfer();
613
        $customerErrorTransfer->setMessage($message);
614
615
        return $customerErrorTransfer;
616
    }
617
618
    /**
619
     * @param \Generated\Shared\Transfer\CustomerTransfer $customerTransfer
620
     *
621
     * @return \Generated\Shared\Transfer\CustomerResponseTransfer
622
     */
623
    public function updatePassword(CustomerTransfer $customerTransfer)
624
    {
625
        $customerEntity = $this->getCustomer($customerTransfer);
626
627
        $customerResponseTransfer = $this->getCustomerPasswordInvalidResponse($customerEntity, $customerTransfer);
628
        if (!$customerResponseTransfer->getIsSuccess()) {
629
            return $customerResponseTransfer;
630
        }
631
632
        $customerResponseTransfer = $this->customerPasswordPolicyValidator->validatePassword($customerTransfer->getNewPassword());
633
        if (!$customerResponseTransfer->getIsSuccess()) {
634
            return $customerResponseTransfer;
635
        }
636
637
        $customerTransfer = $this->encryptNewPassword($customerTransfer);
638
639
        $customerEntity->setPassword($customerTransfer->getNewPassword());
640
641
        $changedRows = $customerEntity->save();
642
643
        $customerTransfer->fromArray($customerEntity->toArray(), true);
644
645
        $customerResponseTransfer
646
            ->setIsSuccess($changedRows > 0)
647
            ->setCustomerTransfer($customerTransfer);
648
649
        return $customerResponseTransfer;
650
    }
651
652
    /**
653
     * @param \Orm\Zed\Customer\Persistence\SpyCustomer $customerEntity
654
     * @param \Generated\Shared\Transfer\CustomerTransfer $customerTransfer
655
     *
656
     * @return \Generated\Shared\Transfer\CustomerResponseTransfer
657
     */
658
    protected function getCustomerPasswordInvalidResponse(SpyCustomer $customerEntity, CustomerTransfer $customerTransfer)
659
    {
660
        $customerResponseTransfer = new CustomerResponseTransfer();
661
        $customerResponseTransfer->setIsSuccess(true);
662
663
        if (!$this->isValidPassword($customerEntity->getPassword(), $customerTransfer->getPassword())) {
664
            $customerErrorTransfer = new CustomerErrorTransfer();
665
            $customerErrorTransfer
666
                ->setMessage(Messages::CUSTOMER_PASSWORD_INVALID);
667
            $customerResponseTransfer
668
                ->setIsSuccess(false)
669
                ->addError($customerErrorTransfer);
670
        }
671
672
        return $customerResponseTransfer;
673
    }
674
675
    /**
676
     * @param \Orm\Zed\Customer\Persistence\SpyCustomerAddress $addressEntity
677
     *
678
     * @return \Generated\Shared\Transfer\AddressTransfer
679
     */
680
    protected function entityToTransfer(SpyCustomerAddress $addressEntity)
681
    {
682
        $addressTransfer = new AddressTransfer();
683
        $addressTransfer->fromArray($addressEntity->toArray(), true);
684
        $addressTransfer->setIso2Code($addressEntity->getCountry()->getIso2Code());
685
686
        return $addressTransfer;
687
    }
688
689
    /**
690
     * @param \Propel\Runtime\Collection\ObjectCollection $addressEntities
691
     * @param \Orm\Zed\Customer\Persistence\SpyCustomer $customer
692
     *
693
     * @return \Generated\Shared\Transfer\AddressesTransfer
694
     */
695
    protected function entityCollectionToTransferCollection(ObjectCollection $addressEntities, SpyCustomer $customer)
696
    {
697
        $addressCollection = new AddressesTransfer();
698
699
        foreach ($addressEntities as $address) {
700
            $addressTransfer = $this->entityToTransfer($address);
701
702
            if ($customer->getDefaultBillingAddress() === $address->getIdCustomerAddress()) {
703
                $addressTransfer->setIsDefaultBilling(true);
704
            }
705
706
            if ($customer->getDefaultShippingAddress() === $address->getIdCustomerAddress()) {
707
                $addressTransfer->setIsDefaultShipping(true);
708
            }
709
710
            $addressCollection->addAddress($addressTransfer);
711
        }
712
713
        return $addressCollection;
714
    }
715
716
    /**
717
     * @param \Generated\Shared\Transfer\CustomerTransfer $customerTransfer
718
     * @param \Generated\Shared\Transfer\AddressesTransfer $addressesTransfer
719
     *
720
     * @return \Generated\Shared\Transfer\CustomerTransfer
721
     */
722
    protected function attachAddressesTransfer(CustomerTransfer $customerTransfer, AddressesTransfer $addressesTransfer)
723
    {
724
        foreach ($addressesTransfer->getAddresses() as $addressTransfer) {
725
            if ($addressTransfer->getIsDefaultBilling()) {
726
                $customerTransfer->addBillingAddress($addressTransfer);
727
            }
728
729
            if ($addressTransfer->getIsDefaultShipping()) {
730
                $customerTransfer->addShippingAddress($addressTransfer);
731
            }
732
        }
733
734
        return $customerTransfer;
735
    }
736
737
    /**
738
     * @param \Generated\Shared\Transfer\CustomerTransfer $customerTransfer
739
     *
740
     * @throws \Spryker\Zed\Customer\Business\Exception\CustomerNotFoundException
741
     *
742
     * @return \Orm\Zed\Customer\Persistence\SpyCustomer
743
     */
744
    protected function getCustomer(CustomerTransfer $customerTransfer)
745
    {
746
        $customerEntity = null;
747
748
        if ($customerTransfer->getIdCustomer()) {
749
            $customerEntity = $this->queryContainer->queryCustomerById($customerTransfer->getIdCustomer())
750
                ->findOne();
751
        } elseif ($customerTransfer->getEmail()) {
752
            $customerEntity = $this->queryContainer->queryCustomerByEmail($customerTransfer->getEmail())
753
                ->findOne();
754
        } elseif ($customerTransfer->getRestorePasswordKey()) {
755
            $customerEntity = $this->queryContainer->queryCustomerByRestorePasswordKey($customerTransfer->getRestorePasswordKey())
756
                ->findOne();
757
        }
758
759
        if ($customerEntity !== null) {
760
            return $customerEntity;
761
        }
762
763
        throw new CustomerNotFoundException(sprintf(
764
            'Customer not found by either ID `%s`, email `%s` or restore password key `%s`.',
765
            $customerTransfer->getIdCustomer(),
766
            $customerTransfer->getEmail(),
767
            $customerTransfer->getRestorePasswordKey(),
768
        ));
769
    }
770
771
    /**
772
     * @param \Generated\Shared\Transfer\CustomerTransfer $customerTransfer
773
     *
774
     * @return bool
775
     */
776
    protected function hasCustomer(CustomerTransfer $customerTransfer)
777
    {
778
        $result = false;
779
        $customerEntity = null;
780
781
        if ($customerTransfer->getIdCustomer()) {
782
            $customerEntity = $this->queryContainer
783
                ->queryCustomerById($customerTransfer->getIdCustomer())
784
                ->findOne();
785
        } elseif ($customerTransfer->getEmail()) {
786
            $customerEntity = $this->queryContainer
787
                ->queryCustomerByEmail($customerTransfer->getEmail())
788
                ->findOne();
789
        }
790
791
        if ($customerEntity !== null) {
792
            $result = true;
793
        }
794
795
        return $result;
796
    }
797
798
    /**
799
     * @param \Generated\Shared\Transfer\CustomerTransfer $customerTransfer
800
     *
801
     * @return bool
802
     */
803
    public function tryAuthorizeCustomerByEmailAndPassword(CustomerTransfer $customerTransfer)
804
    {
805
        $customerEntity = $this->queryContainer->queryCustomerByEmail($customerTransfer->getEmail())
806
            ->findOne();
807
808
        if (!$customerEntity) {
809
            return false;
810
        }
811
812
        if (!$this->isValidPassword($customerEntity->getPassword(), $customerTransfer->getPassword())) {
813
            return false;
814
        }
815
816
        if ($this->customerConfig->isDoubleOptInEnabled() && !$customerEntity->getRegistered()) {
817
            return false;
818
        }
819
820
        return true;
821
    }
822
823
    /**
824
     * @param \Generated\Shared\Transfer\CustomerTransfer $customerTransfer
825
     *
826
     * @return \Generated\Shared\Transfer\CustomerTransfer
827
     */
828
    protected function encryptPassword(CustomerTransfer $customerTransfer)
829
    {
830
        $currentPassword = $customerTransfer->getPassword();
831
        $customerTransfer->setPassword($this->getEncodedPassword($currentPassword));
832
833
        return $customerTransfer;
834
    }
835
836
    /**
837
     * @param \Generated\Shared\Transfer\CustomerTransfer $customerTransfer
838
     *
839
     * @return \Generated\Shared\Transfer\CustomerTransfer
840
     */
841
    protected function encryptNewPassword(CustomerTransfer $customerTransfer)
842
    {
843
        $currentPassword = $customerTransfer->getNewPassword();
844
        $customerTransfer->setNewPassword($this->getEncodedPassword($currentPassword));
845
846
        return $customerTransfer;
847
    }
848
849
    /**
850
     * @param string|null $currentPassword
851
     *
852
     * @return string|null
853
     */
854
    protected function getEncodedPassword($currentPassword)
855
    {
856
        if ($currentPassword === null) {
857
            return $currentPassword;
858
        }
859
860
        if ($this->isSymfonyVersion5() === true) {
861
            return $this->getPasswordEncoder()->encodePassword($currentPassword, static::BCRYPT_SALT);
862
        }
863
864
        return $this->createPasswordHasher()->hash($currentPassword);
865
    }
866
867
    /**
868
     * @return \Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface
869
     */
870
    protected function getPasswordEncoder(): PasswordEncoderInterface
871
    {
872
        return new NativePasswordEncoder(null, null, static::BCRYPT_FACTOR);
873
    }
874
875
    /**
876
     * @return \Symfony\Component\PasswordHasher\PasswordHasherInterface
877
     */
878
    public function createPasswordHasher(): PasswordHasherInterface
879
    {
880
        return new NativePasswordHasher(null, null, static::BCRYPT_FACTOR);
881
    }
882
883
    /**
884
     * @param string $hash
885
     * @param string $rawPassword
886
     *
887
     * @return bool
888
     */
889
    protected function isValidPassword($hash, $rawPassword)
890
    {
891
        if ($this->isSymfonyVersion5() === true) {
892
            return $this->getPasswordEncoder()->isPasswordValid($hash, $rawPassword, static::BCRYPT_SALT);
893
        }
894
895
        return $this->createPasswordHasher()->verify($hash, $rawPassword);
896
    }
897
898
    /**
899
     * @param \Generated\Shared\Transfer\CustomerTransfer $customerTransfer $customerTransfer
900
     *
901
     * @return \Generated\Shared\Transfer\CustomerTransfer|null
902
     */
903
    public function findById($customerTransfer)
904
    {
905
        $customerTransfer->requireIdCustomer();
906
907
        $customerEntity = $this->queryContainer->queryCustomerById($customerTransfer->getIdCustomer())
908
            ->findOne();
909
        if ($customerEntity === null) {
910
            return null;
911
        }
912
913
        $customerTransfer = $this->hydrateCustomerTransferFromEntity($customerTransfer, $customerEntity);
914
915
        return $customerTransfer;
916
    }
917
918
    /**
919
     * @param string $customerReference
920
     *
921
     * @return \Generated\Shared\Transfer\CustomerTransfer|null
922
     */
923
    public function findByReference($customerReference)
924
    {
925
        $customerEntity = $this->queryContainer
926
            ->queryCustomerByReference($customerReference)
927
            ->findOne();
928
929
        if ($customerEntity === null) {
930
            return null;
931
        }
932
933
        $customerTransfer = new CustomerTransfer();
934
        $customerTransfer = $this->hydrateCustomerTransferFromEntity($customerTransfer, $customerEntity);
935
936
        return $customerTransfer;
937
    }
938
939
    /**
940
     * @param \Generated\Shared\Transfer\CustomerTransfer $customerTransfer
941
     * @param \Orm\Zed\Customer\Persistence\SpyCustomer $customerEntity
942
     *
943
     * @return \Generated\Shared\Transfer\CustomerTransfer
944
     */
945
    protected function hydrateCustomerTransferFromEntity(
946
        CustomerTransfer $customerTransfer,
947
        SpyCustomer $customerEntity
948
    ) {
949
        $customerTransfer->fromArray($customerEntity->toArray(), true);
950
        $customerTransfer = $this->attachAddresses($customerTransfer, $customerEntity);
951
        $customerTransfer = $this->attachLocale($customerTransfer, $customerEntity);
952
953
        return $customerTransfer;
954
    }
955
956
    /**
957
     * @param \Generated\Shared\Transfer\CustomerTransfer $customerTransfer
958
     * @param \Orm\Zed\Customer\Persistence\SpyCustomer $customerEntity
959
     *
960
     * @return \Generated\Shared\Transfer\CustomerTransfer
961
     */
962
    protected function attachLocale(CustomerTransfer $customerTransfer, SpyCustomer $customerEntity)
963
    {
964
        $localeEntity = $customerEntity->getLocale();
965
        if (!$localeEntity) {
966
            return $customerTransfer;
967
        }
968
969
        $localeTransfer = new LocaleTransfer();
970
        $localeTransfer->fromArray($localeEntity->toArray(), true);
971
        $customerTransfer->setLocale($localeTransfer);
972
973
        return $customerTransfer;
974
    }
975
976
    /**
977
     * @param \Generated\Shared\Transfer\CustomerResponseTransfer $customerResponseTransfer
978
     * @param string $message
979
     *
980
     * @return \Generated\Shared\Transfer\CustomerResponseTransfer
981
     */
982
    protected function addErrorToCustomerResponseTransfer(CustomerResponseTransfer $customerResponseTransfer, string $message): CustomerResponseTransfer
983
    {
984
        $customerResponseTransfer->setIsSuccess(false);
985
        $customerResponseTransfer->addError(
986
            $this->createErrorCustomerResponseTransfer($message),
987
        );
988
989
        return $customerResponseTransfer;
990
    }
991
992
    /**
993
     * @param \Generated\Shared\Transfer\CustomerCollectionTransfer $customerCollectionTransfer
994
     * @param \Symfony\Component\Console\Output\OutputInterface|null $output
995
     *
996
     * @return void
997
     */
998
    public function sendPasswordRestoreMailForCustomerCollection(
999
        CustomerCollectionTransfer $customerCollectionTransfer,
1000
        ?OutputInterface $output = null
1001
    ): void {
1002
        $customersCount = $customerCollectionTransfer->getCustomers()->count();
1003
        foreach ($customerCollectionTransfer->getCustomers() as $index => $customer) {
1004
            $this->sendPasswordRestoreMail($customer);
1005
1006
            if (!$output) {
1007
                continue;
1008
            }
1009
1010
            $output->write(sprintf(
1011
                "%d out of %d emails sent \r%s",
1012
                ++$index,
1013
                $customersCount,
1014
                $index === $customersCount ? PHP_EOL : '',
1015
            ));
1016
        }
1017
    }
1018
1019
    /**
1020
     * @deprecated Shim for Symfony Security Core 5.x, to be removed when Symfony Security Core dependency becomes 6.x+.
1021
     *
1022
     * @return bool
1023
     */
1024
    protected function isSymfonyVersion5(): bool
1025
    {
1026
        return class_exists(AuthenticationProviderManager::class);
1027
    }
1028
1029
    /**
1030
     * @param \Generated\Shared\Transfer\CustomerTransfer $customerTransfer
1031
     * @param \Generated\Shared\Transfer\CustomerResponseTransfer $customerResponseTransfer
1032
     *
1033
     * @return \Generated\Shared\Transfer\CustomerTransfer
1034
     */
1035
    protected function executePreUpdatePlugins(CustomerTransfer $customerTransfer, CustomerResponseTransfer $customerResponseTransfer): CustomerTransfer
1036
    {
1037
        if ($this->shouldSkipPreUpdatePlugins($customerTransfer)) {
1038
            return $customerTransfer;
1039
        }
1040
1041
        foreach ($this->customerPreUpdatePlugins as $customerPreUpdatePlugin) {
1042
            $customerTransfer = $customerPreUpdatePlugin->preUpdate($customerTransfer);
1043
1044
            if ($customerTransfer->getMessage() === null) {
1045
                continue;
1046
            }
1047
1048
            $customerResponseTransfer->addMessage(
1049
                (new MessageTransfer())->setValue($customerTransfer->getMessage()),
1050
            );
1051
        }
1052
1053
        return $customerTransfer;
1054
    }
1055
1056
    /**
1057
     * @param \Generated\Shared\Transfer\CustomerTransfer $customerTransfer
1058
     *
1059
     * @return bool
1060
     */
1061
    protected function shouldSkipPreUpdatePlugins(CustomerTransfer $customerTransfer): bool
1062
    {
1063
        return $customerTransfer->getAnonymizedAt() !== null || $customerTransfer->getIsEditedInBackoffice() === true;
1064
    }
1065
}
1066