From 9d298d40f69ff2c953429b4c8e5a2373716e16e4 Mon Sep 17 00:00:00 2001 From: Alfreds Genkins Date: Wed, 23 Oct 2019 08:25:43 +0300 Subject: [PATCH] Stop loading products all over again each time we request attributes. (#24) * Reworked attribbutes and rating fields as they were hyper ineeficent each loading data on its own. * Fixed PHPDOC * Fixed entries getting lost in schema * Reworked to use countinue * Fied CR --- src/Model/Resolver/AttributesWithValue.php | 43 +++++----- .../Products/DataProvider/Product.php | 26 +++++- .../AttributeProcessor.php | 85 +++++++++++++++++++ src/etc/di.xml | 6 +- 4 files changed, 136 insertions(+), 24 deletions(-) create mode 100644 src/Model/Resolver/Products/DataProvider/Product/CollectionProcessor/AttributeProcessor.php diff --git a/src/Model/Resolver/AttributesWithValue.php b/src/Model/Resolver/AttributesWithValue.php index 285a36a..0799995 100644 --- a/src/Model/Resolver/AttributesWithValue.php +++ b/src/Model/Resolver/AttributesWithValue.php @@ -47,10 +47,22 @@ public function __construct( $this->productRepository = $productRepository; } + protected function getAttributeOptions($attr, $rawOptions) { + if (!$this->swatchHelper->isSwatchAttribute($attr)) return []; + + $optionIds = array_map(function ($option) { return $option['value']; }, $rawOptions); + $swatchOptions = $this->swatchHelper->getSwatchesByOptionsId($optionIds); + + return array_map(function ($option) use ($swatchOptions) { + $option['swatch_data'] = $swatchOptions[$option['value']] ?? []; + return $option; + }, $rawOptions); + } + /** * Fetches the data from persistence models and format it according to the GraphQL schema. * - * @param \Magento\Framework\GraphQl\Config\Element\Field $field + * @param Field $field * @param ContextInterface $context * @param ResolveInfo $info * @param array|null $value @@ -65,30 +77,21 @@ public function resolve( array $value = null, array $args = null ) { - $product = $this->productRepository->getById($value['entity_id']); - $attributes = $product->getAttributes(); + $product = $value['model']; $attributesToReturn = []; - foreach ($attributes as $attribute) { - if ($attribute->getIsVisibleOnFront()) { - $productAttribute = $product->getCustomAttribute($attribute->getAttributeCode()); - - $rawOptions = $attribute->getSource()->getAllOptions(true, true); + foreach ($product->getAttributes() as $attr) { + if ($attr->getIsVisibleOnFront()) { + $rawOptions = $attr->getSource()->getAllOptions(true, true); array_shift($rawOptions); - $optionIds = array_map(function ($option) { return $option['value']; }, $rawOptions); - $swatchOptions = $this->swatchHelper->getSwatchesByOptionsId($optionIds); - $attributesToReturn[] = [ - 'attribute_value' => $productAttribute ? $productAttribute->getValue() : null, - 'attribute_code' => $attribute->getAttributeCode(), - 'attribute_type' => $attribute->getFrontendInput(), - 'attribute_label' => $attribute->getFrontendLabel(), - 'attribute_id' => $attribute->getAttributeId(), - 'attribute_options' => array_map(function ($option) use ($swatchOptions) { - $option['swatch_data'] = $swatchOptions[$option['value']] ?? []; - return $option; - }, $rawOptions) + 'attribute_value' => $attr ? $attr->getValue() : null, + 'attribute_code' => $attr->getAttributeCode(), + 'attribute_type' => $attr->getFrontendInput(), + 'attribute_label' => $attr->getFrontendLabel(), + 'attribute_id' => $attr->getAttributeId(), + 'attribute_options' => $this->getAttributeOptions($attr, $rawOptions) ]; } } diff --git a/src/Model/Resolver/Products/DataProvider/Product.php b/src/Model/Resolver/Products/DataProvider/Product.php index cb9ab43..2faffcb 100644 --- a/src/Model/Resolver/Products/DataProvider/Product.php +++ b/src/Model/Resolver/Products/DataProvider/Product.php @@ -12,12 +12,16 @@ namespace ScandiPWA\CatalogGraphQl\Model\Resolver\Products\DataProvider; use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ResourceModel\Product\Collection; use Magento\Framework\Api\SearchCriteriaInterface; use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; use Magento\Catalog\Api\Data\ProductSearchResultsInterfaceFactory; use Magento\Framework\Api\SearchResultsInterface; use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Product\CollectionProcessorInterface; +use Magento\Framework\Exception\LocalizedException; use ScandiPWA\CatalogGraphQl\Model\Resolver\Products\DataProvider\Product\CriteriaCheck; +use Magento\Review\Model\Review; +use Magento\Review\Model\ResourceModel\Review\Product\Collection as ProductCollection; /** * Product field data provider, used for GraphQL resolver processing. @@ -56,17 +60,26 @@ class Product extends \Magento\CatalogGraphQl\Model\Resolver\Products\DataProvid private $maxPrice; /** + * @var Review + */ + protected $review; + + /** + * Product constructor. * @param CollectionFactory $collectionFactory * @param ProductSearchResultsInterfaceFactory $searchResultsFactory * @param Visibility $visibility * @param CollectionProcessorInterface $collectionProcessor + * @param Review $review */ public function __construct( CollectionFactory $collectionFactory, ProductSearchResultsInterfaceFactory $searchResultsFactory, Visibility $visibility, - CollectionProcessorInterface $collectionProcessor + CollectionProcessorInterface $collectionProcessor, + Review $review ) { + $this->review = $review; $this->collectionFactory = $collectionFactory; $this->searchResultsFactory = $searchResultsFactory; $this->visibility = $visibility; @@ -81,6 +94,7 @@ public function __construct( * @param bool $isSearch * @param bool $isChildSearch * @return SearchResultsInterface + * @throws LocalizedException */ public function getList( SearchCriteriaInterface $searchCriteria, @@ -88,7 +102,7 @@ public function getList( bool $isSearch = false, bool $isChildSearch = false ): SearchResultsInterface { - /** @var \Magento\Catalog\Model\ResourceModel\Product\Collection $collection */ + /** @var Collection $collection */ $collection = $this->collectionFactory->create(); $this->collectionProcessor->process($collection, $searchCriteria, $attributes); @@ -115,6 +129,12 @@ public function getList( $collection->addOptionsToResult(); } + if (in_array('review_summary', $attributes)) { + /** @var ProductCollection $collection */ + // Only getItems is used inside + $this->review->appendSummary($collection); + } + $searchResult = $this->searchResultsFactory->create(); $searchResult->setSearchCriteria($searchCriteria); $searchResult->setItems($collection->getItems()); @@ -126,7 +146,7 @@ public function getList( /** - * @param \Magento\Catalog\Model\ResourceModel\Product\Collection $collection + * @param Collection $collection * @return array */ public function getCollectionMinMaxPrice($collection) diff --git a/src/Model/Resolver/Products/DataProvider/Product/CollectionProcessor/AttributeProcessor.php b/src/Model/Resolver/Products/DataProvider/Product/CollectionProcessor/AttributeProcessor.php new file mode 100644 index 0000000..d92be86 --- /dev/null +++ b/src/Model/Resolver/Products/DataProvider/Product/CollectionProcessor/AttributeProcessor.php @@ -0,0 +1,85 @@ +collectionFactory = $collectionFactory; + $this->storeManager = $storeManager; + } + + protected function getAttributesVisibleOnFrontend() { + $collection = $this->collectionFactory->create(); + $collection->setItemObjectClass(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class) + ->addStoreLabel($this->storeManager->getStore()->getId()) + ->setOrder('position', 'ASC'); + + // Add filter by storefront visibility + $collection->addFieldToFilter('additional_table.is_visible_on_front', ['gt' => 0]); + return $collection->load(); + } + + /** + * {@inheritdoc} + */ + public function process( + Collection $collection, + SearchCriteriaInterface $searchCriteria, + array $attributeNames + ): Collection { + foreach ($attributeNames as $name) { + if ($name !== self::ATTRIBUTES_FIELD) { + $collection->addAttributeToSelect($name); + continue; + } + + $attributesVisibleOnFront = $this->getAttributesVisibleOnFrontend(); + + $attributeCodes = array_map(function($attr) { + return $attr->getAttributeCode(); + }, $attributesVisibleOnFront->getItems()); + + $collection->addAttributeToSelect($attributeCodes); + } + + return $collection; + } +} diff --git a/src/etc/di.xml b/src/etc/di.xml index 8226786..462fb9d 100644 --- a/src/etc/di.xml +++ b/src/etc/di.xml @@ -15,7 +15,11 @@ + type="ScandiPWA\CatalogGraphQl\Model\Resolver\Products\DataProvider\Product\CollectionProcessor\StockProcessor" /> + + +