Skip to content

Commit f7d0491

Browse files
authored
LYNX-105: Catalog GraphQL SwatchAttributeOptionMetadata implementation of CustomAttributeOptionInterface (#122)
1 parent a796656 commit f7d0491

File tree

4 files changed

+218
-3
lines changed

4 files changed

+218
-3
lines changed

app/code/Magento/Catalog/Test/Fixture/Attribute.php

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,12 @@
1111
use Magento\Catalog\Api\ProductAttributeManagementInterface;
1212
use Magento\Catalog\Api\ProductAttributeRepositoryInterface;
1313
use Magento\Catalog\Model\Product;
14+
use Magento\Catalog\Model\ResourceModel\Attribute as ResourceModelAttribute;
15+
use Magento\Catalog\Model\ResourceModel\Eav\Attribute as EavAttribute;
16+
use Magento\Eav\Model\AttributeFactory;
1417
use Magento\Eav\Setup\EavSetup;
1518
use Magento\Framework\DataObject;
19+
use Magento\TestFramework\Fixture\Api\DataMerger;
1620
use Magento\TestFramework\Fixture\Api\ServiceFactory;
1721
use Magento\TestFramework\Fixture\RevertibleDataFixtureInterface;
1822
use Magento\TestFramework\Fixture\Data\ProcessorInterface;
@@ -49,7 +53,6 @@ class Attribute implements RevertibleDataFixtureInterface
4953
'backend_type' => 'varchar',
5054
'is_unique' => '0',
5155
'validation_rules' => []
52-
5356
];
5457

5558
private const DEFAULT_ATTRIBUTE_SET_DATA = [
@@ -78,29 +81,59 @@ class Attribute implements RevertibleDataFixtureInterface
7881
*/
7982
private $productAttributeManagement;
8083

84+
/**
85+
* @var AttributeFactory
86+
*/
87+
private AttributeFactory $attributeFactory;
88+
89+
/**
90+
* @var DataMerger
91+
*/
92+
private DataMerger $dataMerger;
93+
94+
/**
95+
* @var ResourceModelAttribute
96+
*/
97+
private ResourceModelAttribute $resourceModelAttribute;
98+
8199
/**
82100
* @param ServiceFactory $serviceFactory
83101
* @param ProcessorInterface $dataProcessor
84102
* @param EavSetup $eavSetup
103+
* @param ProductAttributeManagementInterface $productAttributeManagement
104+
* @param AttributeFactory $attributeFactory
105+
* @param DataMerger $dataMerger
106+
* @param ResourceModelAttribute $resourceModelAttribute
85107
*/
86108
public function __construct(
87109
ServiceFactory $serviceFactory,
88110
ProcessorInterface $dataProcessor,
89111
EavSetup $eavSetup,
90-
ProductAttributeManagementInterface $productAttributeManagement
112+
ProductAttributeManagementInterface $productAttributeManagement,
113+
AttributeFactory $attributeFactory,
114+
DataMerger $dataMerger,
115+
ResourceModelAttribute $resourceModelAttribute
91116
) {
92117
$this->serviceFactory = $serviceFactory;
93118
$this->dataProcessor = $dataProcessor;
94119
$this->eavSetup = $eavSetup;
95120
$this->productAttributeManagement = $productAttributeManagement;
121+
$this->attributeFactory = $attributeFactory;
122+
$this->dataMerger = $dataMerger;
123+
$this->resourceModelAttribute = $resourceModelAttribute;
96124
}
97125

98126
/**
99127
* {@inheritdoc}
100128
* @param array $data Parameters. Same format as Attribute::DEFAULT_DATA.
129+
* @return DataObject|null
101130
*/
102131
public function apply(array $data = []): ?DataObject
103132
{
133+
if (array_key_exists('additional_data', $data)) {
134+
return $this->applyAttributeWithAdditionalData($data);
135+
}
136+
104137
$service = $this->serviceFactory->create(ProductAttributeRepositoryInterface::class, 'save');
105138

106139
/**
@@ -139,6 +172,26 @@ public function revert(DataObject $data): void
139172
);
140173
}
141174

175+
/**
176+
* @param array $data Parameters. Same format as Attribute::DEFAULT_DATA.
177+
* @return DataObject|null
178+
*/
179+
private function applyAttributeWithAdditionalData(array $data = []): ?DataObject
180+
{
181+
$defaultData = array_merge(self::DEFAULT_DATA, ['additional_data' => null]);
182+
/** @var EavAttribute $attr */
183+
$attr = $this->attributeFactory->createAttribute(EavAttribute::class, $defaultData);
184+
$mergedData = $this->dataProcessor->process($this, $this->dataMerger->merge($defaultData, $data));
185+
186+
$attributeSetData = $this->prepareAttributeSetData(
187+
array_intersect_key($data, self::DEFAULT_ATTRIBUTE_SET_DATA)
188+
);
189+
190+
$attr->setData(array_merge($mergedData, $attributeSetData));
191+
$this->resourceModelAttribute->save($attr);
192+
return $attr;
193+
}
194+
142195
/**
143196
* Prepare attribute data
144197
*

app/code/Magento/CatalogGraphQl/Model/Output/AttributeMetadata.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,21 @@ public function execute(
6161
'used_in_product_listing' => $attribute->getUsedInProductListing() === "1",
6262
'is_wysiwyg_enabled' => $attribute->getIsWysiwygEnabled() === "1",
6363
'is_used_for_promo_rules' => $attribute->getIsUsedForPromoRules() === "1",
64-
'apply_to' => null,
64+
'apply_to' => null
6565
];
6666

6767
if (!empty($attribute->getApplyTo())) {
6868
$metadata['apply_to'] = array_map('strtoupper', $attribute->getApplyTo());
6969
}
7070

71+
if (!empty($attribute->getAdditionalData())) {
72+
$additionalData = json_decode($attribute->getAdditionalData(), true);
73+
$metadata = array_merge(
74+
$metadata,
75+
array_map('strtoupper', $additionalData)
76+
);
77+
}
78+
7179
return $metadata;
7280
}
7381
}

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,30 @@ type ColorSwatchData implements SwatchDataInterface {
5151
type ConfigurableProductOptionValue {
5252
swatch: SwatchDataInterface @resolver(class: "Magento\\SwatchesGraphQl\\Model\\Resolver\\Product\\Options\\SwatchData") @doc(description: "The URL assigned to the thumbnail of the swatch image.")
5353
}
54+
55+
type CatalogAttributeMetadata implements CustomAttributeMetadataInterface @doc(description: "Swatch attribute metadata.") {
56+
swatch_input_type: SwatchInputTypeEnum @doc(description: "Input type of the swatch attribute option.")
57+
update_product_preview_image: Boolean @doc(description: "Whether update product preview image or not.")
58+
use_product_image_for_swatch: Boolean @doc(description: "Whether use product image for swatch or not.")
59+
}
60+
61+
enum SwatchInputTypeEnum @doc(description: "Swatch attribute metadata input types.") {
62+
BOOLEAN
63+
DATE
64+
DATETIME
65+
DROPDOWN
66+
FILE
67+
GALLERY
68+
HIDDEN
69+
IMAGE
70+
MEDIA_IMAGE
71+
MULTILINE
72+
MULTISELECT
73+
PRICE
74+
SELECT
75+
TEXT
76+
TEXTAREA
77+
UNDEFINED
78+
VISUAL
79+
WEIGHT
80+
}
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
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\GraphQl\SwatchesGraphQl;
9+
10+
use Magento\Catalog\Api\Data\ProductAttributeInterface;
11+
use Magento\Catalog\Test\Fixture\Attribute;
12+
use Magento\EavGraphQl\Model\Uid;
13+
use Magento\TestFramework\Fixture\DataFixture;
14+
use Magento\TestFramework\Fixture\DataFixtureStorageManager;
15+
use Magento\TestFramework\Helper\Bootstrap;
16+
use Magento\TestFramework\TestCase\GraphQlAbstract;
17+
18+
/**
19+
* Test catalog EAV attributes metadata retrieval via GraphQL API
20+
*/
21+
#[
22+
DataFixture(
23+
Attribute::class,
24+
[
25+
'frontend_input' => 'multiselect',
26+
'is_filterable_in_search' => true,
27+
'position' => 6,
28+
'additional_data' =>
29+
'{"swatch_input_type":"visual","update_product_preview_image":1,"use_product_image_for_swatch":0}'
30+
],
31+
'product_attribute'
32+
),
33+
]
34+
class AttributesMetadataTest extends GraphQlAbstract
35+
{
36+
private const QUERY = <<<QRY
37+
{
38+
customAttributeMetadataV2(attributes: [{attribute_code: "%s", entity_type: "catalog_product"}]) {
39+
items {
40+
uid
41+
code
42+
label
43+
entity_type
44+
frontend_input
45+
is_required
46+
default_value
47+
is_unique
48+
...on CatalogAttributeMetadata {
49+
is_filterable_in_search
50+
is_searchable
51+
is_filterable
52+
is_comparable
53+
is_html_allowed_on_front
54+
is_used_for_price_rules
55+
is_wysiwyg_enabled
56+
is_used_for_promo_rules
57+
used_in_product_listing
58+
apply_to
59+
swatch_input_type
60+
update_product_preview_image
61+
use_product_image_for_swatch
62+
}
63+
}
64+
errors {
65+
type
66+
message
67+
}
68+
}
69+
}
70+
QRY;
71+
72+
/**
73+
* @return void
74+
* @throws \Exception
75+
*/
76+
public function testMetadataProduct(): void
77+
{
78+
/** @var ProductAttributeInterface $productAttribute */
79+
$productAttribute = DataFixtureStorageManager::getStorage()->get('product_attribute');
80+
81+
$productUid = Bootstrap::getObjectManager()->get(Uid::class)->encode(
82+
ProductAttributeInterface::ENTITY_TYPE_CODE,
83+
$productAttribute->getAttributeCode()
84+
);
85+
86+
$result = $this->graphQlQuery(
87+
sprintf(
88+
self::QUERY,
89+
$productAttribute->getAttributeCode()
90+
)
91+
);
92+
93+
$this->assertEquals(
94+
[
95+
'customAttributeMetadataV2' => [
96+
'items' => [
97+
[
98+
'uid' => $productUid,
99+
'code' => $productAttribute->getAttributeCode(),
100+
'label' => $productAttribute->getDefaultFrontendLabel(),
101+
'entity_type' => strtoupper(ProductAttributeInterface::ENTITY_TYPE_CODE),
102+
'frontend_input' => 'MULTISELECT',
103+
'is_required' => false,
104+
'default_value' => $productAttribute->getDefaultValue(),
105+
'is_unique' => false,
106+
'is_filterable_in_search' => true,
107+
'is_searchable' => false,
108+
'is_filterable' => false,
109+
'is_comparable' => false,
110+
'is_html_allowed_on_front' => true,
111+
'is_used_for_price_rules' => false,
112+
'is_wysiwyg_enabled' => false,
113+
'is_used_for_promo_rules' => false,
114+
'used_in_product_listing' => false,
115+
'apply_to' => null,
116+
'swatch_input_type' => 'VISUAL',
117+
'update_product_preview_image' => true,
118+
'use_product_image_for_swatch' => false
119+
]
120+
],
121+
'errors' => []
122+
]
123+
],
124+
$result
125+
);
126+
}
127+
}

0 commit comments

Comments
 (0)