Skip to content

Commit 85d0700

Browse files
authored
LYNX-110: Customer custom_attributes field
1 parent eb5ca28 commit 85d0700

File tree

14 files changed

+601
-30
lines changed

14 files changed

+601
-30
lines changed

app/code/Magento/Customer/Test/Fixture/CustomerAttribute.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ class CustomerAttribute implements RevertibleDataFixtureInterface
3636
'is_unique' => '0',
3737
'frontend_class' => null,
3838
'used_in_forms' => [],
39+
'sort_order' => null,
40+
'attribute_set_id' => null,
41+
'attribute_group_id' => null,
3942
];
4043

4144
/**

app/code/Magento/CustomerGraphQl/Model/Customer/ExtractCustomerData.php

Lines changed: 33 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,15 @@
77

88
namespace Magento\CustomerGraphQl\Model\Customer;
99

10+
use Magento\Customer\Api\CustomerMetadataInterface;
1011
use Magento\Customer\Api\CustomerRepositoryInterface;
12+
use Magento\Customer\Api\Data\CustomerInterface;
13+
use Magento\Eav\Model\AttributeRepository;
14+
use Magento\EavGraphQl\Model\GetAttributeValueComposite;
15+
use Magento\EavGraphQl\Model\Uid;
1116
use Magento\Framework\Exception\LocalizedException;
12-
use Magento\Framework\Serialize\SerializerInterface;
17+
use Magento\Framework\Exception\NoSuchEntityException;
1318
use Magento\Framework\Webapi\ServiceOutputProcessor;
14-
use Magento\Customer\Api\Data\CustomerInterface;
1519

1620
/**
1721
* Transform single customer data from object to in array format
@@ -24,20 +28,36 @@ class ExtractCustomerData
2428
private $serviceOutputProcessor;
2529

2630
/**
27-
* @var SerializerInterface
31+
* @var AttributeRepository
32+
*/
33+
private AttributeRepository $attributeRepository;
34+
35+
/**
36+
* @var Uid
2837
*/
29-
private $serializer;
38+
private Uid $uid;
39+
40+
/**
41+
* @var GetAttributeValueComposite
42+
*/
43+
private GetAttributeValueComposite $getAttributeValueComposite;
3044

3145
/**
3246
* @param ServiceOutputProcessor $serviceOutputProcessor
33-
* @param SerializerInterface $serializer
47+
* @param AttributeRepository $attributeRepository
48+
* @param Uid $uid
49+
* @param GetAttributeValueComposite $getAttributeValueComposite
3450
*/
3551
public function __construct(
3652
ServiceOutputProcessor $serviceOutputProcessor,
37-
SerializerInterface $serializer
53+
AttributeRepository $attributeRepository,
54+
Uid $uid,
55+
GetAttributeValueComposite $getAttributeValueComposite
3856
) {
3957
$this->serviceOutputProcessor = $serviceOutputProcessor;
40-
$this->serializer = $serializer;
58+
$this->attributeRepository = $attributeRepository;
59+
$this->uid = $uid;
60+
$this->getAttributeValueComposite = $getAttributeValueComposite;
4161
}
4262

4363
/**
@@ -77,30 +97,14 @@ public function execute(CustomerInterface $customer): array
7797
if (isset($customerData['extension_attributes'])) {
7898
$customerData = array_merge($customerData, $customerData['extension_attributes']);
7999
}
80-
$customAttributes = [];
81100
if (isset($customerData['custom_attributes'])) {
82-
foreach ($customerData['custom_attributes'] as $attribute) {
83-
$isArray = false;
84-
if (is_array($attribute['value'])) {
85-
$isArray = true;
86-
foreach ($attribute['value'] as $attributeValue) {
87-
if (is_array($attributeValue)) {
88-
$customAttributes[$attribute['attribute_code']] = $this->serializer->serialize(
89-
$attribute['value']
90-
);
91-
continue;
92-
}
93-
$customAttributes[$attribute['attribute_code']] = implode(',', $attribute['value']);
94-
continue;
95-
}
96-
}
97-
if ($isArray) {
98-
continue;
99-
}
100-
$customAttributes[$attribute['attribute_code']] = $attribute['value'];
101-
}
101+
$customerData['custom_attributes'] = array_map(
102+
function (array $customAttribute) {
103+
return $this->getAttributeValueComposite->execute($customAttribute);
104+
},
105+
$customerData['custom_attributes']
106+
);
102107
}
103-
$customerData = array_merge($customerData, $customAttributes);
104108
//Fields are deprecated and should not be exposed on storefront.
105109
$customerData['group_id'] = null;
106110
$customerData['id'] = null;
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\CustomerGraphQl\Model\Customer;
9+
10+
use Magento\Customer\Api\CustomerMetadataInterface;
11+
use Magento\Eav\Model\AttributeRepository;
12+
use Magento\EavGraphQl\Model\GetAttributeSelectedOptionComposite;
13+
use Magento\EavGraphQl\Model\GetAttributeValueInterface;
14+
use Magento\EavGraphQl\Model\Uid;
15+
16+
/**
17+
* Custom attribute value provider for customer
18+
*/
19+
class GetCustomAttributes implements GetAttributeValueInterface
20+
{
21+
/**
22+
* @var Uid
23+
*/
24+
private Uid $uid;
25+
26+
/**
27+
* @var AttributeRepository
28+
*/
29+
private AttributeRepository $attributeRepository;
30+
31+
/**
32+
* @var GetAttributeSelectedOptionComposite
33+
*/
34+
private GetAttributeSelectedOptionComposite $attributeSelectedOptionComposite;
35+
36+
/**
37+
* @var array
38+
*/
39+
private array $frontendInputs;
40+
41+
/**
42+
* @param Uid $uid
43+
* @param AttributeRepository $attributeRepository
44+
* @param GetAttributeSelectedOptionComposite $attributeSelectedOptionComposite
45+
* @param array $frontendInputs
46+
*/
47+
public function __construct(
48+
Uid $uid,
49+
AttributeRepository $attributeRepository,
50+
GetAttributeSelectedOptionComposite $attributeSelectedOptionComposite,
51+
array $frontendInputs = []
52+
) {
53+
$this->uid = $uid;
54+
$this->attributeRepository = $attributeRepository;
55+
$this->frontendInputs = $frontendInputs;
56+
$this->attributeSelectedOptionComposite = $attributeSelectedOptionComposite;
57+
}
58+
59+
/**
60+
* @inheritDoc
61+
*/
62+
public function execute(array $customAttribute): ?array
63+
{
64+
$attr = $this->attributeRepository->get(
65+
CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER,
66+
$customAttribute['attribute_code']
67+
);
68+
69+
$result = [
70+
'uid' => $this->uid->encode(
71+
CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER,
72+
$customAttribute['attribute_code']
73+
),
74+
'code' => $customAttribute['attribute_code']
75+
];
76+
77+
if (in_array($attr->getFrontendInput(), $this->frontendInputs)) {
78+
$result['selected_options'] = $this->attributeSelectedOptionComposite->execute($customAttribute);
79+
} else {
80+
$result['value'] = $customAttribute['value'];
81+
}
82+
return $result;
83+
}
84+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\CustomerGraphQl\Model\Customer;
9+
10+
use Magento\Customer\Api\CustomerMetadataInterface;
11+
use Magento\Eav\Model\AttributeRepository;
12+
use Magento\EavGraphQl\Model\GetAttributeSelectedOptionInterface;
13+
use Magento\Framework\GraphQl\Query\Uid;
14+
15+
/**
16+
* Custom attribute value provider for customer
17+
*/
18+
class GetCustomSelectedOptionAttributes implements GetAttributeSelectedOptionInterface
19+
{
20+
/**
21+
* @var Uid
22+
*/
23+
private Uid $uid;
24+
25+
/**
26+
* @var AttributeRepository
27+
*/
28+
private AttributeRepository $attributeRepository;
29+
30+
/**
31+
* @param Uid $uid
32+
* @param AttributeRepository $attributeRepository
33+
*/
34+
public function __construct(
35+
Uid $uid,
36+
AttributeRepository $attributeRepository,
37+
) {
38+
$this->uid = $uid;
39+
$this->attributeRepository = $attributeRepository;
40+
}
41+
42+
/**
43+
* @inheritDoc
44+
*/
45+
public function execute(array $customAttribute): ?array
46+
{
47+
$attr = $this->attributeRepository->get(
48+
CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER,
49+
$customAttribute['attribute_code']
50+
);
51+
52+
$result = [];
53+
$selectedValues = explode(',', $customAttribute['value']);
54+
foreach ($attr->getOptions() as $option) {
55+
if (!in_array($option->getValue(), $selectedValues)) {
56+
continue;
57+
}
58+
$result[] = [
59+
'uid' => $this->uid->encode($option->getValue()),
60+
'value' => $option->getValue(),
61+
'label' => $option->getLabel()
62+
];
63+
}
64+
return $result;
65+
}
66+
}

app/code/Magento/CustomerGraphQl/etc/graphql/di.xml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,34 @@
9292
<argument name="type" xsi:type="string">customer_address</argument>
9393
</arguments>
9494
</virtualType>
95+
<type name="Magento\EavGraphQl\Model\GetAttributeValueComposite">
96+
<arguments>
97+
<argument name="providers" xsi:type="array">
98+
<item name="customer" xsi:type="object">Magento\CustomerGraphQl\Model\Customer\GetCustomAttributes</item>
99+
</argument>
100+
</arguments>
101+
</type>
102+
<type name="Magento\CustomerGraphQl\Model\Customer\GetCustomAttributes">
103+
<arguments>
104+
<argument name="frontendInputs" xsi:type="array">
105+
<item name="multiselect" xsi:type="string">multiselect</item>
106+
<item name="select" xsi:type="string">select</item>
107+
</argument>
108+
</arguments>
109+
</type>
110+
<type name="Magento\EavGraphQl\Model\GetAttributeSelectedOptionComposite">
111+
<arguments>
112+
<argument name="providers" xsi:type="array">
113+
<item name="customer" xsi:type="object">Magento\CustomerGraphQl\Model\Customer\GetCustomSelectedOptionAttributes</item>
114+
</argument>
115+
</arguments>
116+
</type>
117+
<type name="Magento\EavGraphQl\Model\TypeResolver\AttributeValue">
118+
<arguments>
119+
<argument name="frontendInputs" xsi:type="array">
120+
<item name="multiselect" xsi:type="string">multiselect</item>
121+
<item name="select" xsi:type="string">select</item>
122+
</argument>
123+
</arguments>
124+
</type>
95125
</config>

app/code/Magento/CustomerGraphQl/etc/schema.graphqls

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ type Customer @doc(description: "Defines the customer name, addresses, and other
136136
is_subscribed: Boolean @doc(description: "Indicates whether the customer is subscribed to the company's newsletter.") @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\IsSubscribed")
137137
addresses: [CustomerAddress] @doc(description: "An array containing the customer's shipping and billing addresses.") @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\CustomerAddresses")
138138
gender: Int @doc(description: "The customer's gender (Male - 1, Female - 2).")
139+
custom_attributes: [AttributeValueInterface!]! @doc(description: "Customer's custom attributes.")
139140
}
140141

141142
type CustomerAddress @doc(description: "Contains detailed information about a customer's billing or shipping address."){
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\EavGraphQl\Model;
9+
10+
use Magento\Framework\Exception\LocalizedException;
11+
use Magento\Framework\Exception\RuntimeException;
12+
13+
/**
14+
* Format selected options values provider for GraphQL output
15+
*/
16+
class GetAttributeSelectedOptionComposite implements GetAttributeSelectedOptionInterface
17+
{
18+
/**
19+
* @var GetAttributeSelectedOptionInterface[]
20+
*/
21+
private array $providers;
22+
23+
/**
24+
* @param array $providers
25+
*/
26+
public function __construct(array $providers = [])
27+
{
28+
$this->providers = $providers;
29+
}
30+
31+
/**
32+
* Returns right GetAttributeSelectedOptionInterface to use for attribute with $attributeCode
33+
*
34+
* @param array $customAttribute
35+
* @return array|null
36+
* @throws RuntimeException
37+
*/
38+
public function execute(array $customAttribute): ?array
39+
{
40+
foreach ($this->providers as $provider) {
41+
if (!$provider instanceof GetAttributeSelectedOptionInterface) {
42+
throw new RuntimeException(
43+
__('Configured attribute selected option data providers should implement
44+
GetAttributeSelectedOptionInterface')
45+
);
46+
}
47+
48+
try {
49+
return $provider->execute($customAttribute);
50+
} catch (LocalizedException $e) {
51+
continue;
52+
}
53+
}
54+
return null;
55+
}
56+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\EavGraphQl\Model;
9+
10+
use Magento\Framework\Exception\LocalizedException;
11+
12+
/**
13+
* Interface for getting custom attributes seelcted options.
14+
*/
15+
interface GetAttributeSelectedOptionInterface
16+
{
17+
/**
18+
* Retrieve all selected options of an attribute filtered by attribute code
19+
*
20+
* @param array $customAttribute
21+
* @return array|null
22+
* @throws LocalizedException
23+
*/
24+
public function execute(array $customAttribute): ?array;
25+
}

0 commit comments

Comments
 (0)