From ffeb8f41d4dff47ba94a9017f820ca005ccd98cd Mon Sep 17 00:00:00 2001 From: Alfreds Genkins Date: Mon, 30 Dec 2019 19:31:12 +0200 Subject: [PATCH 1/2] WIP - product load optimization. --- src/Model/Resolver/GetCartForCustomer.php | 110 +++++++++++++--------- 1 file changed, 67 insertions(+), 43 deletions(-) diff --git a/src/Model/Resolver/GetCartForCustomer.php b/src/Model/Resolver/GetCartForCustomer.php index f98c792..38f3b21 100644 --- a/src/Model/Resolver/GetCartForCustomer.php +++ b/src/Model/Resolver/GetCartForCustomer.php @@ -14,20 +14,20 @@ namespace ScandiPWA\QuoteGraphQl\Model\Resolver; +use Magento\Catalog\Model\Product; use Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable; use Magento\Catalog\Model\ProductFactory; - use Magento\Framework\Exception\NotFoundException; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; use Magento\Framework\GraphQl\Query\Resolver\Value; -use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Quote\Api\CartManagementInterface; use Magento\Quote\Api\Data\CartInterface; use Magento\Quote\Api\GuestCartRepositoryInterface; -use Magento\Quote\Model\QuoteManagement; +use Magento\Quote\Model\Quote\Item as QuoteItem; use Magento\Webapi\Controller\Rest\ParamOverriderCustomerId; +use ScandiPWA\Performance\Model\Resolver\ProductPostProcessor; class GetCartForCustomer extends CartResolver { @@ -41,6 +41,16 @@ class GetCartForCustomer extends CartResolver */ protected $productFactory; + /** + * @var ProductPostProcessor + */ + protected $productPostProcessor; + + /** + * @var array + */ + protected $productsData; + /** * GetCartForCustomer constructor. * @param ParamOverriderCustomerId $overriderCustomerId @@ -48,18 +58,37 @@ class GetCartForCustomer extends CartResolver * @param GuestCartRepositoryInterface $guestCartRepository * @param Configurable $configurable * @param ProductFactory $productFactory + * @param ProductPostProcessor $productPostProcessor */ public function __construct( ParamOverriderCustomerId $overriderCustomerId, CartManagementInterface $quoteManagement, GuestCartRepositoryInterface $guestCartRepository, Configurable $configurable, - ProductFactory $productFactory - ) - { - parent::__construct($guestCartRepository, $overriderCustomerId, $quoteManagement); + ProductFactory $productFactory, + ProductPostProcessor $productPostProcessor + ) { + parent::__construct( + $guestCartRepository, + $overriderCustomerId, + $quoteManagement + ); + $this->configurable = $configurable; $this->productFactory = $productFactory; + $this->productPostProcessor = $productPostProcessor; + } + + /** + * @param QuoteItem $item + * @param Product $product + * @return array + */ + protected function mergeQuoteItemData( + QuoteItem $item, + Product $product + ) { + return $item->getData() + ['product' => $this->productsData[$product->getId()]]; } /** @@ -79,36 +108,35 @@ public function resolve( ResolveInfo $info, array $value = null, array $args = null - ) - { + ) { $cart = $this->getCart($args); - + $items = $cart->getItems(); $itemsData = []; - foreach ($cart->getItems() as $item) { - $product = $item->getProduct(); - $parentIds = $this->configurable->getParentIdsByChild($product->getId()); - if (count($parentIds)) { - $parentProduct = $this->productFactory->create()->load(reset($parentIds)); - $itemsData[] = array_merge( - $item->getData(), - ['product' => - array_merge( - $parentProduct->getData(), - ['model' => $parentProduct] - ) - ] - ); - } else { - $itemsData[] = array_merge( - $item->getData(), - ['product' => - array_merge( - $product->getData(), - ['model' => $product] - ) - ] - ); + if ($items) { + // Prepare product data in advance + $products = array_map(function ($item) { + return $item->getProduct(); + }, $items); + + $adjustedInfo = $info->fieldNodes[0]; + $this->productsData = $this->productPostProcessor->process( + $products, + 'items/product', + $adjustedInfo + ); + + foreach ($items as $item) { + /** @var QuoteItem $item */ + $product = $item->getProduct(); + $parentIds = $this->configurable->getParentIdsByChild($product->getId()); + + if (count($parentIds)) { + $parentProduct = $this->productFactory->create()->load(reset($parentIds)); + $itemsData[] = $this->mergeQuoteItemData($item, $parentProduct); + } else { + $itemsData[] = $this->mergeQuoteItemData($item, $product); + } } } @@ -116,18 +144,14 @@ public function resolve( $tax_amount = $address->getTaxAmount(); $discount_amount = $address->getDiscountAmount(); - return array_merge( - $cart->getData(), + return $cart->getData() + [ 'items' => $itemsData, 'tax_amount' => $tax_amount, 'discount_amount' => $discount_amount, - /** - * In interface it is PHPDocumented that it returns bool, - * while in implementation it returns int. - */ - 'is_virtual' => (bool)$cart->getIsVirtual() - ] - ); + // In interface it is PHPDocumented that it returns bool, + // while in implementation it returns int. + 'is_virtual' => (bool) $cart->getIsVirtual() + ]; } } From d4c24c4d765d8fe09f24187f9ec3462eda682f04 Mon Sep 17 00:00:00 2001 From: Alfreds Genkins Date: Mon, 6 Jan 2020 15:37:06 +0200 Subject: [PATCH 2/2] Changed order processing logic, fixed product resolver. --- composer.json | 59 +++++++-------- src/Model/Resolver/ExpandedOrderResolver.php | 10 +-- src/Model/Resolver/GetCartForCustomer.php | 17 +++-- src/Model/Resolver/ProductResolver.php | 78 +++++++++++++++++--- 4 files changed, 112 insertions(+), 52 deletions(-) diff --git a/composer.json b/composer.json index 52076f2..4d7f21e 100644 --- a/composer.json +++ b/composer.json @@ -1,34 +1,35 @@ { - "name": "scandipwa/quote-graphql", - "description": "N/A", - "type": "magento2-module", - "require": { - "magento/magento2-base": "^2.3.2" + "name": "scandipwa/quote-graphql", + "description": "N/A", + "type": "magento2-module", + "require": { + "magento/magento2-base": "^2.3.2", + "scandipwa/performance": "^1.0" + }, + "support": { + "source": "https://github.com/scandipwa/quote-graphql", + "email": "info@scandiweb.com" + }, + "authors": [ + { + "name": "Alfreds Genkins", + "email": "alfreds@scandiweb.com" }, - "support": { - "source": "https://github.com/scandipwa/quote-graphql", - "email": "info@scandiweb.com" - }, - "authors": [ - { - "name": "Alfreds Genkins", - "email": "alfreds@scandiweb.com" - }, - { - "name": "Ilja Lapkovskis", - "email": "ilja@scandiweb.com" - } - ], - "license": [ - "OSL-3.0", - "AFL-3.0" + { + "name": "Ilja Lapkovskis", + "email": "ilja@scandiweb.com" + } + ], + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "src/registration.php" ], - "autoload": { - "files": [ - "src/registration.php" - ], - "psr-4": { - "ScandiPWA\\QuoteGraphQl\\": "src" - } + "psr-4": { + "ScandiPWA\\QuoteGraphQl\\": "src" } + } } diff --git a/src/Model/Resolver/ExpandedOrderResolver.php b/src/Model/Resolver/ExpandedOrderResolver.php index a0370cb..6d49ad2 100755 --- a/src/Model/Resolver/ExpandedOrderResolver.php +++ b/src/Model/Resolver/ExpandedOrderResolver.php @@ -19,30 +19,28 @@ use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Sales\Model\ResourceModel\Order\CollectionFactoryInterface; use ScandiPWA\QuoteGraphQl\Model\Customer\CheckCustomerAccount; -use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; - use Magento\Sales\Model\OrderRepository; /** - * Orders data reslover + * Orders data resolver */ class ExpandedOrderResolver implements ResolverInterface { /** * @var CollectionFactoryInterface */ - private $collectionFactory; + protected $collectionFactory; /** * @var CheckCustomerAccount */ - private $checkCustomerAccount; + protected $checkCustomerAccount; /** * @var OrderRepository */ - private $orderRepository; + protected $orderRepository; /** * @param CollectionFactoryInterface $collectionFactory diff --git a/src/Model/Resolver/GetCartForCustomer.php b/src/Model/Resolver/GetCartForCustomer.php index 38f3b21..286a72b 100644 --- a/src/Model/Resolver/GetCartForCustomer.php +++ b/src/Model/Resolver/GetCartForCustomer.php @@ -27,7 +27,7 @@ use Magento\Quote\Api\GuestCartRepositoryInterface; use Magento\Quote\Model\Quote\Item as QuoteItem; use Magento\Webapi\Controller\Rest\ParamOverriderCustomerId; -use ScandiPWA\Performance\Model\Resolver\ProductPostProcessor; +use ScandiPWA\Performance\Model\Resolver\Products\DataPostProcessor; class GetCartForCustomer extends CartResolver { @@ -42,7 +42,7 @@ class GetCartForCustomer extends CartResolver protected $productFactory; /** - * @var ProductPostProcessor + * @var DataPostProcessor */ protected $productPostProcessor; @@ -58,7 +58,7 @@ class GetCartForCustomer extends CartResolver * @param GuestCartRepositoryInterface $guestCartRepository * @param Configurable $configurable * @param ProductFactory $productFactory - * @param ProductPostProcessor $productPostProcessor + * @param DataPostProcessor $productPostProcessor */ public function __construct( ParamOverriderCustomerId $overriderCustomerId, @@ -66,7 +66,7 @@ public function __construct( GuestCartRepositoryInterface $guestCartRepository, Configurable $configurable, ProductFactory $productFactory, - ProductPostProcessor $productPostProcessor + DataPostProcessor $productPostProcessor ) { parent::__construct( $guestCartRepository, @@ -88,7 +88,9 @@ protected function mergeQuoteItemData( QuoteItem $item, Product $product ) { - return $item->getData() + ['product' => $this->productsData[$product->getId()]]; + return [ + 'product' => $this->productsData[$product->getId()] + ] + $item->getData(); } /** @@ -144,14 +146,13 @@ public function resolve( $tax_amount = $address->getTaxAmount(); $discount_amount = $address->getDiscountAmount(); - return $cart->getData() + - [ + return [ 'items' => $itemsData, 'tax_amount' => $tax_amount, 'discount_amount' => $discount_amount, // In interface it is PHPDocumented that it returns bool, // while in implementation it returns int. 'is_virtual' => (bool) $cart->getIsVirtual() - ]; + ] + $cart->getData(); } } diff --git a/src/Model/Resolver/ProductResolver.php b/src/Model/Resolver/ProductResolver.php index aa7ec1a..8900818 100755 --- a/src/Model/Resolver/ProductResolver.php +++ b/src/Model/Resolver/ProductResolver.php @@ -14,47 +14,107 @@ namespace ScandiPWA\QuoteGraphQl\Model\Resolver; +use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Catalog\Model\ProductRepository; +use Magento\Sales\Model\Order\Item; +use ScandiPWA\Performance\Model\Resolver\Products\DataPostProcessor; +use ScandiPWA\Performance\Model\Resolver\ResolveInfoFieldsTrait; +use ScandiPWA\CatalogGraphQl\Model\Resolver\Products\DataProvider\Product; /** * Retrieves the Product list in orders */ class ProductResolver implements ResolverInterface { + use ResolveInfoFieldsTrait; + /** * @var ProductRepository */ protected $productRepository; /** + * @var Product + */ + protected $productDataProvider; + + /** + * @var SearchCriteriaBuilder + */ + protected $searchCriteriaBuilder; + + /** + * @var DataPostProcessor + */ + protected $postProcessor; + + /** + * ProductResolver constructor. * @param ProductRepository $productRepository + * @param Product $productDataProvider + * @param SearchCriteriaBuilder $searchCriteriaBuilder + * @param DataPostProcessor $postProcessor */ public function __construct( - ProductRepository $productRepository + ProductRepository $productRepository, + Product $productDataProvider, + SearchCriteriaBuilder $searchCriteriaBuilder, + DataPostProcessor $postProcessor ) { $this->productRepository = $productRepository; + $this->productDataProvider = $productDataProvider; + $this->searchCriteriaBuilder = $searchCriteriaBuilder; + $this->postProcessor = $postProcessor; } /** * Get All Product Items of Order. * @inheritdoc */ - public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) - { + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) { if (!isset($value['products'])) { - return null; + return []; } - foreach ($value['products'] as $key => $item) { - $product = $this->productRepository->get($item['sku']); + $productSKUs = array_map(function ($item) { + return $item['sku']; + }, $value['products']); + + $attributeCodes = $this->getFieldsFromProductInfo($info, 'order_products'); - $productData = $product->toArray(); - $productData['model'] = $product; + $searchCriteria = $this->searchCriteriaBuilder + ->addFilter('sku', $productSKUs, 'in') + ->create(); - $data[$key] = $productData; + $products = $this->productDataProvider + ->getList( + $searchCriteria, + $attributeCodes, + false, + true + ) + ->getItems(); + + $productsData = $this->postProcessor->process( + $products, + 'order_products', + $info + ); + + $data = []; + + foreach ($value['products'] as $key => $item) { + /** @var $item Item */ + $data[$key] = $productsData[$item->getProductId()]; $data[$key]['qty'] = $item->getQtyOrdered(); $data[$key]['row_total'] = $item->getBaseRowTotalInclTax(); $data[$key]['original_price'] = $item->getBaseOriginalPrice();