-
Notifications
You must be signed in to change notification settings - Fork 154
My Account > Change account information and Newsletter subscription #162
Changes from 3 commits
09dc09c
ef7f579
8963811
d0dd90f
b691d84
d759867
a1dd683
cc8d328
9200a16
2057ddf
20e3393
661988b
706af91
a1c4d33
0cb378b
6e6b5c3
6f73325
7ca6097
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,6 +13,11 @@ | |
use Magento\Framework\Serialize\SerializerInterface; | ||
use Magento\Framework\Webapi\ServiceOutputProcessor; | ||
use Magento\Customer\Api\Data\CustomerInterface; | ||
use Magento\Newsletter\Model\SubscriberFactory; | ||
use Magento\Customer\Model\CustomerRegistry; | ||
use Magento\Framework\Encryption\EncryptorInterface as Encryptor; | ||
use Magento\Store\Api\StoreResolverInterface; | ||
use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; | ||
|
||
/** | ||
* Customer field data provider, used for GraphQL request processing. | ||
|
@@ -29,11 +34,31 @@ class CustomerDataProvider | |
*/ | ||
private $serviceOutputProcessor; | ||
|
||
/** | ||
* @var StoreResolverInterface | ||
*/ | ||
private $storeResolver; | ||
|
||
/** | ||
* @var \Magento\Newsletter\Model\SubscriberFactory | ||
*/ | ||
protected $subscriberFactory; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pls, use |
||
|
||
/** | ||
* @var CustomerRegistry | ||
*/ | ||
protected $customerRegistry; | ||
|
||
/** | ||
* @var SerializerInterface | ||
*/ | ||
private $jsonSerializer; | ||
|
||
/** | ||
* @var Encryptor | ||
*/ | ||
protected $encryptor; | ||
|
||
/** | ||
* @param CustomerRepositoryInterface $customerRepository | ||
* @param ServiceOutputProcessor $serviceOutputProcessor | ||
|
@@ -42,11 +67,32 @@ class CustomerDataProvider | |
public function __construct( | ||
CustomerRepositoryInterface $customerRepository, | ||
ServiceOutputProcessor $serviceOutputProcessor, | ||
SerializerInterface $jsonSerializer | ||
SerializerInterface $jsonSerializer, | ||
SubscriberFactory $subscriberFactory, | ||
CustomerRegistry $customerRegistry, | ||
Encryptor $encryptor, | ||
StoreResolverInterface $storeResolver | ||
) { | ||
$this->customerRepository = $customerRepository; | ||
$this->serviceOutputProcessor = $serviceOutputProcessor; | ||
$this->jsonSerializer = $jsonSerializer; | ||
$this->subscriberFactory = $subscriberFactory; | ||
$this->customerRegistry = $customerRegistry; | ||
$this->encryptor = $encryptor; | ||
$this->storeResolver = $storeResolver; | ||
} | ||
|
||
/** | ||
* Load customer object | ||
* | ||
* @param int $customerId | ||
* @return CustomerInterface | ||
* @throws LocalizedException | ||
* @throws NoSuchEntityException | ||
*/ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need mathod-wrapper over Repository? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch, I planned to use it in another location, but currently it not needed anymore |
||
public function loadCustomerById(int $customerId): CustomerInterface | ||
{ | ||
return $this->customerRepository->getById($customerId); | ||
} | ||
|
||
/** | ||
|
@@ -56,7 +102,7 @@ public function __construct( | |
* @return array | ||
* @throws NoSuchEntityException|LocalizedException | ||
*/ | ||
public function getCustomerById(int $customerId) : array | ||
public function getCustomerById(int $customerId): array | ||
{ | ||
try { | ||
$customerObject = $this->customerRepository->getById($customerId); | ||
|
@@ -73,7 +119,7 @@ public function getCustomerById(int $customerId) : array | |
* @param CustomerInterface $customerObject | ||
* @return array | ||
*/ | ||
private function processCustomer(CustomerInterface $customerObject) : array | ||
private function processCustomer(CustomerInterface $customerObject): array | ||
{ | ||
$customer = $this->serviceOutputProcessor->process( | ||
$customerObject, | ||
|
@@ -110,4 +156,84 @@ private function processCustomer(CustomerInterface $customerObject) : array | |
|
||
return $customer; | ||
} | ||
|
||
/** | ||
* Check if customer is subscribed to Newsletter | ||
* | ||
* @param int $customerId | ||
* @return bool | ||
*/ | ||
public function isSubscribed(int $customerId): bool | ||
{ | ||
$checkSubscriber = $this->subscriberFactory->create()->loadByCustomerId($customerId); | ||
return $checkSubscriber->isSubscribed(); | ||
} | ||
|
||
/** | ||
* Manage customer subscription. Subscribe OR unsubscribe if required | ||
* | ||
* @param int $customerId | ||
* @param $newSubscriptionStatus | ||
* @return bool | ||
*/ | ||
public function manageSubscription(int $customerId, bool $newSubscriptionStatus): bool | ||
{ | ||
$checkSubscriber = $this->subscriberFactory->create()->loadByCustomerId($customerId); | ||
$isSubscribed = $this->isSubscribed($customerId); | ||
|
||
if ($newSubscriptionStatus === true && !$isSubscribed) { | ||
$this->subscriberFactory->create()->subscribeCustomerById($customerId); | ||
} elseif ($newSubscriptionStatus === false && $checkSubscriber->isSubscribed()) { | ||
$this->subscriberFactory->create()->unsubscribeCustomerById($customerId); | ||
} | ||
return true; | ||
} | ||
|
||
/** | ||
* @param int $customerId | ||
* @param array $customerData | ||
* @return CustomerInterface | ||
* @throws LocalizedException | ||
* @throws NoSuchEntityException | ||
* @throws \Magento\Framework\Exception\InputException | ||
* @throws \Magento\Framework\Exception\State\InputMismatchException | ||
*/ | ||
public function updateAccountInformation(int $customerId, array $customerData): CustomerInterface | ||
{ | ||
|
||
$customer = $this->loadCustomerById($customerId); | ||
|
||
if (isset($customerData['email']) | ||
&& $customer->getEmail() !== $customerData['email'] | ||
&& isset($customerData['password'])) { | ||
if ($this->isPasswordCorrect($customerData['password'], $customerId)) { | ||
$customer->setEmail($customerData['email']); | ||
} else { | ||
throw new GraphQlAuthorizationException(__('Invalid current user password.')); | ||
} | ||
} | ||
|
||
if (isset($customerData['firstname'])) { | ||
$customer->setFirstname($customerData['firstname']); | ||
} | ||
if (isset($customerData['lastname'])) { | ||
$customer->setLastname($customerData['lastname']); | ||
} | ||
|
||
$customer->setStoreId($this->storeResolver->getCurrentStoreId()); | ||
$this->customerRepository->save($customer); | ||
|
||
return $customer; | ||
} | ||
|
||
private function isPasswordCorrect(string $password, int $customerId) | ||
{ | ||
|
||
$customerSecure = $this->customerRegistry->retrieveSecureData($customerId); | ||
$hash = $customerSecure->getPasswordHash(); | ||
if (!$this->encryptor->validateHash($password, $hash)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can simplify this condition |
||
return false; | ||
} | ||
return true; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
<?php | ||
/** | ||
* Copyright © Magento, Inc. All rights reserved. | ||
* See COPYING.txt for license details. | ||
*/ | ||
declare(strict_types=1); | ||
|
||
namespace Magento\CustomerGraphQl\Model\Resolver; | ||
|
||
use Magento\Authorization\Model\UserContextInterface; | ||
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; | ||
use Magento\CustomerGraphQl\Model\Resolver\Customer\CustomerDataProvider; | ||
use Magento\Framework\GraphQl\Config\Element\Field; | ||
use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; | ||
use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; | ||
use Magento\Framework\GraphQl\Query\Resolver\Value; | ||
use Magento\Framework\GraphQl\Query\Resolver\ValueFactory; | ||
use Magento\Framework\GraphQl\Query\ResolverInterface; | ||
|
||
/** | ||
* Customers field resolver, used for GraphQL request processing. | ||
*/ | ||
class CustomerUpdate implements ResolverInterface | ||
{ | ||
/** | ||
* @var CustomerDataProvider | ||
*/ | ||
private $customerResolver; | ||
|
||
/** | ||
* @var ValueFactory | ||
*/ | ||
private $valueFactory; | ||
|
||
/** | ||
* @var \Magento\Newsletter\Model\SubscriberFactory | ||
*/ | ||
protected $subscriberFactory; | ||
|
||
/** | ||
* @var CustomerRegistry | ||
*/ | ||
protected $customerRegistry; | ||
|
||
/** | ||
* @param CustomerDataProvider $customerResolver | ||
* @param ValueFactory $valueFactory | ||
*/ | ||
public function __construct( | ||
CustomerDataProvider $customerResolver, | ||
ValueFactory $valueFactory, | ||
\Magento\Newsletter\Model\SubscriberFactory $subscriberFactory | ||
) { | ||
$this->customerResolver = $customerResolver; | ||
$this->valueFactory = $valueFactory; | ||
$this->subscriberFactory = $subscriberFactory; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function resolve( | ||
Field $field, | ||
$context, | ||
ResolveInfo $info, | ||
array $value = null, | ||
array $args = null | ||
) : Value { | ||
|
||
/** @var ContextInterface $context */ | ||
if ((!$context->getUserId()) || $context->getUserType() == UserContextInterface::USER_TYPE_GUEST) { | ||
throw new GraphQlAuthorizationException( | ||
__( | ||
'Current customer does not have access to the resource "%1"', | ||
[\Magento\Customer\Model\Customer::ENTITY] | ||
) | ||
); | ||
} | ||
|
||
$this->customerResolver->updateAccountInformation($context->getUserId(), $args); | ||
|
||
if (isset($args['is_subscribed'])) { | ||
$this->customerResolver->manageSubscription($context->getUserId(), $args['is_subscribed']); | ||
} | ||
|
||
$data = $args; | ||
$result = function () use ($data) { | ||
return !empty($data) ? $data : []; | ||
}; | ||
|
||
return $this->valueFactory->create($result); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need to return something if it is 'command' operation? |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -51,3 +51,13 @@ type CustomerAddressRegion @doc(description: "CustomerAddressRegion defines the | |
region_id: Int @doc(description: "Uniquely identifies the region") | ||
} | ||
|
||
type Mutation { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, need to cover functionality with API-functional tests |
||
customerUpdate (firstname: String, lastname: String, email: String, password: String, is_subscribed: Boolean): customerItem @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\CustomerUpdate") @doc(description:"Update customer personal information") | ||
} | ||
|
||
type customerItem { | ||
firstname: String | ||
lastname: String | ||
email: String | ||
is_subscribed: Boolean | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like this class has a lot of responsibility (looks like helper in Magento 1)
Need to split the class