Skip to content

Commit

Permalink
Mutation for updating cart item quantities
Browse files Browse the repository at this point in the history
  • Loading branch information
pmclain committed Feb 24, 2019
1 parent 0d0b300 commit 37eacee
Show file tree
Hide file tree
Showing 4 changed files with 290 additions and 0 deletions.
58 changes: 58 additions & 0 deletions app/code/Magento/QuoteGraphQl/Model/Cart/UpdateCartItems.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php
declare(strict_types=1);
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Magento\QuoteGraphQl\Model\Cart;

use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Quote\Model\Quote;
use Magento\Quote\Api\CartRepositoryInterface;
use Magento\Quote\Api\GuestCartRepositoryInterface;

class UpdateCartItems
{
/**
* @var CartRepositoryInterface
*/
private $quoteRepository;

/**
* @var GuestCartRepositoryInterface
*/
private $guestCartRepository;

public function __construct(
CartRepositoryInterface $quoteRepository,
GuestCartRepositoryInterface $guestCartRepository
) {
$this->quoteRepository = $quoteRepository;
$this->guestCartRepository = $guestCartRepository;
}

public function update(string $maskedCartId, array $items): Quote
{
$quote = $this->guestCartRepository->get($maskedCartId);

foreach ($items as $item) {
$quoteItem = $quote->getItemById($item['item_id']);
if ($quoteItem === false) {
throw new NoSuchEntityException(__('Could not find cart item with id: %1', $item['item_id']));
}

$qty = $item['qty'];

if ($qty <= 0.0) {
$quote->removeItem($quoteItem->getItemId());
continue;
}

$quoteItem->setQty($qty);
}

$this->quoteRepository->save($quote);

return $quote;
}
}
74 changes: 74 additions & 0 deletions app/code/Magento/QuoteGraphQl/Model/Resolver/UpdateCartItems.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php
declare(strict_types=1);
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Magento\QuoteGraphQl\Model\Resolver;

use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\Quote\Api\GuestCartRepositoryInterface;
use Magento\QuoteGraphQl\Model\Cart\ExtractDataFromCart;
use Magento\Framework\Stdlib\ArrayManager;
use Magento\QuoteGraphQl\Model\Cart\UpdateCartItems as UpdateCartItemsService;

class UpdateCartItems implements ResolverInterface
{
/**
* @var ExtractDataFromCart
*/
private $extractDataFromCart;

/**
* @var ArrayManager
*/
private $arrayManager;

/**
* @var UpdateCartItemsService
*/
private $updateCartItems;

/**
* @param ExtractDataFromCart $extractDataFromCart
* @param ArrayManager $arrayManager
* @param UpdateCartItemsService $updateCartItems
*/
public function __construct(
ExtractDataFromCart $extractDataFromCart,
ArrayManager $arrayManager,
UpdateCartItemsService $updateCartItems
) {
$this->extractDataFromCart = $extractDataFromCart;
$this->arrayManager = $arrayManager;
$this->updateCartItems = $updateCartItems;
}

public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null)
{
$cartItems = $this->arrayManager->get('input/cart_items', $args);
$maskedCartId = $this->arrayManager->get('input/cart_id', $args);

if (!$maskedCartId) {
throw new GraphQlInputException(__('Required parameter "cart_id" is missing'));
}
if (!$cartItems) {
throw new GraphQlInputException(__('Required parameter "cart_items " is missing'));
}

try {
$cart = $this->updateCartItems->update($maskedCartId, $cartItems);
} catch (NoSuchEntityException $e) {
throw new GraphQlNoSuchEntityException(__($e->getMessage()));
}

$cartData = $this->extractDataFromCart->execute($cart);

return ['cart' => array_merge(['cart_id' => $maskedCartId], $cartData)];
}
}
15 changes: 15 additions & 0 deletions app/code/Magento/QuoteGraphQl/etc/schema.graphqls
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type Mutation {
setShippingAddressesOnCart(input: SetShippingAddressesOnCartInput): SetShippingAddressesOnCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\SetShippingAddressesOnCart")
setBillingAddressOnCart(input: SetBillingAddressOnCartInput): SetBillingAddressOnCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\SetBillingAddressOnCart")
setShippingMethodsOnCart(input: SetShippingMethodsOnCartInput): SetShippingMethodsOnCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\SetShippingMethodsOnCart")
updateCartItems(input: UpdateCartItemsInput): UpdateCartItemsOutput @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\UpdateCartItems")
}

input AddSimpleProductsToCartInput {
Expand Down Expand Up @@ -206,6 +207,20 @@ type AddVirtualProductsToCartOutput {
cart: Cart!
}

input UpdateCartItemsInput {
cart_id: String!
cart_items: [UpdateCartItemInput!]!
}

input UpdateCartItemInput {
item_id: String!
qty: Float!
}

type UpdateCartItemsOutput {
cart: Cart!
}

type SimpleCartItem implements CartItemInterface @doc(description: "Simple Cart Item") {
customizable_options: [SelectedCustomizableOption] @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\CustomizableOptions")
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\GraphQl\Quote;

use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Quote\Model\Quote;
use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface;
use Magento\Quote\Model\ResourceModel\Quote as QuoteResource;
use Magento\TestFramework\Helper\Bootstrap;
use Magento\TestFramework\TestCase\GraphQlAbstract;

/**
* Test for updating/removing shopping cart items
*/
class UpdateCartItemsTest extends GraphQlAbstract
{
/**
* @var QuoteResource
*/
private $quoteResource;

/**
* @var Quote
*/
private $quote;

/**
* @var QuoteIdToMaskedQuoteIdInterface
*/
private $quoteIdToMaskedId;

/**
* @var ProductRepositoryInterface
*/
private $productRepository;

protected function setUp()
{
$objectManager = Bootstrap::getObjectManager();
$this->quoteResource = $objectManager->create(QuoteResource::class);
$this->quote = $objectManager->create(Quote::class);
$this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class);
$this->productRepository = $objectManager->get(ProductRepositoryInterface::class);
}

/**
* @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php
*/
public function testUpdateCartItemQty()
{
$this->quoteResource->load(
$this->quote,
'test_order_with_simple_product_without_address',
'reserved_order_id'
);
$maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId());
$quoteItem = $this->quote->getItemByProduct($this->productRepository->get('simple'));
$qty = $quoteItem->getQty() + 2;

$query = $this->prepareUpdateItemsQuery($maskedQuoteId, (string) $quoteItem->getItemId(), $qty);
$response = $this->graphQlQuery($query);

$this->assertArrayHasKey('updateCartItems', $response);
$this->assertArrayHasKey('cart', $response['updateCartItems']);

$responseCart = $response['updateCartItems']['cart'];
$item = current($responseCart['items']);

$this->assertEquals($quoteItem->getItemId(), $item['id']);
$this->assertEquals($qty, $item['qty']);
}

/**
* @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php
*/
public function testRemoveCartItemByZeroQuantityUpdate()
{
$this->quoteResource->load(
$this->quote,
'test_order_with_simple_product_without_address',
'reserved_order_id'
);
$maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId());
$quoteItem = $this->quote->getItemByProduct($this->productRepository->get('simple'));

$query = $this->prepareUpdateItemsQuery($maskedQuoteId, (string) $quoteItem->getItemId(), 0);
$response = $this->graphQlQuery($query);

$this->assertArrayHasKey('updateCartItems', $response);
$this->assertArrayHasKey('cart', $response['updateCartItems']);

$responseCart = $response['updateCartItems']['cart'];
$this->assertCount(0, $responseCart['items']);
}

/**
* @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php
* @expectedException \Exception
* @expectedExceptionMessage Could not find cart item with id
*/
public function testUpdateCartItemNoSuchItemEntity()
{
$this->quoteResource->load(
$this->quote,
'test_order_with_simple_product_without_address',
'reserved_order_id'
);
$maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId());

$query = $this->prepareUpdateItemsQuery($maskedQuoteId, '999', 4);
$this->graphQlQuery($query);
}

private function prepareUpdateItemsQuery(string $maskedQuoteId, string $itemId, float $qty): string
{
return <<<QUERY
mutation {
updateCartItems(input:{
cart_id:"$maskedQuoteId"
cart_items:[
{
item_id:"$itemId"
qty: $qty
}
]
}) {
cart {
cart_id
items {
id
qty
}
}
}
}
QUERY;
}
}

0 comments on commit 37eacee

Please sign in to comment.