diff --git a/app/code/Magento/Catalog/Model/Category/AttributeRepository.php b/app/code/Magento/Catalog/Model/Category/AttributeRepository.php index 3243bf718e66..65443e223e85 100644 --- a/app/code/Magento/Catalog/Model/Category/AttributeRepository.php +++ b/app/code/Magento/Catalog/Model/Category/AttributeRepository.php @@ -29,6 +29,11 @@ class AttributeRepository implements CategoryAttributeRepositoryInterface */ private $eavConfig; + /** + * @var array + */ + private $metadataCache; + /** * @param \Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder * @param \Magento\Framework\Api\FilterBuilder $filterBuilder @@ -48,7 +53,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function getList(\Magento\Framework\Api\SearchCriteriaInterface $searchCriteria) { @@ -59,7 +64,7 @@ public function getList(\Magento\Framework\Api\SearchCriteriaInterface $searchCr } /** - * {@inheritdoc} + * @inheritdoc */ public function get($attributeCode) { @@ -70,23 +75,27 @@ public function get($attributeCode) } /** - * {@inheritdoc} + * @inheritdoc + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function getCustomAttributesMetadata($dataObjectClassName = null) { - $defaultAttributeSetId = $this->eavConfig - ->getEntityType(\Magento\Catalog\Api\Data\CategoryAttributeInterface::ENTITY_TYPE_CODE) - ->getDefaultAttributeSetId(); - $searchCriteria = $this->searchCriteriaBuilder->addFilters( - [ - $this->filterBuilder - ->setField('attribute_set_id') - ->setValue($defaultAttributeSetId) - ->create(), - ] - ); - - return $this->getList($searchCriteria->create())->getItems(); + if (!isset($this->metadataCache[$dataObjectClassName])) { + $defaultAttributeSetId = $this->eavConfig + ->getEntityType(\Magento\Catalog\Api\Data\CategoryAttributeInterface::ENTITY_TYPE_CODE) + ->getDefaultAttributeSetId(); + $searchCriteria = $this->searchCriteriaBuilder->addFilters( + [ + $this->filterBuilder + ->setField('attribute_set_id') + ->setValue($defaultAttributeSetId) + ->create(), + ] + ); + $this->metadataCache[$dataObjectClassName] = $this->getList($searchCriteria->create()) + ->getItems(); + } + return $this->metadataCache[$dataObjectClassName]; } } diff --git a/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilter.php b/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilter.php index 4350b6dd8526..d8b90b454b4a 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilter.php +++ b/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilter.php @@ -7,13 +7,11 @@ namespace Magento\CatalogGraphQl\Model\Category; -use Magento\Catalog\Api\CategoryRepositoryInterface; -use Magento\Catalog\Api\Data\CategorySearchResultsInterface; -use Magento\Catalog\Api\Data\CategorySearchResultsInterfaceFactory; use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory; use Magento\CatalogGraphQl\Model\Resolver\Categories\DataProvider\Category\CollectionProcessorInterface; use Magento\CatalogGraphQl\Model\Category\Filter\SearchCriteria; use Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface; +use Magento\Framework\DB\Select; use Magento\Framework\Exception\InputException; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\GraphQl\Model\Query\ContextInterface; @@ -39,16 +37,6 @@ class CategoryFilter */ private $extensionAttributesJoinProcessor; - /** - * @var CategorySearchResultsInterfaceFactory - */ - private $categorySearchResultsFactory; - - /** - * @var CategoryRepositoryInterface - */ - private $categoryRepository; - /** * @var SearchCriteria */ @@ -58,23 +46,17 @@ class CategoryFilter * @param CollectionFactory $categoryCollectionFactory * @param CollectionProcessorInterface $collectionProcessor * @param JoinProcessorInterface $extensionAttributesJoinProcessor - * @param CategorySearchResultsInterfaceFactory $categorySearchResultsFactory - * @param CategoryRepositoryInterface $categoryRepository * @param SearchCriteria $searchCriteria */ public function __construct( CollectionFactory $categoryCollectionFactory, CollectionProcessorInterface $collectionProcessor, JoinProcessorInterface $extensionAttributesJoinProcessor, - CategorySearchResultsInterfaceFactory $categorySearchResultsFactory, - CategoryRepositoryInterface $categoryRepository, SearchCriteria $searchCriteria ) { $this->categoryCollectionFactory = $categoryCollectionFactory; $this->collectionProcessor = $collectionProcessor; $this->extensionAttributesJoinProcessor = $extensionAttributesJoinProcessor; - $this->categorySearchResultsFactory = $categorySearchResultsFactory; - $this->categoryRepository = $categoryRepository; $this->searchCriteria = $searchCriteria; } @@ -95,22 +77,21 @@ public function getResult(array $criteria, StoreInterface $store, array $attribu $this->extensionAttributesJoinProcessor->process($collection); $this->collectionProcessor->process($collection, $searchCriteria, $attributeNames, $context); - /** @var CategorySearchResultsInterface $searchResult */ - $categories = $this->categorySearchResultsFactory->create(); - $categories->setSearchCriteria($searchCriteria); - $categories->setItems($collection->getItems()); - $categories->setTotalCount($collection->getSize()); + // only fetch necessary category entity id + $collection + ->getSelect() + ->reset(Select::COLUMNS) + ->columns( + 'e.entity_id' + ); - $categoryIds = []; - foreach ($categories->getItems() as $category) { - $categoryIds[] = (int)$category->getId(); - } + $categoryIds = $collection->load()->getLoadedIds(); $totalPages = 0; - if ($categories->getTotalCount() > 0 && $searchCriteria->getPageSize() > 0) { - $totalPages = ceil($categories->getTotalCount() / $searchCriteria->getPageSize()); + if ($collection->getSize() > 0 && $searchCriteria->getPageSize() > 0) { + $totalPages = ceil($collection->getSize() / $searchCriteria->getPageSize()); } - if ($searchCriteria->getCurrentPage() > $totalPages && $categories->getTotalCount() > 0) { + if ($searchCriteria->getCurrentPage() > $totalPages && $collection->getSize() > 0) { throw new GraphQlInputException( __( 'currentPage value %1 specified is greater than the %2 page(s) available.', @@ -121,7 +102,7 @@ public function getResult(array $criteria, StoreInterface $store, array $attribu return [ 'category_ids' => $categoryIds, - 'total_count' => $categories->getTotalCount(), + 'total_count' => $collection->getSize(), 'page_info' => [ 'total_pages' => $totalPages, 'page_size' => $searchCriteria->getPageSize(), diff --git a/app/code/Magento/CatalogGraphQl/Model/Category/Hydrator.php b/app/code/Magento/CatalogGraphQl/Model/Category/Hydrator.php index 675118b95310..fc234c1de4e4 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Category/Hydrator.php +++ b/app/code/Magento/CatalogGraphQl/Model/Category/Hydrator.php @@ -60,8 +60,12 @@ public function hydrateCategory(Category $category, $basicFieldsOnly = false) : if ($basicFieldsOnly) { $categoryData = $category->getData(); } else { - $categoryData = $this->dataObjectProcessor->buildOutputDataArray($category, CategoryInterface::class); + $categoryData = $this->dataObjectProcessor->buildOutputDataArray( + $category, + CategoryInterface::class + ); } + $categoryData['id'] = $category->getId(); $categoryData['uid'] = $this->uidEncoder->encode((string) $category->getId()); $categoryData['children'] = []; diff --git a/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml b/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml index 369f4bfb2303..0e0fa9d95580 100644 --- a/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml +++ b/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml @@ -191,6 +191,11 @@ + + + Magento\CatalogGraphQl\Category\DataObjectProcessor + + @@ -207,4 +212,16 @@ + + + + + getChildren + + + + diff --git a/lib/internal/Magento/Framework/Reflection/DataObjectProcessor.php b/lib/internal/Magento/Framework/Reflection/DataObjectProcessor.php index 967826be09ce..94023d129d1d 100644 --- a/lib/internal/Magento/Framework/Reflection/DataObjectProcessor.php +++ b/lib/internal/Magento/Framework/Reflection/DataObjectProcessor.php @@ -46,6 +46,11 @@ class DataObjectProcessor */ private $processors; + /** + * @var array[] + */ + private $excludedMethodsClassMap; + /** * @param MethodsMap $methodsMapProcessor * @param TypeCaster $typeCaster @@ -53,6 +58,7 @@ class DataObjectProcessor * @param CustomAttributesProcessor $customAttributesProcessor * @param ExtensionAttributesProcessor $extensionAttributesProcessor * @param array $processors + * @param array $excludedMethodsClassMap */ public function __construct( MethodsMap $methodsMapProcessor, @@ -60,7 +66,8 @@ public function __construct( FieldNamer $fieldNamer, CustomAttributesProcessor $customAttributesProcessor, ExtensionAttributesProcessor $extensionAttributesProcessor, - array $processors = [] + array $processors = [], + array $excludedMethodsClassMap = [] ) { $this->methodsMapProcessor = $methodsMapProcessor; $this->typeCaster = $typeCaster; @@ -68,6 +75,7 @@ public function __construct( $this->extensionAttributesProcessor = $extensionAttributesProcessor; $this->customAttributesProcessor = $customAttributesProcessor; $this->processors = $processors; + $this->excludedMethodsClassMap = $excludedMethodsClassMap; } /** @@ -84,7 +92,13 @@ public function buildOutputDataArray($dataObject, $dataObjectType) $methods = $this->methodsMapProcessor->getMethodsMap($dataObjectType); $outputData = []; + $excludedMethodsForDataObjectType = $this->excludedMethodsClassMap[$dataObjectType] ?? []; + foreach (array_keys($methods) as $methodName) { + if (in_array($methodName, $excludedMethodsForDataObjectType)) { + continue; + } + if (!$this->methodsMapProcessor->isMethodValidForDataField($dataObjectType, $methodName)) { continue; } diff --git a/lib/internal/Magento/Framework/Reflection/Test/Unit/DataObjectProcessorTest.php b/lib/internal/Magento/Framework/Reflection/Test/Unit/DataObjectProcessorTest.php index 8e55d4395d20..620b3684b4ab 100644 --- a/lib/internal/Magento/Framework/Reflection/Test/Unit/DataObjectProcessorTest.php +++ b/lib/internal/Magento/Framework/Reflection/Test/Unit/DataObjectProcessorTest.php @@ -26,6 +26,11 @@ class DataObjectProcessorTest extends TestCase */ private $dataObjectProcessor; + /** + * @var MethodsMap + */ + private $methodsMapProcessor; + /** * @var ExtensionAttributesProcessor|MockObject */ @@ -34,7 +39,7 @@ class DataObjectProcessorTest extends TestCase protected function setUp(): void { $objectManager = new ObjectManager($this); - $methodsMapProcessor = $objectManager->getObject( + $this->methodsMapProcessor = $objectManager->getObject( MethodsMap::class, [ 'fieldNamer' => $objectManager->getObject(FieldNamer::class), @@ -48,7 +53,7 @@ protected function setUp(): void ->willReturn(['unserializedData']); $objectManager->setBackwardCompatibleProperty( - $methodsMapProcessor, + $this->methodsMapProcessor, 'serializer', $serializerMock ); @@ -56,27 +61,32 @@ protected function setUp(): void $this->extensionAttributesProcessorMock = $this->getMockBuilder(ExtensionAttributesProcessor::class) ->disableOriginalConstructor() ->getMock(); + } + + /** + * @param array $extensionAttributes + * @param array $excludedMethodsClassMap + * @param array $expectedOutput + * @dataProvider buildOutputDataArrayDataProvider + */ + public function testBuildOutputDataArray( + array $extensionAttributes, + array $excludedMethodsClassMap, + array $expectedOutput + ) { + $objectManager = new ObjectManager($this); $this->dataObjectProcessor = $objectManager->getObject( DataObjectProcessor::class, [ - 'methodsMapProcessor' => $methodsMapProcessor, + 'methodsMapProcessor' => $this->methodsMapProcessor, 'typeCaster' => $objectManager->getObject(TypeCaster::class), 'fieldNamer' => $objectManager->getObject(FieldNamer::class), - 'extensionAttributesProcessor' => $this->extensionAttributesProcessorMock + 'extensionAttributesProcessor' => $this->extensionAttributesProcessorMock, + 'excludedMethodsClassMap' => $excludedMethodsClassMap, ] ); - } - /** - * @param array $extensionAttributes - * @param array $expectedOutputDataArray - * - * @dataProvider buildOutputDataArrayDataProvider - */ - public function testBuildOutputDataArray($extensionAttributes, $expectedOutputDataArray) - { - $objectManager = new ObjectManager($this); /** @var TestDataObject $testDataObject */ $testDataObject = $objectManager->getObject( TestDataObject::class, @@ -87,13 +97,19 @@ public function testBuildOutputDataArray($extensionAttributes, $expectedOutputDa ] ); - $this->extensionAttributesProcessorMock->expects($this->once()) + if (in_array('getExtensionAttributes', $excludedMethodsClassMap[TestDataInterface::class] ?? [])) { + $expectedTimes = $this->never(); + } else { + $expectedTimes = $this->once(); + } + + $this->extensionAttributesProcessorMock->expects($expectedTimes) ->method('buildOutputDataArray') ->willReturn($extensionAttributes); $outputData = $this->dataObjectProcessor ->buildOutputDataArray($testDataObject, TestDataInterface::class); - $this->assertEquals($expectedOutputDataArray, $outputData); + $this->assertEquals($expectedOutput, $outputData); } /** @@ -101,26 +117,50 @@ public function testBuildOutputDataArray($extensionAttributes, $expectedOutputDa */ public function buildOutputDataArrayDataProvider() { - $expectedOutputDataArray = [ + $expectedOutput = [ 'id' => '1', 'address' => 'someAddress', 'default_shipping' => 'true', 'required_billing' => 'false', ]; - $extensionAttributeArray = [ + + $extensionAttributes = [ 'attribute1' => 'value1', - 'attribute2' => 'value2' + 'attribute2' => 'value2', ]; return [ - 'No Attributes' => [[], $expectedOutputDataArray], - 'With Attributes' => [ - $extensionAttributeArray, + 'No Extension Attributes or Excluded Methods' => [ + [], + [], + $expectedOutput, + ], + 'With Extension Attributes' => [ + $extensionAttributes, + [], array_merge( - $expectedOutputDataArray, - ['extension_attributes' => $extensionAttributeArray] - ) - ] + $expectedOutput, + ['extension_attributes' => $extensionAttributes] + ), + ], + 'With Excluded Method' => [ + [], + [ + TestDataInterface::class => [ + 'getAddress', + ], + ], + array_diff_key($expectedOutput, array_flip(['address'])), + ], + 'With getExtensionAttributes as Excluded Method' => [ + $extensionAttributes, + [ + TestDataInterface::class => [ + 'getExtensionAttributes', + ], + ], + $expectedOutput, + ], ]; } }