diff --git a/README.md b/README.md index 01f5097..fb55ec5 100644 --- a/README.md +++ b/README.md @@ -3,14 +3,15 @@ **WishlistGraphQl** provides additional resolvers for wishlist, extending Magento_WishlistGraphQl. -### AddProductToWishlist +### SaveWishlistItem -This endpoint allows to add product to Wishlist +This endpoint allows to save Wishlist item ```graphql -mutation AddProductToWishlist($productSku: String!) { - addProductToWishlist(productSku: $productSku) { +mutation SaveWishlistItem($wishlistItem: WishlistItemInput!) { + saveWishlistItem(wishlistItem: $wishlistItem) { id + sku qty description added_at @@ -21,15 +22,24 @@ mutation AddProductToWishlist($productSku: String!) { ```json { - "product_sku": "n31189077-1" + "wishlistItem": { + "sku": "n31189077-1", + "quantity": 2, + "description": "Description", + "product_option": { + "extension_attributes": {} + } + } } ``` ### RemoveProductFromWishlist +This endpoint allows removing item from wishlist + ```graphql -mutation RemoveProductFromWishlist($item_id: Int!) { +mutation RemoveProductFromWishlist($item_id: ID!) { removeProductFromWishlist(item_id: $item_id) } ``` @@ -39,3 +49,23 @@ mutation RemoveProductFromWishlist($item_id: Int!) { "item_id": 1 } ``` + +### MoveWishlistToCart + +This endpoint allows to move all wishlist items to cart + +```graphql +mutation MoveWishlistToCart { + moveWishlistToCart() +} +``` + +### ClearWishlist + +This endpoint allows to clear wishlist + +```graphql +mutation ClearWishlist { + clearWishlist() +} +``` diff --git a/composer.json b/composer.json index c4f990b..69ded58 100644 --- a/composer.json +++ b/composer.json @@ -17,6 +17,10 @@ { "name": "Alfreds Genkins", "email": "alfreds@scandiweb.com" + }, + { + "name": "Artjoms Travkovs", + "email": "artjoms.travkovs@scandiweb.com" } ], "license": [ diff --git a/src/Model/Resolver/AddProductToWishlist.php b/src/Model/Resolver/AddProductToWishlist.php deleted file mode 100644 index 9a1a85b..0000000 --- a/src/Model/Resolver/AddProductToWishlist.php +++ /dev/null @@ -1,122 +0,0 @@ -productRepository = $productRepository; - $this->customerRepository = $customerRepository; - $this->wishlistFactory = $wishlistFactory; - } - - /** - * @inheritdoc - */ - public function resolve( - Field $field, - $context, - ResolveInfo $info, - array $value = null, - array $args = null - ) { - $customerId = $context->getUserId(); - if ($customerId === null || $customerId === 0) { - throw new GraphQlAuthorizationException(__('Authorization unsuccessful')); - } - - if (!isset($args['productSku'])) { - throw new GraphQlInputException(__('Please specify valid product')); - } - - $product = $this->productRepository->get($args['productSku']); - if (!$product->isVisibleInCatalog()) { - throw new GraphQlInputException(__('Please specify valid product')); - } - - try { - $wishlist = $this->wishlistFactory->create(); - $wishlist->loadByCustomerId($customerId, true); - - $itemCollection = $wishlist->getItemCollection() - ->addFieldToFilter('product_id', $product->getId()); - - if ($itemCollection->getSize() > 0) { - throw new GraphQlInputException(__('Product has already been added to wishlist')); - } - - $wishlistItem = $wishlist->addNewItem($product); - $wishlist->save(); - } catch (Exception $e) { - throw new GraphQlNoSuchEntityException(__('There was an error when trying to save wishlist')); - } - - if ($wishlistItem->getProductId() === null) { - return []; - } - - return array_merge( - $wishlistItem->getData(), - ['model' => $wishlistItem], - ['product' => - array_merge( - $wishlistItem->getProduct()->getData(), - ['model' => $product] - ) - ]); - } -} \ No newline at end of file diff --git a/src/Model/Resolver/ClearWishlist.php b/src/Model/Resolver/ClearWishlist.php new file mode 100644 index 0000000..d0da73e --- /dev/null +++ b/src/Model/Resolver/ClearWishlist.php @@ -0,0 +1,84 @@ +wishlistFactory = $wishlistFactory; + $this->wishlistResource = $wishlistResource; + } + + /** + * @inheritDoc + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) { + $customerId = $context->getUserId(); + if (!$customerId) { + throw new GraphQlAuthorizationException(__('Authorization unsuccessful')); + } + + /** @var Wishlist $wishlist */ + $wishlist = $this->wishlistFactory->create(); + $this->wishlistResource->load($wishlist, $customerId, 'customer_id'); + + if (!$wishlist->getId() || $wishlist->getItemsCount() <= 0) { + return true; + } + + $wishlistItems = $wishlist->getItemCollection(); + foreach ($wishlistItems as $item) { + $item->delete(); + } + + try { + $wishlist->save(); + } catch (Exception $e) { + throw new GraphQlNoSuchEntityException(__('There was an error when clearing wishlist')); + } + + return true; + } +} diff --git a/src/Model/Resolver/MoveWishlistToCart.php b/src/Model/Resolver/MoveWishlistToCart.php new file mode 100644 index 0000000..72c5840 --- /dev/null +++ b/src/Model/Resolver/MoveWishlistToCart.php @@ -0,0 +1,187 @@ +overriderCartId = $overriderCartId; + $this->quoteRepository = $quoteRepository; + $this->quoteManagement = $quoteManagement; + $this->wishlistFactory = $wishlistFactory; + $this->wishlistResource = $wishlistResource; + } + + /** + * Adds new items from wishlist to cart + * + * @param array $wishlistItems + * @return void + */ + protected function addItemsToCart(array $wishlistItems): void + { + $quoteId = $this->overriderCartId->getOverriddenValue(); + $quote = $this->quoteRepository->getActive($quoteId); + + foreach ($wishlistItems as $item) { + $product = $item['product']; + + $data = []; + if ($product->getTypeId() === Configurable::TYPE_CODE) { + $data['super_attribute'] = $item['super_attribute']; + } + + $buyRequest = new DataObject(); + $buyRequest->setData($data); + + $quoteItem = $quote->addProduct($product, $buyRequest); + $quoteItem->setQty($item['qty']); + } + + try { + $this->quoteRepository->save($quote); + } catch (Exception $e) { + throw new GraphQlNoSuchEntityException(__('There was an error when trying to save wishlist items to cart')); + } + } + + /** + * Prepares array with wishlist data + * + * @param Wishlist $wishlist + * @return array + */ + protected function getWishlistItems(Wishlist $wishlist): array + { + $items = []; + $itemsCollection = $wishlist->getItemCollection(); + + foreach ($itemsCollection as $item) { + $product = $item->getProduct(); + $superAttribute = $item->getBuyRequest()->getSuperAttribute(); + + $items[$product->getSku()] = [ + 'qty' => $item->getQty(), + 'product' => $product, + 'super_attribute' => $superAttribute, + ]; + + $item->delete(); + } + + return $items; + } + + /** + * @inheritDoc + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) { + $customerId = $context->getUserId(); + if ($customerId === null || $customerId === 0) { + throw new GraphQlAuthorizationException(__('Authorization unsuccessful')); + } + + $cart = $this->quoteManagement->getCartForCustomer($customerId); + + /** @var Wishlist $wishlist */ + $wishlist = $this->wishlistFactory->create(); + $this->wishlistResource->load($wishlist, $customerId, 'customer_id'); + + if (!$wishlist->getId() || $wishlist->getItemsCount() <= 0) { + return true; + } + + $wishlistItems = $this->getWishlistItems($wishlist); + $cartItems = $cart->getItems(); + + foreach ($cartItems as $item) { + $product = $item->getProduct(); + $qty = $item->getQty(); + $sku = $product->getSku(); + + if (array_key_exists($sku, $wishlistItems)) { + $wishlistItem = $wishlistItems[$sku]; + unset($wishlistItems[$sku]); + + $qty += $wishlistItem['qty']; + $item->setQty($qty); + } + } + + $this->addItemsToCart($wishlistItems, $cart); + + try { + $wishlist->save(); + $cart->save(); + } catch (Exception $e) { + throw new GraphQlNoSuchEntityException(__('There was an error when trying to save wishlist items to cart')); + } + + return true; + } +} diff --git a/src/Model/Resolver/RemoveProductFromWishlist.php b/src/Model/Resolver/RemoveProductFromWishlist.php index e768880..1a82e8b 100644 --- a/src/Model/Resolver/RemoveProductFromWishlist.php +++ b/src/Model/Resolver/RemoveProductFromWishlist.php @@ -16,8 +16,6 @@ namespace ScandiPWA\WishlistGraphQl\Model\Resolver; 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\Customer\Api\CustomerRepositoryInterface; @@ -75,7 +73,7 @@ public function resolve( array $args = null ) { $customerId = $context->getUserId(); - if ($customerId === null || $customerId === 0) { + if (!$customerId) { throw new GraphQlAuthorizationException(__('There was an issue with authorization')); } diff --git a/src/Model/Resolver/SaveProductToWishlist.php b/src/Model/Resolver/SaveProductToWishlist.php new file mode 100644 index 0000000..3afc291 --- /dev/null +++ b/src/Model/Resolver/SaveProductToWishlist.php @@ -0,0 +1,189 @@ +productRepository = $productRepository; + $this->customerRepository = $customerRepository; + $this->wishlistFactory = $wishlistFactory; + } + + /** + * @inheritdoc + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) { + $customerId = $context->getUserId(); + if (!$customerId) { + throw new GraphQlAuthorizationException(__('Authorization unsuccessful')); + } + + $sku = $args['wishlistItem']['sku'] ?? ''; + $itemId = $args['wishlistItem']['item_id'] ?? ''; + + $wishlist = $this->wishlistFactory->create(); + $wishlist->loadByCustomerId($customerId, true); + + if ($sku !== '') { + return $this->addProductToWishlist($wishlist, $sku, $args['wishlistItem']); + } + + if ($itemId !== '') { + return $this->updateWishlistItem($wishlist, $itemId, $args['wishlistItem']); + } + + throw new GraphQlInputException(__('Please specify either sku or item_id')); + } + + protected function addProductToWishlist(Wishlist $wishlist, string $sku, array $parameters) + { + $quantity = $parameters['quantity'] || 1; + $description = $parameters['description'] ?? ''; + $productOption = $parameters['product_option'] ?? []; + + $product = $this->productRepository->get($sku); + if (!$product->isVisibleInCatalog()) { + throw new GraphQlInputException(__('Please specify valid product')); + } + + try { + $configurableData = []; + if ($product->getTypeId() === Configurable::TYPE_CODE) { + $configurableOptions = $this->getOptionsArray($productOption['extension_attributes']['configurable_item_options']); + $configurableData['super_attribute'] = $configurableOptions; + } + + $wishlistItem = $wishlist->addNewItem($product, $configurableData); + $wishlistItem->setDescription($description); + $wishlistItem->setQty($quantity); + + $wishlist->save(); + } catch (Exception $e) { + throw new GraphQlNoSuchEntityException(__('There was an error when trying to save wishlist')); + } + + if ($wishlistItem->getProductId() === null) { + return []; + } + + return array_merge( + $wishlistItem->getData(), + ['model' => $wishlistItem], + [ + 'product' => + array_merge( + $wishlistItem->getProduct()->getData(), + ['model' => $product] + ), + ] + ); + } + + protected function updateWishlistItem(Wishlist $wishlist, string $itemId, array $parameters) + { + if (!(array_key_exists('quantity', $parameters) || array_key_exists('description', $parameters))) { + throw new GraphQlInputException(__('Please specify either quantity or description to update')); + } + + $item = $wishlist->getItem($itemId); + + if (!$item->getId()) { + throw new GraphQlInputException(__('Please specify a valid wishlist item')); + } + + if ($wishlist->getId() !== $item->getWishlistId()) { + throw new GraphQlNoSuchEntityException(__('Invalid wishlist')); + } + + if (array_key_exists('quantity', $parameters)) { + $item->setQty($parameters['quantity']); + } + + if (array_key_exists('description', $parameters)) { + $item->setDescription($parameters['description']); + } + + try { + $item->save(); + $wishlist->save(); + } catch (Exception $e) { + throw new GraphQlNoSuchEntityException(__('There was an error when trying to update wishlist item')); + } + + return array_merge( + $item->getData(), + ['model' => $item] + ); + } + + protected function getOptionsArray(array $configurableOptions) + { + $optionsArray = []; + foreach ($configurableOptions as ['option_id' => $id, 'option_value' => $value]) { + $optionsArray[(string) $id] = (int) $value; + } + + return $optionsArray; + } +} diff --git a/src/Model/Resolver/WishlistItemsResolver.php b/src/Model/Resolver/WishlistItemsResolver.php new file mode 100644 index 0000000..8fa2ed0 --- /dev/null +++ b/src/Model/Resolver/WishlistItemsResolver.php @@ -0,0 +1,136 @@ +wishlistItemsFactory = $wishlistItemsFactory; + $this->storeManager = $storeManager; + $this->productFactory = $productFactory; + } + + /** + * @inheritdoc + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) { + if (!isset($value['model'])) { + throw new LocalizedException(__('Missing key "model" in Wishlist value data')); + } + /** @var Wishlist $wishlist */ + $wishlist = $value['model']; + + $wishlistItems = $this->getWishListItems($wishlist); + + $data = []; + foreach ($wishlistItems as $wishlistItem) { + $data[] = [ + 'id' => $wishlistItem->getId(), + 'qty' => $wishlistItem->getData('qty'), + 'sku' => $this->getWishListItemSku($wishlistItem), + 'description' => $wishlistItem->getDescription(), + 'added_at' => $wishlistItem->getAddedAt(), + 'model' => $wishlistItem, + ]; + } + + return $data; + } + + /** + * Get wishlist items + * + * @param Wishlist $wishlist + * @return Item[] + */ + private function getWishListItems(Wishlist $wishlist): array + { + /** @var WishlistItemCollection $collection */ + $collection = $this->wishlistItemsFactory->create(); + $collection + ->addWishlistFilter($wishlist) + ->addStoreFilter(array_map(function (StoreInterface $store) { + return $store->getId(); + }, $this->storeManager->getStores())) + ->setVisibilityFilter(); + + return $collection->getItems(); + } + + /** + * Get wishlist item's sku + * + * @return string + */ + private function getWishListItemSku($wishlistItem): string + { + $product = $wishlistItem->getProduct(); + + if ($product->getTypeId() === Configurable::TYPE_CODE) { + $variantId = $wishlistItem->getOptionByCode('simple_product')->getValue(); + $childProduct = $this->productFactory->create()->load($variantId); + + return $childProduct->getSku(); + } + + return $product->getSku(); + } +} diff --git a/src/Model/Resolver/WishlistResolver.php b/src/Model/Resolver/WishlistResolver.php index f4777d3..9403a8f 100644 --- a/src/Model/Resolver/WishlistResolver.php +++ b/src/Model/Resolver/WishlistResolver.php @@ -20,9 +20,6 @@ use Magento\Wishlist\Model\ResourceModel\Wishlist as WishlistResourceModel; use Magento\Wishlist\Model\Wishlist; use Magento\Wishlist\Model\WishlistFactory; -use Magento\Framework\GraphQl\Exception\GraphQlInputException; -use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; -use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; /** * Fetches the Wishlist data according to the GraphQL schema @@ -65,7 +62,7 @@ public function resolve( $wishlist = $this->wishlistFactory->create(); $this->wishlistResource->load($wishlist, $customerId, 'customer_id'); - if (null === $wishlist->getId()) { + if (!$wishlist->getId()) { return [ 'model' => $wishlist, ]; diff --git a/src/etc/di.xml b/src/etc/di.xml index 4541144..21b3ee7 100644 --- a/src/etc/di.xml +++ b/src/etc/di.xml @@ -14,4 +14,6 @@ + diff --git a/src/etc/schema.graphqls b/src/etc/schema.graphqls index 6be1a9d..4d49f0e 100755 --- a/src/etc/schema.graphqls +++ b/src/etc/schema.graphqls @@ -12,6 +12,20 @@ # See COPYING.txt for license details. type Mutation { - addProductToWishlist(productSku: String!): WishlistItem @resolver(class:"\\ScandiPWA\\WishlistGraphQl\\Model\\Resolver\\AddProductToWishlist") - removeProductFromWishlist(itemId: String!): Boolean @resolver(class:"\\ScandiPWA\\WishlistGraphQl\\Model\\Resolver\\RemoveProductFromWishlist") + saveWishlistItem(wishlistItem: WishlistItemInput!): WishlistItem @doc(description: "Saves wishlist item") @resolver(class:"\\ScandiPWA\\WishlistGraphQl\\Model\\Resolver\\SaveProductToWishlist") + removeProductFromWishlist(itemId: ID!): Boolean @doc(description: "Removes product from wishlist") @resolver(class:"\\ScandiPWA\\WishlistGraphQl\\Model\\Resolver\\RemoveProductFromWishlist") + moveWishlistToCart: Boolean @doc(description: "Moves items from wishlist to cart") @resolver(class:"ScandiPWA\\WishlistGraphQl\\Model\\Resolver\\MoveWishlistToCart") + clearWishlist: Boolean @doc(description: "Clears wishlist") @resolver(class: "ScandiPWA\\WishlistGraphQl\\Model\\Resolver\\ClearWishlist") +} + +input WishlistItemInput { + sku: ID @doc(description: "Sku of the product") + item_id: ID @doc(description: "Id of the wishlist item") + quantity: Int @doc(description: "Quantity of the product") + description: String @doc(description: "User description of wish list item") + product_option: ProductOptionInput @doc(description: "Configurable product options") +} + +extend type WishlistItem { + sku: ID @doc(description: "The wish list item's SKU") }