From e5ae941438ac29a1cecbf626e075944a3cf43980 Mon Sep 17 00:00:00 2001 From: Sergiy Zhovnir Date: Thu, 21 May 2020 14:57:33 +0300 Subject: [PATCH 1/7] #catalog-storefront/issue-12 Add new GraphQL endpoint for single product retrieval --- .../DataProvider/ProductDataProvider.php | 58 +++++++++++++++ .../ProductDataProviderInterface.php | 25 +++++++ .../Resolver/Product/ProductIdIdentity.php | 34 +++++++++ .../Model/Resolver/ProductId.php | 72 +++++++++++++++++++ app/code/Magento/CatalogGraphQl/etc/di.xml | 1 + .../CatalogGraphQl/etc/schema.graphqls | 4 ++ 6 files changed, 194 insertions(+) create mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/Product/DataProvider/ProductDataProvider.php create mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/Product/DataProvider/ProductDataProviderInterface.php create mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductIdIdentity.php create mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/ProductId.php diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/DataProvider/ProductDataProvider.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/DataProvider/ProductDataProvider.php new file mode 100644 index 0000000000000..74db074e2b581 --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/DataProvider/ProductDataProvider.php @@ -0,0 +1,58 @@ +productResourceModel = $productResourceModel; + $this->productFactory = $productFactory; + } + + /** + * Get product data by ID with full data set + * + * @param int $productId + * @param array $attributeCodes + * @return ProductInterface|Product + */ + public function getProductById(int $productId, array $attributeCodes) + { + /** @var \Magento\Catalog\Model\Product $product */ + $product = $this->productFactory->create(); + $this->productResourceModel->load($product, $productId, $attributeCodes); + + return $product; + } +} diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/DataProvider/ProductDataProviderInterface.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/DataProvider/ProductDataProviderInterface.php new file mode 100644 index 0000000000000..acd18c7f92e5f --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/DataProvider/ProductDataProviderInterface.php @@ -0,0 +1,25 @@ +cacheTag, $resolvedData['id']]; + } +} diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/ProductId.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/ProductId.php new file mode 100644 index 0000000000000..8174c751982e1 --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/ProductId.php @@ -0,0 +1,72 @@ +productDataProvider = $productDataProvider; + $this->productFieldsSelector = $productFieldsSelector; + } + + /** + * @inheritdoc + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) { + $productId = (int) ($args['id'] ?? 0); + $fields = $this->productFieldsSelector->getProductFieldsFromInfo($info); + + if (!$productId) { + throw new GraphQlInputException( + __("'id' input argument is required.") + ); + } + + /** @var \Magento\Catalog\Model\Product $product */ + $product = $this->productDataProvider->getProductById($productId, $fields); + $data = $product->getData(); + $data['model'] = $product; + + return $data; + } +} diff --git a/app/code/Magento/CatalogGraphQl/etc/di.xml b/app/code/Magento/CatalogGraphQl/etc/di.xml index 5fec7bfd4fda7..8abd7e9ed14fe 100644 --- a/app/code/Magento/CatalogGraphQl/etc/di.xml +++ b/app/code/Magento/CatalogGraphQl/etc/di.xml @@ -74,4 +74,5 @@ + diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls index f77b301d61e28..85aeb9e6a8f10 100644 --- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls @@ -10,6 +10,10 @@ type Query { sort: ProductAttributeSortInput @doc(description: "Specifies which attributes to sort on, and whether to return the results in ascending or descending order.") ): Products @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Products") @doc(description: "The products query searches for products that match the criteria specified in the search and filter attributes.") @cache(cacheIdentity: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\Identity") + product ( + id: Int @doc(description: "Id of the product.") + ): ProductInterface + @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\ProductId") @doc(description: "The product query searches for product that match the criteria specified in the search and filter attributes.") @cache(cacheIdentity: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductIdIdentity") category ( id: Int @doc(description: "Id of the category.") ): CategoryTree From 5d21f2367d6a0f1573dc3c00724633ea0d466efc Mon Sep 17 00:00:00 2001 From: Sergiy Zhovnir Date: Fri, 22 May 2020 09:48:55 +0300 Subject: [PATCH 2/7] Add strict type --- .../Resolver/Product/DataProvider/ProductDataProvider.php | 5 +++-- .../Product/DataProvider/ProductDataProviderInterface.php | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/DataProvider/ProductDataProvider.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/DataProvider/ProductDataProvider.php index 74db074e2b581..0166318a0dcb6 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/DataProvider/ProductDataProvider.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/DataProvider/ProductDataProvider.php @@ -8,6 +8,7 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Product\DataProvider; use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Model\Product; use Magento\Catalog\Model\ProductFactory; use Magento\Catalog\Model\ResourceModel\Product as ProductResourceModel; @@ -47,9 +48,9 @@ public function __construct( * @param array $attributeCodes * @return ProductInterface|Product */ - public function getProductById(int $productId, array $attributeCodes) + public function getProductById(int $productId, array $attributeCodes): ProductInterface { - /** @var \Magento\Catalog\Model\Product $product */ + /** @var Product $product */ $product = $this->productFactory->create(); $this->productResourceModel->load($product, $productId, $attributeCodes); diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/DataProvider/ProductDataProviderInterface.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/DataProvider/ProductDataProviderInterface.php index acd18c7f92e5f..9a00e0e1e0d21 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/DataProvider/ProductDataProviderInterface.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/DataProvider/ProductDataProviderInterface.php @@ -21,5 +21,5 @@ interface ProductDataProviderInterface * @param array $attributeCodes * @return ProductInterface */ - public function getProductById(int $productId, array $attributeCodes); + public function getProductById(int $productId, array $attributeCodes): ProductInterface; } From 4e17fd7dc31ddaaf158d9c9d4351e9aaddba5025 Mon Sep 17 00:00:00 2001 From: Sergiy Zhovnir Date: Fri, 22 May 2020 09:49:41 +0300 Subject: [PATCH 3/7] Adjust description --- app/code/Magento/CatalogGraphQl/etc/schema.graphqls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls index 85aeb9e6a8f10..ab3627a5852b0 100644 --- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls @@ -13,7 +13,7 @@ type Query { product ( id: Int @doc(description: "Id of the product.") ): ProductInterface - @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\ProductId") @doc(description: "The product query searches for product that match the criteria specified in the search and filter attributes.") @cache(cacheIdentity: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductIdIdentity") + @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\ProductId") @doc(description: "The product query searches for product that match the specified product id.") @cache(cacheIdentity: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductIdIdentity") category ( id: Int @doc(description: "Id of the category.") ): CategoryTree From 7c02282a8afa553f0ec71fca9650abe0c6164f26 Mon Sep 17 00:00:00 2001 From: Sergiy Zhovnir Date: Tue, 9 Jun 2020 16:13:47 +0300 Subject: [PATCH 4/7] #catalog-storefront/issue-12 Added validation for negative product ID --- .../Magento/CatalogGraphQl/Model/Resolver/ProductId.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/ProductId.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/ProductId.php index 8174c751982e1..e8cb0155e094f 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/ProductId.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/ProductId.php @@ -62,6 +62,12 @@ public function resolve( ); } + if ($productId < 0) { + throw new GraphQlInputException( + __("'id' input argument should not be negative.") + ); + } + /** @var \Magento\Catalog\Model\Product $product */ $product = $this->productDataProvider->getProductById($productId, $fields); $data = $product->getData(); From b68976592ac1cd005f46cb39bd22977fd2461b12 Mon Sep 17 00:00:00 2001 From: Sergiy Zhovnir Date: Tue, 23 Jun 2020 16:34:09 +0300 Subject: [PATCH 5/7] #issue-12 Added ability to fetch list of products by IDs & GraphQl schema adjustments --- .../DataProvider/ProductDataProvider.php | 21 ++++++++++------- .../ProductDataProviderInterface.php | 10 ++++---- .../Resolver/Product/ProductIdIdentity.php | 8 +++---- .../Model/Resolver/ProductId.php | 23 +++++++------------ .../CatalogGraphQl/etc/schema.graphqls | 6 ++--- 5 files changed, 33 insertions(+), 35 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/DataProvider/ProductDataProvider.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/DataProvider/ProductDataProvider.php index 0166318a0dcb6..dbbb9d4caddaa 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/DataProvider/ProductDataProvider.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/DataProvider/ProductDataProvider.php @@ -42,18 +42,23 @@ public function __construct( } /** - * Get product data by ID with full data set + * Get list of products by IDs with full data set * - * @param int $productId + * @param array $productIds * @param array $attributeCodes - * @return ProductInterface|Product + * @return ProductInterface[]|Product[] */ - public function getProductById(int $productId, array $attributeCodes): ProductInterface + public function getProductByIds(array $productIds, array $attributeCodes): array { - /** @var Product $product */ - $product = $this->productFactory->create(); - $this->productResourceModel->load($product, $productId, $attributeCodes); + $products = []; - return $product; + foreach ($productIds as $productId) { + /** @var Product $product */ + $product = $this->productFactory->create(); + $this->productResourceModel->load($product, $productId, $attributeCodes); + $products[] = $product; + } + + return $products; } } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/DataProvider/ProductDataProviderInterface.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/DataProvider/ProductDataProviderInterface.php index 9a00e0e1e0d21..c5a2aea864e20 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/DataProvider/ProductDataProviderInterface.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/DataProvider/ProductDataProviderInterface.php @@ -10,16 +10,16 @@ use Magento\Catalog\Api\Data\ProductInterface; /** - * Provides product data by product ID. + * Provides product data by product IDs. */ interface ProductDataProviderInterface { /** - * Retrieve product by product ID. + * Retrieve product by product IDs. * - * @param int $productId + * @param array $productIds * @param array $attributeCodes - * @return ProductInterface + * @return ProductInterface[] */ - public function getProductById(int $productId, array $attributeCodes): ProductInterface; + public function getProductByIds(array $productIds, array $attributeCodes): array; } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductIdIdentity.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductIdIdentity.php index dbe24a375eade..059a51d032852 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductIdIdentity.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductIdIdentity.php @@ -11,7 +11,7 @@ use Magento\Framework\GraphQl\Query\Resolver\IdentityInterface; /** - * Identity for resolved product by ID + * Identity for resolved products by IDs */ class ProductIdIdentity implements IdentityInterface { @@ -21,14 +21,14 @@ class ProductIdIdentity implements IdentityInterface private $cacheTag = Product::CACHE_TAG; /** - * Get product id for cache tag + * Get product ids for cache tag * * @param array $resolvedData * @return array */ public function getIdentities(array $resolvedData): array { - return empty($resolvedData['id']) ? - [] : [$this->cacheTag, $resolvedData['id']]; + return empty($resolvedData['ids']) ? + [] : [$this->cacheTag, implode('_', $resolvedData['ids'])]; } } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/ProductId.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/ProductId.php index e8cb0155e094f..2f9bebb5d56e3 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/ProductId.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/ProductId.php @@ -53,26 +53,19 @@ public function resolve( array $value = null, array $args = null ) { - $productId = (int) ($args['id'] ?? 0); + $productIds = ($args['ids'] ?? []); $fields = $this->productFieldsSelector->getProductFieldsFromInfo($info); - if (!$productId) { - throw new GraphQlInputException( - __("'id' input argument is required.") - ); - } + /** @var \Magento\Catalog\Model\Product[] $products */ + $products = $this->productDataProvider->getProductByIds($productIds, $fields); + $data = []; - if ($productId < 0) { - throw new GraphQlInputException( - __("'id' input argument should not be negative.") - ); + foreach ($products as $product) { + $productData = $product->getData(); + $productData['model'] = $product; + $data[] = $productData; } - /** @var \Magento\Catalog\Model\Product $product */ - $product = $this->productDataProvider->getProductById($productId, $fields); - $data = $product->getData(); - $data['model'] = $product; - return $data; } } diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls index ab3627a5852b0..c03d9ef9c3914 100644 --- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls @@ -11,9 +11,9 @@ type Query { ): Products @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Products") @doc(description: "The products query searches for products that match the criteria specified in the search and filter attributes.") @cache(cacheIdentity: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\Identity") product ( - id: Int @doc(description: "Id of the product.") - ): ProductInterface - @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\ProductId") @doc(description: "The product query searches for product that match the specified product id.") @cache(cacheIdentity: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductIdIdentity") + ids: [ID!]! @doc(description: "Product IDs.") + ): [ProductInterface] + @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\ProductId") @doc(description: "The product query fetches products that match the specified IDs.") @cache(cacheIdentity: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductIdIdentity") category ( id: Int @doc(description: "Id of the category.") ): CategoryTree From 9539a75b484efbcb40b7c07a7c9ee6bec8d94c9b Mon Sep 17 00:00:00 2001 From: Sergiy Zhovnir Date: Wed, 24 Jun 2020 09:28:15 +0300 Subject: [PATCH 6/7] #issue-12 Refactoring the class names according to the new behavior & Renamed the query to productsByID --- .../Resolver/Product/ProductsByIdIdentity.php | 34 +++++++++ .../Model/Resolver/ProductsByID.php | 70 +++++++++++++++++++ .../CatalogGraphQl/etc/schema.graphqls | 4 +- 3 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductsByIdIdentity.php create mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/ProductsByID.php diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductsByIdIdentity.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductsByIdIdentity.php new file mode 100644 index 0000000000000..9f78be9d5760a --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductsByIdIdentity.php @@ -0,0 +1,34 @@ +cacheTag, implode('_', $resolvedData['ids'])]; + } +} diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/ProductsByID.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/ProductsByID.php new file mode 100644 index 0000000000000..45aa2ae05b85d --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/ProductsByID.php @@ -0,0 +1,70 @@ +productDataProvider = $productDataProvider; + $this->productFieldsSelector = $productFieldsSelector; + } + + /** + * @inheritdoc + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) { + $productIds = ($args['ids'] ?? []); + $fields = $this->productFieldsSelector->getProductFieldsFromInfo($info); + + /** @var \Magento\Catalog\Model\Product[] $products */ + $products = $this->productDataProvider->getProductByIds($productIds, $fields); + $data = []; + + foreach ($products as $product) { + $productData = $product->getData(); + $productData['model'] = $product; + $data[] = $productData; + } + + return $data; + } +} diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls index c03d9ef9c3914..15c48343ac5f7 100644 --- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls @@ -10,10 +10,10 @@ type Query { sort: ProductAttributeSortInput @doc(description: "Specifies which attributes to sort on, and whether to return the results in ascending or descending order.") ): Products @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Products") @doc(description: "The products query searches for products that match the criteria specified in the search and filter attributes.") @cache(cacheIdentity: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\Identity") - product ( + productsByID ( ids: [ID!]! @doc(description: "Product IDs.") ): [ProductInterface] - @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\ProductId") @doc(description: "The product query fetches products that match the specified IDs.") @cache(cacheIdentity: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductIdIdentity") + @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\ProductsByID") @doc(description: "The productsByID query fetches products that match the specified IDs.") @cache(cacheIdentity: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductsByIdIdentity") category ( id: Int @doc(description: "Id of the category.") ): CategoryTree From 578ea6e1528a9d1db1c580210b77f4f6aa55bcbe Mon Sep 17 00:00:00 2001 From: Sergiy Zhovnir Date: Wed, 24 Jun 2020 09:28:39 +0300 Subject: [PATCH 7/7] #issue-12 Removed unused classes --- .../Resolver/Product/ProductIdIdentity.php | 34 --------- .../Model/Resolver/ProductId.php | 71 ------------------- 2 files changed, 105 deletions(-) delete mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductIdIdentity.php delete mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/ProductId.php diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductIdIdentity.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductIdIdentity.php deleted file mode 100644 index 059a51d032852..0000000000000 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductIdIdentity.php +++ /dev/null @@ -1,34 +0,0 @@ -cacheTag, implode('_', $resolvedData['ids'])]; - } -} diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/ProductId.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/ProductId.php deleted file mode 100644 index 2f9bebb5d56e3..0000000000000 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/ProductId.php +++ /dev/null @@ -1,71 +0,0 @@ -productDataProvider = $productDataProvider; - $this->productFieldsSelector = $productFieldsSelector; - } - - /** - * @inheritdoc - */ - public function resolve( - Field $field, - $context, - ResolveInfo $info, - array $value = null, - array $args = null - ) { - $productIds = ($args['ids'] ?? []); - $fields = $this->productFieldsSelector->getProductFieldsFromInfo($info); - - /** @var \Magento\Catalog\Model\Product[] $products */ - $products = $this->productDataProvider->getProductByIds($productIds, $fields); - $data = []; - - foreach ($products as $product) { - $productData = $product->getData(); - $productData['model'] = $product; - $data[] = $productData; - } - - return $data; - } -}