Skip to content

Commit 7780091

Browse files
committed
Merge branch '2.4-develop' of https://github.com/magento-commerce/magento2ce into PR-05242023
2 parents f2b5a85 + 2abc466 commit 7780091

File tree

15 files changed

+1090
-5
lines changed

15 files changed

+1090
-5
lines changed
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
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\Catalog\Test\Fixture;
9+
10+
use Magento\Catalog\Model\Category\Attribute;
11+
use Magento\Eav\Api\AttributeRepositoryInterface;
12+
use Magento\Eav\Model\AttributeFactory;
13+
use Magento\Eav\Model\ResourceModel\Entity\Attribute as ResourceModelAttribute;
14+
use Magento\Framework\DataObject;
15+
use Magento\TestFramework\Fixture\Api\DataMerger;
16+
use Magento\TestFramework\Fixture\Data\ProcessorInterface;
17+
use Magento\TestFramework\Fixture\RevertibleDataFixtureInterface;
18+
19+
class CategoryAttribute implements RevertibleDataFixtureInterface
20+
{
21+
private const DEFAULT_DATA = [
22+
'is_wysiwyg_enabled' => false,
23+
'is_html_allowed_on_front' => true,
24+
'used_for_sort_by' => false,
25+
'is_filterable' => false,
26+
'is_filterable_in_search' => false,
27+
'is_used_in_grid' => true,
28+
'is_visible_in_grid' => true,
29+
'is_filterable_in_grid' => true,
30+
'position' => 0,
31+
'is_searchable' => '0',
32+
'is_visible_in_advanced_search' => '0',
33+
'is_comparable' => '0',
34+
'is_used_for_promo_rules' => '0',
35+
'is_visible_on_front' => '0',
36+
'used_in_product_listing' => '0',
37+
'is_visible' => true,
38+
'scope' => 'store',
39+
'attribute_code' => 'category_attribute%uniqid%',
40+
'frontend_input' => 'text',
41+
'entity_type_id' => '3',
42+
'is_required' => false,
43+
'is_user_defined' => true,
44+
'default_frontend_label' => 'Category Attribute%uniqid%',
45+
'backend_type' => 'varchar',
46+
'is_unique' => '0',
47+
'apply_to' => [],
48+
];
49+
50+
/**
51+
* @var DataMerger
52+
*/
53+
private DataMerger $dataMerger;
54+
55+
/**
56+
* @var ProcessorInterface
57+
*/
58+
private ProcessorInterface $processor;
59+
60+
/**
61+
* @var AttributeFactory
62+
*/
63+
private AttributeFactory $attributeFactory;
64+
65+
/**
66+
* @var ResourceModelAttribute
67+
*/
68+
private ResourceModelAttribute $resourceModelAttribute;
69+
70+
/**
71+
* @var AttributeRepositoryInterface
72+
*/
73+
private AttributeRepositoryInterface $attributeRepository;
74+
75+
/**
76+
* @param DataMerger $dataMerger
77+
* @param ProcessorInterface $processor
78+
* @param AttributeRepositoryInterface $attributeRepository
79+
* @param AttributeFactory $attributeFactory
80+
* @param ResourceModelAttribute $resourceModelAttribute
81+
*/
82+
public function __construct(
83+
DataMerger $dataMerger,
84+
ProcessorInterface $processor,
85+
AttributeRepositoryInterface $attributeRepository,
86+
AttributeFactory $attributeFactory,
87+
ResourceModelAttribute $resourceModelAttribute
88+
) {
89+
$this->dataMerger = $dataMerger;
90+
$this->processor = $processor;
91+
$this->attributeFactory = $attributeFactory;
92+
$this->resourceModelAttribute = $resourceModelAttribute;
93+
$this->attributeRepository = $attributeRepository;
94+
}
95+
96+
/**
97+
* @inheritdoc
98+
*/
99+
public function apply(array $data = []): ?DataObject
100+
{
101+
/** @var Attribute $attr */
102+
$attr = $this->attributeFactory->createAttribute(Attribute::class, self::DEFAULT_DATA);
103+
$mergedData = $this->processor->process($this, $this->dataMerger->merge(self::DEFAULT_DATA, $data));
104+
$attr->setData($mergedData);
105+
$this->resourceModelAttribute->save($attr);
106+
return $attr;
107+
}
108+
109+
/**
110+
* @inheritdoc
111+
*/
112+
public function revert(DataObject $data): void
113+
{
114+
$this->attributeRepository->deleteById($data['attribute_id']);
115+
}
116+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
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\CatalogGraphQl\Model\Output;
9+
10+
use Magento\Catalog\Model\Entity\Attribute;
11+
use Magento\Eav\Api\Data\AttributeInterface;
12+
use Magento\EavGraphQl\Model\Output\GetAttributeDataInterface;
13+
use Magento\Framework\Exception\LocalizedException;
14+
use Magento\Framework\Exception\NoSuchEntityException;
15+
16+
/**
17+
* Format attributes metadata for GraphQL output
18+
*/
19+
class AttributeMetadata implements GetAttributeDataInterface
20+
{
21+
/**
22+
* @var string
23+
*/
24+
private string $entityType;
25+
26+
/**
27+
* @param string $entityType
28+
*/
29+
public function __construct(string $entityType)
30+
{
31+
$this->entityType = $entityType;
32+
}
33+
34+
/**
35+
* Retrieve formatted attribute data
36+
*
37+
* @param Attribute $attribute
38+
* @param string $entityType
39+
* @param int $storeId
40+
* @return array
41+
* @throws LocalizedException
42+
* @throws NoSuchEntityException
43+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
44+
*/
45+
public function execute(
46+
AttributeInterface $attribute,
47+
string $entityType,
48+
int $storeId
49+
): array {
50+
if ($entityType !== $this->entityType) {
51+
return [];
52+
}
53+
54+
$metadata = [
55+
'is_searchable' => $attribute->getIsSearchable() === "1",
56+
'is_filterable' => $attribute->getIsFilterable() === "1",
57+
'is_comparable' => $attribute->getIsComparable() === "1",
58+
'is_html_allowed_on_front' => $attribute->getIsHtmlAllowedOnFront() === "1",
59+
'is_used_for_price_rules' => $attribute->getIsUsedForPriceRules() === "1",
60+
'is_filterable_in_search' => $attribute->getIsFilterableInSearch() === "1",
61+
'used_in_product_listing' => $attribute->getUsedInProductListing() === "1",
62+
'is_wysiwyg_enabled' => $attribute->getIsWysiwygEnabled() === "1",
63+
'is_used_for_promo_rules' => $attribute->getIsUsedForPromoRules() === "1",
64+
'apply_to' => null,
65+
];
66+
67+
if (!empty($attribute->getApplyTo())) {
68+
$metadata['apply_to'] = array_map('strtoupper', $attribute->getApplyTo());
69+
}
70+
71+
return $metadata;
72+
}
73+
}
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
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\CatalogGraphQl\Model\Resolver\Product;
9+
10+
use Magento\Catalog\Api\Data\ProductAttributeInterface;
11+
use Magento\Catalog\Model\FilterProductCustomAttribute;
12+
use Magento\Catalog\Model\Product;
13+
use Magento\CatalogGraphQl\Model\ProductDataProvider;
14+
use Magento\Eav\Api\Data\AttributeInterface;
15+
use Magento\Eav\Model\AttributeRepository;
16+
use Magento\EavGraphQl\Model\Output\Value\GetAttributeValueInterface;
17+
use Magento\EavGraphQl\Model\Resolver\AttributeFilter;
18+
use Magento\Framework\Api\SearchCriteriaBuilder;
19+
use Magento\GraphQl\Model\Query\ContextInterface;
20+
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
21+
use Magento\Framework\GraphQl\Config\Element\Field;
22+
use Magento\Framework\GraphQl\Query\ResolverInterface;
23+
24+
/**
25+
*
26+
* Format a product's custom attribute information to conform to GraphQL schema representation
27+
*/
28+
class ProductCustomAttributes implements ResolverInterface
29+
{
30+
/**
31+
* @var AttributeRepository
32+
*/
33+
private AttributeRepository $attributeRepository;
34+
35+
/**
36+
* @var SearchCriteriaBuilder
37+
*/
38+
private SearchCriteriaBuilder $searchCriteriaBuilder;
39+
40+
/**
41+
* @var GetAttributeValueInterface
42+
*/
43+
private GetAttributeValueInterface $getAttributeValue;
44+
45+
/**
46+
* @var ProductDataProvider
47+
*/
48+
private ProductDataProvider $productDataProvider;
49+
50+
/**
51+
* @var AttributeFilter
52+
*/
53+
private AttributeFilter $attributeFilter;
54+
55+
/**
56+
* @var FilterProductCustomAttribute
57+
*/
58+
private FilterProductCustomAttribute $filterCustomAttribute;
59+
60+
/**
61+
* @param AttributeRepository $attributeRepository
62+
* @param SearchCriteriaBuilder $searchCriteriaBuilder
63+
* @param GetAttributeValueInterface $getAttributeValue
64+
* @param ProductDataProvider $productDataProvider
65+
* @param AttributeFilter $attributeFilter
66+
* @param FilterProductCustomAttribute $filterCustomAttribute
67+
*/
68+
public function __construct(
69+
AttributeRepository $attributeRepository,
70+
SearchCriteriaBuilder $searchCriteriaBuilder,
71+
GetAttributeValueInterface $getAttributeValue,
72+
ProductDataProvider $productDataProvider,
73+
AttributeFilter $attributeFilter,
74+
FilterProductCustomAttribute $filterCustomAttribute
75+
) {
76+
$this->attributeRepository = $attributeRepository;
77+
$this->searchCriteriaBuilder = $searchCriteriaBuilder;
78+
$this->getAttributeValue = $getAttributeValue;
79+
$this->productDataProvider = $productDataProvider;
80+
$this->attributeFilter = $attributeFilter;
81+
$this->filterCustomAttribute = $filterCustomAttribute;
82+
}
83+
84+
/**
85+
* @inheritdoc
86+
*
87+
* @param Field $field
88+
* @param ContextInterface $context
89+
* @param ResolveInfo $info
90+
* @param array|null $value
91+
* @param array|null $args
92+
* @throws \Exception
93+
* @return array
94+
*/
95+
public function resolve(
96+
Field $field,
97+
$context,
98+
ResolveInfo $info,
99+
array $value = null,
100+
array $args = null
101+
) {
102+
$filterArgs = $args['filter'] ?? [];
103+
104+
$searchCriteriaBuilder = $this->attributeFilter->execute($filterArgs, $this->searchCriteriaBuilder);
105+
106+
$searchCriteriaBuilder = $searchCriteriaBuilder
107+
->addFilter('is_visible', true)
108+
->addFilter('backend_type', 'static', 'neq')
109+
->create();
110+
111+
$productCustomAttributes = $this->attributeRepository->getList(
112+
ProductAttributeInterface::ENTITY_TYPE_CODE,
113+
$searchCriteriaBuilder
114+
)->getItems();
115+
116+
$attributeCodes = array_map(
117+
function (AttributeInterface $customAttribute) {
118+
return $customAttribute->getAttributeCode();
119+
},
120+
$productCustomAttributes
121+
);
122+
123+
$filteredAttributeCodes = $this->filterCustomAttribute->execute(array_flip($attributeCodes));
124+
125+
/** @var Product $product */
126+
$product = $value['model'];
127+
$productData = $this->productDataProvider->getProductDataById((int)$product->getId());
128+
129+
$customAttributes = [];
130+
foreach ($filteredAttributeCodes as $attributeCode => $value) {
131+
if (!array_key_exists($attributeCode, $productData)) {
132+
continue;
133+
}
134+
$attributeValue = $productData[$attributeCode];
135+
if (is_array($attributeValue)) {
136+
$attributeValue = implode(',', $attributeValue);
137+
}
138+
$customAttributes[] = [
139+
'attribute_code' => $attributeCode,
140+
'value' => $attributeValue
141+
];
142+
}
143+
144+
return array_map(
145+
function (array $customAttribute) {
146+
return $this->getAttributeValue->execute(
147+
ProductAttributeInterface::ENTITY_TYPE_CODE,
148+
$customAttribute['attribute_code'],
149+
$customAttribute['value']
150+
);
151+
},
152+
$customAttributes
153+
);
154+
}
155+
}

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

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,8 @@
211211
<type name="Magento\EavGraphQl\Model\TypeResolver\AttributeMetadata">
212212
<arguments>
213213
<argument name="entityTypes" xsi:type="array">
214-
<item name="PRODUCT" xsi:type="string">CatalogAttributeMetadata</item>
214+
<item name="CATALOG_PRODUCT" xsi:type="string">CatalogAttributeMetadata</item>
215+
<item name="CATALOG_CATEGORY" xsi:type="string">CatalogAttributeMetadata</item>
215216
</argument>
216217
</arguments>
217218
</type>
@@ -220,8 +221,27 @@
220221
<argument name="map" xsi:type="array">
221222
<item name="AttributeEntityTypeEnum" xsi:type="array">
222223
<item name="catalog_product" xsi:type="string">catalog_product</item>
224+
<item name="catalog_category" xsi:type="string">catalog_category</item>
223225
</item>
224226
</argument>
225227
</arguments>
226228
</type>
229+
<type name="Magento\EavGraphQl\Model\Output\GetAttributeDataComposite">
230+
<arguments>
231+
<argument name="providers" xsi:type="array">
232+
<item name="catalog_product" xsi:type="object">GetCatalogProductAttributesMetadata</item>
233+
<item name="catalog_category" xsi:type="object">GetCatalogCategoryAttributesMetadata</item>
234+
</argument>
235+
</arguments>
236+
</type>
237+
<virtualType name="GetCatalogProductAttributesMetadata" type="Magento\CatalogGraphQl\Model\Output\AttributeMetadata">
238+
<arguments>
239+
<argument name="entityType" xsi:type="string">catalog_product</argument>
240+
</arguments>
241+
</virtualType>
242+
<virtualType name="GetCatalogCategoryAttributesMetadata" type="Magento\CatalogGraphQl\Model\Output\AttributeMetadata">
243+
<arguments>
244+
<argument name="entityType" xsi:type="string">catalog_category</argument>
245+
</arguments>
246+
</virtualType>
227247
</config>

0 commit comments

Comments
 (0)