Skip to content

Commit

Permalink
LYNX-109: Add custom_Attributes field to product (#125)
Browse files Browse the repository at this point in the history
  • Loading branch information
eliseacornejo authored Jun 27, 2023
1 parent 5b556f6 commit e9a93c6
Show file tree
Hide file tree
Showing 5 changed files with 596 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\CatalogGraphQl\Model\Resolver\Product;

use Magento\Catalog\Api\Data\ProductAttributeInterface;
use Magento\Catalog\Model\FilterProductCustomAttribute;
use Magento\Catalog\Model\Product;
use Magento\CatalogGraphQl\Model\ProductDataProvider;
use Magento\Eav\Api\Data\AttributeInterface;
use Magento\Eav\Model\AttributeRepository;
use Magento\EavGraphQl\Model\Output\Value\GetAttributeValueInterface;
use Magento\EavGraphQl\Model\Resolver\AttributeFilter;
use Magento\Framework\Api\SearchCriteriaBuilder;
use Magento\GraphQl\Model\Query\ContextInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Query\ResolverInterface;

/**
*
* Format a product's custom attribute information to conform to GraphQL schema representation
*/
class ProductCustomAttributes implements ResolverInterface
{
/**
* @var AttributeRepository
*/
private AttributeRepository $attributeRepository;

/**
* @var SearchCriteriaBuilder
*/
private SearchCriteriaBuilder $searchCriteriaBuilder;

/**
* @var GetAttributeValueInterface
*/
private GetAttributeValueInterface $getAttributeValue;

/**
* @var ProductDataProvider
*/
private ProductDataProvider $productDataProvider;

/**
* @var AttributeFilter
*/
private AttributeFilter $attributeFilter;

/**
* @var FilterProductCustomAttribute
*/
private FilterProductCustomAttribute $filterCustomAttribute;

/**
* @param AttributeRepository $attributeRepository
* @param SearchCriteriaBuilder $searchCriteriaBuilder
* @param GetAttributeValueInterface $getAttributeValue
* @param ProductDataProvider $productDataProvider
* @param AttributeFilter $attributeFilter
* @param FilterProductCustomAttribute $filterCustomAttribute
*/
public function __construct(
AttributeRepository $attributeRepository,
SearchCriteriaBuilder $searchCriteriaBuilder,
GetAttributeValueInterface $getAttributeValue,
ProductDataProvider $productDataProvider,
AttributeFilter $attributeFilter,
FilterProductCustomAttribute $filterCustomAttribute
) {
$this->attributeRepository = $attributeRepository;
$this->searchCriteriaBuilder = $searchCriteriaBuilder;
$this->getAttributeValue = $getAttributeValue;
$this->productDataProvider = $productDataProvider;
$this->attributeFilter = $attributeFilter;
$this->filterCustomAttribute = $filterCustomAttribute;
}

/**
* @inheritdoc
*
* @param Field $field
* @param ContextInterface $context
* @param ResolveInfo $info
* @param array|null $value
* @param array|null $args
* @throws \Exception
* @return array
*/
public function resolve(
Field $field,
$context,
ResolveInfo $info,
array $value = null,
array $args = null
) {
$filterArgs = $args['filter'] ?? [];

$searchCriteriaBuilder = $this->attributeFilter->execute($filterArgs, $this->searchCriteriaBuilder);

$searchCriteriaBuilder = $searchCriteriaBuilder
->addFilter('is_visible', true)
->addFilter('backend_type', 'static', 'neq')
->create();

$productCustomAttributes = $this->attributeRepository->getList(
ProductAttributeInterface::ENTITY_TYPE_CODE,
$searchCriteriaBuilder
)->getItems();

$attributeCodes = array_map(
function (AttributeInterface $customAttribute) {
return $customAttribute->getAttributeCode();
},
$productCustomAttributes
);

$filteredAttributeCodes = $this->filterCustomAttribute->execute(array_flip($attributeCodes));

/** @var Product $product */
$product = $value['model'];
$productData = $this->productDataProvider->getProductDataById((int)$product->getId());

$customAttributes = [];
foreach ($filteredAttributeCodes as $attributeCode => $value) {
if (!array_key_exists($attributeCode, $productData)) {
continue;
}
$attributeValue = $productData[$attributeCode];
if (is_array($attributeValue)) {
$attributeValue = implode(',', $attributeValue);
}
$customAttributes[] = [
'attribute_code' => $attributeCode,
'value' => $attributeValue
];
}

return array_map(
function (array $customAttribute) {
return $this->getAttributeValue->execute(
ProductAttributeInterface::ENTITY_TYPE_CODE,
$customAttribute['attribute_code'],
$customAttribute['value']
);
},
$customAttributes
);
}
}
20 changes: 20 additions & 0 deletions app/code/Magento/CatalogGraphQl/etc/schema.graphqls
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ interface ProductInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model\
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")
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")
media_gallery: [MediaGalleryInterface] @doc(description: "An array of media gallery objects.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\MediaGallery")
custom_attributes(filter: AttributeFilterInput): [AttributeValueInterface] @doc(description: "Product custom attributes.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductCustomAttributes")
}

interface PhysicalProductInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model\\ProductInterfaceTypeResolverComposite") @doc(description: "Contains attributes specific to tangible products.") {
Expand Down Expand Up @@ -560,3 +561,22 @@ enum CatalogAttributeApplyToEnum {
GROUPED
CATEGORY
}

input AttributeFilterInput @doc(description: "An input object that specifies the filters used for product.") {
is_comparable: Boolean @doc(description: "Whether a product or category attribute can be compared against another or not.")
is_filterable_in_search: Boolean @doc(description: "Whether a product or category attribute can be filtered in search or not.")
is_searchable: Boolean @doc(description: "Whether a product or category attribute can be searched or not.")
is_filterable: Boolean @doc(description: "Whether a product or category attribute can be filtered or not.")
is_html_allowed_on_front: Boolean @doc(description: "Whether a product or category attribute can use HTML on front or not.")
is_used_for_price_rules: Boolean @doc(description: "Whether a product or category attribute can be used for price rules or not.")
is_visible_in_advanced_search: Boolean @doc(description: "Whether a product or category attribute is visible in advanced search or not.")
is_wysiwyg_enabled: Boolean @doc(description: "Whether a product or category attribute has WYSIWYG enabled or not.")
is_used_for_promo_rules: Boolean @doc(description: "Whether a product or category attribute is used for promo rules or not.")
used_in_product_listing: Boolean @doc(description: "Whether a product or category attribute is used in product listing or not.")
is_visible_on_front: Boolean @doc(description: "Whether a product or category attribute is visible on front or not.")
used_for_sort_by: Boolean @doc(description: "Whether a product or category attribute is used for sort or not.")
is_required_in_admin_store: Boolean @doc(description: "Whether a product or category attribute is required in admin store or not.")
is_used_in_grid: Boolean @doc(description: "Whether a product or category attribute is used in grid or not.")
is_visible_in_grid: Boolean @doc(description: "Whether a product or category attribute is visible in grid or not.")
is_filterable_in_grid: Boolean @doc(description: "Whether a product or category attribute is filterable in grid or not.")
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public function execute(string $entity, string $code, string $value): ?array
continue;
}
$result[] = [
'uid' => $this->uid->encode($option->getValue()),
'uid' => $this->uid->encode((string)$option->getValue()),
'value' => $option->getValue(),
'label' => $option->getLabel()
];
Expand Down
32 changes: 32 additions & 0 deletions app/code/Magento/EavGraphQl/Model/Resolver/AttributeFilter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\EavGraphQl\Model\Resolver;

use Magento\Framework\Api\SearchCriteriaBuilder;

/**
* Creates a SearchCriteriaBuilder object from the provided arguments
*/
class AttributeFilter
{
/**
* Returns a SearchCriteriaBuilder object with filters from the passed args
*
* @param array $filterArgs
* @param SearchCriteriaBuilder $searchCriteriaBuilder
* @return SearchCriteriaBuilder SearchCriteriaBuilder
*/
public function execute(array $filterArgs, $searchCriteriaBuilder): SearchCriteriaBuilder
{
foreach ($filterArgs as $key => $value) {
$searchCriteriaBuilder->addFilter($key, $value);
}

return $searchCriteriaBuilder;
}
}
Loading

0 comments on commit e9a93c6

Please sign in to comment.