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