Skip to content

Commit

Permalink
Merge branch '2.4-develop' of https://github.com/mage-os/mirror-magento2
Browse files Browse the repository at this point in the history
 into 2.4-develop
  • Loading branch information
mage-os-ci committed Jun 30, 2023
2 parents 21ae694 + 2abc466 commit 43e3b26
Show file tree
Hide file tree
Showing 15 changed files with 1,090 additions and 5 deletions.
116 changes: 116 additions & 0 deletions app/code/Magento/Catalog/Test/Fixture/CategoryAttribute.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\Catalog\Test\Fixture;

use Magento\Catalog\Model\Category\Attribute;
use Magento\Eav\Api\AttributeRepositoryInterface;
use Magento\Eav\Model\AttributeFactory;
use Magento\Eav\Model\ResourceModel\Entity\Attribute as ResourceModelAttribute;
use Magento\Framework\DataObject;
use Magento\TestFramework\Fixture\Api\DataMerger;
use Magento\TestFramework\Fixture\Data\ProcessorInterface;
use Magento\TestFramework\Fixture\RevertibleDataFixtureInterface;

class CategoryAttribute implements RevertibleDataFixtureInterface
{
private const DEFAULT_DATA = [
'is_wysiwyg_enabled' => false,
'is_html_allowed_on_front' => true,
'used_for_sort_by' => false,
'is_filterable' => false,
'is_filterable_in_search' => false,
'is_used_in_grid' => true,
'is_visible_in_grid' => true,
'is_filterable_in_grid' => true,
'position' => 0,
'is_searchable' => '0',
'is_visible_in_advanced_search' => '0',
'is_comparable' => '0',
'is_used_for_promo_rules' => '0',
'is_visible_on_front' => '0',
'used_in_product_listing' => '0',
'is_visible' => true,
'scope' => 'store',
'attribute_code' => 'category_attribute%uniqid%',
'frontend_input' => 'text',
'entity_type_id' => '3',
'is_required' => false,
'is_user_defined' => true,
'default_frontend_label' => 'Category Attribute%uniqid%',
'backend_type' => 'varchar',
'is_unique' => '0',
'apply_to' => [],
];

/**
* @var DataMerger
*/
private DataMerger $dataMerger;

/**
* @var ProcessorInterface
*/
private ProcessorInterface $processor;

/**
* @var AttributeFactory
*/
private AttributeFactory $attributeFactory;

/**
* @var ResourceModelAttribute
*/
private ResourceModelAttribute $resourceModelAttribute;

/**
* @var AttributeRepositoryInterface
*/
private AttributeRepositoryInterface $attributeRepository;

/**
* @param DataMerger $dataMerger
* @param ProcessorInterface $processor
* @param AttributeRepositoryInterface $attributeRepository
* @param AttributeFactory $attributeFactory
* @param ResourceModelAttribute $resourceModelAttribute
*/
public function __construct(
DataMerger $dataMerger,
ProcessorInterface $processor,
AttributeRepositoryInterface $attributeRepository,
AttributeFactory $attributeFactory,
ResourceModelAttribute $resourceModelAttribute
) {
$this->dataMerger = $dataMerger;
$this->processor = $processor;
$this->attributeFactory = $attributeFactory;
$this->resourceModelAttribute = $resourceModelAttribute;
$this->attributeRepository = $attributeRepository;
}

/**
* @inheritdoc
*/
public function apply(array $data = []): ?DataObject
{
/** @var Attribute $attr */
$attr = $this->attributeFactory->createAttribute(Attribute::class, self::DEFAULT_DATA);
$mergedData = $this->processor->process($this, $this->dataMerger->merge(self::DEFAULT_DATA, $data));
$attr->setData($mergedData);
$this->resourceModelAttribute->save($attr);
return $attr;
}

/**
* @inheritdoc
*/
public function revert(DataObject $data): void
{
$this->attributeRepository->deleteById($data['attribute_id']);
}
}
73 changes: 73 additions & 0 deletions app/code/Magento/CatalogGraphQl/Model/Output/AttributeMetadata.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\CatalogGraphQl\Model\Output;

use Magento\Catalog\Model\Entity\Attribute;
use Magento\Eav\Api\Data\AttributeInterface;
use Magento\EavGraphQl\Model\Output\GetAttributeDataInterface;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Exception\NoSuchEntityException;

/**
* Format attributes metadata for GraphQL output
*/
class AttributeMetadata implements GetAttributeDataInterface
{
/**
* @var string
*/
private string $entityType;

/**
* @param string $entityType
*/
public function __construct(string $entityType)
{
$this->entityType = $entityType;
}

/**
* Retrieve formatted attribute data
*
* @param Attribute $attribute
* @param string $entityType
* @param int $storeId
* @return array
* @throws LocalizedException
* @throws NoSuchEntityException
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function execute(
AttributeInterface $attribute,
string $entityType,
int $storeId
): array {
if ($entityType !== $this->entityType) {
return [];
}

$metadata = [
'is_searchable' => $attribute->getIsSearchable() === "1",
'is_filterable' => $attribute->getIsFilterable() === "1",
'is_comparable' => $attribute->getIsComparable() === "1",
'is_html_allowed_on_front' => $attribute->getIsHtmlAllowedOnFront() === "1",
'is_used_for_price_rules' => $attribute->getIsUsedForPriceRules() === "1",
'is_filterable_in_search' => $attribute->getIsFilterableInSearch() === "1",
'used_in_product_listing' => $attribute->getUsedInProductListing() === "1",
'is_wysiwyg_enabled' => $attribute->getIsWysiwygEnabled() === "1",
'is_used_for_promo_rules' => $attribute->getIsUsedForPromoRules() === "1",
'apply_to' => null,
];

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

return $metadata;
}
}
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
);
}
}
22 changes: 21 additions & 1 deletion app/code/Magento/CatalogGraphQl/etc/graphql/di.xml
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,8 @@
<type name="Magento\EavGraphQl\Model\TypeResolver\AttributeMetadata">
<arguments>
<argument name="entityTypes" xsi:type="array">
<item name="PRODUCT" xsi:type="string">CatalogAttributeMetadata</item>
<item name="CATALOG_PRODUCT" xsi:type="string">CatalogAttributeMetadata</item>
<item name="CATALOG_CATEGORY" xsi:type="string">CatalogAttributeMetadata</item>
</argument>
</arguments>
</type>
Expand All @@ -220,8 +221,27 @@
<argument name="map" xsi:type="array">
<item name="AttributeEntityTypeEnum" xsi:type="array">
<item name="catalog_product" xsi:type="string">catalog_product</item>
<item name="catalog_category" xsi:type="string">catalog_category</item>
</item>
</argument>
</arguments>
</type>
<type name="Magento\EavGraphQl\Model\Output\GetAttributeDataComposite">
<arguments>
<argument name="providers" xsi:type="array">
<item name="catalog_product" xsi:type="object">GetCatalogProductAttributesMetadata</item>
<item name="catalog_category" xsi:type="object">GetCatalogCategoryAttributesMetadata</item>
</argument>
</arguments>
</type>
<virtualType name="GetCatalogProductAttributesMetadata" type="Magento\CatalogGraphQl\Model\Output\AttributeMetadata">
<arguments>
<argument name="entityType" xsi:type="string">catalog_product</argument>
</arguments>
</virtualType>
<virtualType name="GetCatalogCategoryAttributesMetadata" type="Magento\CatalogGraphQl\Model\Output\AttributeMetadata">
<arguments>
<argument name="entityType" xsi:type="string">catalog_category</argument>
</arguments>
</virtualType>
</config>
Loading

0 comments on commit 43e3b26

Please sign in to comment.