Skip to content

Commit e9a93c6

Browse files
LYNX-109: Add custom_Attributes field to product (#125)
1 parent 5b556f6 commit e9a93c6

File tree

5 files changed

+596
-1
lines changed

5 files changed

+596
-1
lines changed
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/schema.graphqls

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ interface ProductInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model\
125125
categories: [CategoryInterface] @doc(description: "The categories assigned to a product.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Categories") @cache(cacheIdentity: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\CategoriesIdentity")
126126
canonical_url: String @doc(description: "The relative canonical URL. This value is returned only if the system setting 'Use Canonical Link Meta Tag For Products' is enabled.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\CanonicalUrl")
127127
media_gallery: [MediaGalleryInterface] @doc(description: "An array of media gallery objects.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\MediaGallery")
128+
custom_attributes(filter: AttributeFilterInput): [AttributeValueInterface] @doc(description: "Product custom attributes.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductCustomAttributes")
128129
}
129130

130131
interface PhysicalProductInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model\\ProductInterfaceTypeResolverComposite") @doc(description: "Contains attributes specific to tangible products.") {
@@ -560,3 +561,22 @@ enum CatalogAttributeApplyToEnum {
560561
GROUPED
561562
CATEGORY
562563
}
564+
565+
input AttributeFilterInput @doc(description: "An input object that specifies the filters used for product.") {
566+
is_comparable: Boolean @doc(description: "Whether a product or category attribute can be compared against another or not.")
567+
is_filterable_in_search: Boolean @doc(description: "Whether a product or category attribute can be filtered in search or not.")
568+
is_searchable: Boolean @doc(description: "Whether a product or category attribute can be searched or not.")
569+
is_filterable: Boolean @doc(description: "Whether a product or category attribute can be filtered or not.")
570+
is_html_allowed_on_front: Boolean @doc(description: "Whether a product or category attribute can use HTML on front or not.")
571+
is_used_for_price_rules: Boolean @doc(description: "Whether a product or category attribute can be used for price rules or not.")
572+
is_visible_in_advanced_search: Boolean @doc(description: "Whether a product or category attribute is visible in advanced search or not.")
573+
is_wysiwyg_enabled: Boolean @doc(description: "Whether a product or category attribute has WYSIWYG enabled or not.")
574+
is_used_for_promo_rules: Boolean @doc(description: "Whether a product or category attribute is used for promo rules or not.")
575+
used_in_product_listing: Boolean @doc(description: "Whether a product or category attribute is used in product listing or not.")
576+
is_visible_on_front: Boolean @doc(description: "Whether a product or category attribute is visible on front or not.")
577+
used_for_sort_by: Boolean @doc(description: "Whether a product or category attribute is used for sort or not.")
578+
is_required_in_admin_store: Boolean @doc(description: "Whether a product or category attribute is required in admin store or not.")
579+
is_used_in_grid: Boolean @doc(description: "Whether a product or category attribute is used in grid or not.")
580+
is_visible_in_grid: Boolean @doc(description: "Whether a product or category attribute is visible in grid or not.")
581+
is_filterable_in_grid: Boolean @doc(description: "Whether a product or category attribute is filterable in grid or not.")
582+
}

app/code/Magento/EavGraphQl/Model/Output/Value/Options/GetCustomSelectedOptionAttributes.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public function execute(string $entity, string $code, string $value): ?array
5151
continue;
5252
}
5353
$result[] = [
54-
'uid' => $this->uid->encode($option->getValue()),
54+
'uid' => $this->uid->encode((string)$option->getValue()),
5555
'value' => $option->getValue(),
5656
'label' => $option->getLabel()
5757
];
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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\Resolver;
9+
10+
use Magento\Framework\Api\SearchCriteriaBuilder;
11+
12+
/**
13+
* Creates a SearchCriteriaBuilder object from the provided arguments
14+
*/
15+
class AttributeFilter
16+
{
17+
/**
18+
* Returns a SearchCriteriaBuilder object with filters from the passed args
19+
*
20+
* @param array $filterArgs
21+
* @param SearchCriteriaBuilder $searchCriteriaBuilder
22+
* @return SearchCriteriaBuilder SearchCriteriaBuilder
23+
*/
24+
public function execute(array $filterArgs, $searchCriteriaBuilder): SearchCriteriaBuilder
25+
{
26+
foreach ($filterArgs as $key => $value) {
27+
$searchCriteriaBuilder->addFilter($key, $value);
28+
}
29+
30+
return $searchCriteriaBuilder;
31+
}
32+
}

0 commit comments

Comments
 (0)