Skip to content
This repository has been archived by the owner on Dec 19, 2019. It is now read-only.

#141 add simple product to cart #170

Merged
merged 22 commits into from
Oct 3, 2018
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
eea5cdd
#141 Added add simple product to cart mutation to quote-graphql module
Sep 7, 2018
50a928c
#141 Added required configurations
Sep 7, 2018
5105989
#141 Added hard dependencies
Sep 7, 2018
1734dfc
#141 Fixed issues from Travis
Sep 7, 2018
7e7e82c
#141 Running tests again
Sep 7, 2018
01404a0
#141 Added descriptions to classes and methods
Sep 17, 2018
1064854
#141 Performed decoupling of the resolvers
Sep 18, 2018
34e0106
#141 Added missed lines in phpdocs
Sep 19, 2018
e8011a8
#141 Removed redundant dependency
Sep 19, 2018
436dee7
#141 Restored config after patching the branch
Sep 20, 2018
4942c6f
#141 Decoupling the CustomOption data provider
Sep 20, 2018
e901431
Merge remote-tracking branch 'origin/2.3-develop' into 141-add-simple…
Sep 25, 2018
7990b83
GraphQL-141: [Mutations] Cart Operations > Add simple product to Cart
Sep 26, 2018
ada51dc
GraphQL-141: [Mutations] Cart Operations > Add simple product to Cart
Sep 26, 2018
484c65c
GraphQL-141: [Mutations] Cart Operations > Add simple product to Cart
Sep 26, 2018
6b7300a
GraphQL-141: [Mutations] Cart Operations > Add simple product to Cart
Sep 26, 2018
c21a016
GraphQL-141: [Mutations] Cart Operations > Add simple product to Cart
Sep 26, 2018
d39230e
GraphQL-141: [Mutations] Cart Operations > Add simple product to Cart
Sep 26, 2018
d43164b
GraphQL-141: [Mutations] Cart Operations > Add simple product to Cart
Oct 1, 2018
fa5940a
Merge remote-tracking branch 'origin/2.3-develop' into 141-add-simple…
Oct 1, 2018
c78b993
GraphQL-141: [Mutations] Cart Operations > Add simple product to Cart
Oct 2, 2018
7e3c023
GraphQL-141: [Mutations] Cart Operations > Add simple product to Cart
Oct 2, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions app/code/Magento/CatalogGraphQl/Model/Product/Option/DateType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\CatalogGraphQl\Model\Product\Option;

use Magento\Catalog\Model\Product\Option\Type\Date as ProductDateOptionType;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Stdlib\DateTime;

/**
* Catalog product option date validator
* {@inheritdoc}
*/
class DateType extends ProductDateOptionType
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this one needed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason why it's needed is ProductDateOptionType wants to see date value as an associate array (like ['year' => '2018', 'month' => '1', 'day' => '1']). By this changes, we allow to use string as a date value. This meets our schema shape.
The same approach is used in REST API.

{
/**
* Make valid string as a value of date option type for GraphQl queries
* {@inheritdoc}
*/
public function validateUserValue($values)
{
if ($this->_dateExists() || $this->_timeExists()) {
return parent::validateUserValue($this->formatValues($values));
}

return $this;
}

/**
* Format date value from string to date array
*
* @param [] $values
* @return []
* @throws LocalizedException
*/
protected function formatValues($values)
{
if (isset($values[$this->getOption()->getId()])) {
$value = $values[$this->getOption()->getId()];
$dateTime = \DateTime::createFromFormat(DateTime::DATETIME_PHP_FORMAT, $value);
$values[$this->getOption()->getId()] = [
'date' => $value,
'year' => $dateTime->format('Y'),
'month' => $dateTime->format('m'),
'day' => $dateTime->format('d'),
'hour' => $dateTime->format('H'),
'minute' => $dateTime->format('i'),
'day_part' => $dateTime->format('a'),
];
}

return $values;
}

/**
* @inheritdoc
*/
public function useCalendar()
{
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\QuoteGraphQl\Model\Cart;

use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Framework\DataObject;
use Magento\Framework\DataObjectFactory;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\Stdlib\ArrayManager;
use Magento\Quote\Api\CartRepositoryInterface;
use Magento\Quote\Api\Data\CartInterface;
use Magento\Quote\Api\GuestCartRepositoryInterface;
use Magento\Quote\Model\MaskedQuoteIdToQuoteIdInterface;
use Magento\Quote\Model\Quote;
use Magento\Quote\Model\Quote\Item as QuoteItem;

/**
* Add simple product to cart process
*/
class AddSimpleProductToCartProcessor
{
/**
* @var ArrayManager
*/
private $arrayManager;

/**
* @var CartRepositoryInterface
*/
private $cartRepository;

/**
* @var MaskedQuoteIdToQuoteIdInterface
*/
private $maskedQuoteIdToQuoteId;

/**
* @var DataObjectFactory
*/
private $dataObjectFactory;

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

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

/**
* @param ArrayManager $arrayManager
* @param DataObjectFactory $dataObjectFactory
* @param MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId
* @param CartRepositoryInterface $cartRepository
* @param GuestCartRepositoryInterface $guestCartRepository
* @param ProductRepositoryInterface $productRepository
*/
public function __construct(
ArrayManager $arrayManager,
DataObjectFactory $dataObjectFactory,
MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId,
CartRepositoryInterface $cartRepository,
GuestCartRepositoryInterface $guestCartRepository,
ProductRepositoryInterface $productRepository
) {
$this->productRepository = $productRepository;
$this->guestCartRepository = $guestCartRepository;
$this->dataObjectFactory = $dataObjectFactory;
$this->cartRepository = $cartRepository;
$this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId;
$this->arrayManager = $arrayManager;
}

/**
* Resolve adding simple product to cart for customers/guests
*
* @param CartInterface|Quote $cart
* @param array $cartItemData
* @return QuoteItem|string
* @throws LocalizedException
* @throws NoSuchEntityException
*/
public function process($cart, array $cartItemData)
{
$sku = $this->arrayManager->get('details/sku', $cartItemData);
$product = $this->productRepository->get($sku);

return $cart->addProduct($product, $this->getBuyRequest($cartItemData));
}

/**
* Format GraphQl input data to a shape that buy request has
*
* @param array $cartItem
* @return DataObject
*/
private function getBuyRequest(array $cartItem): DataObject
{
$customOptions = [];
$qty = $this->arrayManager->get('details/qty', $cartItem);
$customizableOptions = $this->arrayManager->get('customizable_options', $cartItem, []);

foreach ($customizableOptions as $customizableOption) {
$customOptions[$customizableOption['id']] = $customizableOption['value'];
}

return $this->dataObjectFactory->create([
'data' => [
'qty' => $qty,
'options' => $customOptions
]
]);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\QuoteGraphQl\Model\Resolver\Cart;

use Magento\Authorization\Model\UserContextInterface;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
use Magento\Framework\GraphQl\Query\Resolver\Value;
use Magento\Framework\GraphQl\Query\Resolver\ValueFactory;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\Framework\Message\AbstractMessage;
use Magento\Framework\Stdlib\ArrayManager;
use Magento\Quote\Api\CartRepositoryInterface;
use Magento\Quote\Api\Data\CartInterface;
use Magento\Quote\Model\MaskedQuoteIdToQuoteIdInterface;
use Magento\Quote\Model\Quote;
use Magento\QuoteGraphQl\Model\Cart\AddSimpleProductToCartProcessor;
use Magento\QuoteGraphQl\Model\Resolver\DataProvider\Cart\CartHydrator;

/**
* Add simple product to cart GraphQl resolver
* {@inheritdoc}
*/
class AddSimpleProductsToCart implements ResolverInterface
{
/**
* @var AddSimpleProductToCartProcessor
*/
private $addSimpleProductToCartProcessor;

/**
* @var CartRepositoryInterface
*/
private $cartRepository;

/**
* @var MaskedQuoteIdToQuoteIdInterface
*/
private $maskedQuoteIdToQuoteId;

/**
* @var CartHydrator
*/
private $cartHydrator;

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

/**
* @var ValueFactory
*/
private $valueFactory;

/**
* @var UserContextInterface
*/
private $userContext;

/**
* @param AddSimpleProductToCartProcessor $addSimpleProductToCartProcessor
* @param CartHydrator $cartHydrator
* @param ArrayManager $arrayManager
* @param MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId
* @param CartRepositoryInterface $cartRepository
* @param ValueFactory $valueFactory
* @param UserContextInterface $userContext
*/
public function __construct(
AddSimpleProductToCartProcessor $addSimpleProductToCartProcessor,
CartHydrator $cartHydrator,
ArrayManager $arrayManager,
MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId,
CartRepositoryInterface $cartRepository,
ValueFactory $valueFactory,
UserContextInterface $userContext
) {
$this->valueFactory = $valueFactory;
$this->userContext = $userContext;
$this->arrayManager = $arrayManager;
$this->cartHydrator = $cartHydrator;
$this->cartRepository = $cartRepository;
$this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId;
$this->addSimpleProductToCartProcessor = $addSimpleProductToCartProcessor;
}

/**
* Resolve adding simple product to cart for customers/guests
* {@inheritdoc}
*/
public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) : Value
{
$cartHash = $this->arrayManager->get('input/cart_id', $args);
$cartItems = $this->arrayManager->get('input/cartItems', $args);

if (!isset($cartHash)) {
throw new GraphQlInputException(
__('Missing key %1 in cart data', ['cart_id'])
);
}

if (!isset($cartItems)) {
throw new GraphQlInputException(
__('Missing key %1 in cart data', ['cartItems'])
);
}

$cart = $this->getCart((string) $cartHash);

foreach ($cartItems as $cartItemData) {
$sku = $this->arrayManager->get('details/sku', $cartItemData);

$message = $this->addSimpleProductToCartProcessor->process($cart, $cartItemData);

if (is_string($message)) {
throw new GraphQlInputException(
__('%1: %2', $sku, $message)
);
}

if ($cart->getData('has_error')) {
throw new GraphQlInputException(
__('%1: %2', $sku, $this->getCartErrors($cart))
);
}
}

$this->cartRepository->save($cart);

$result = function () use ($cart) {
return [
'cart' => $this->cartHydrator->hydrate($cart)
];
};

return $this->valueFactory->create($result);
}

/**
* Collecting cart errors
*
* @param CartInterface|Quote $cart
* @return string
*/
private function getCartErrors($cart): string
{
$errorMessages = [];

/** @var AbstractMessage $error */
foreach ($cart->getErrors() as $error) {
$errorMessages[] = $error->getText();
}

return implode(PHP_EOL, $errorMessages);
}

/**
* Retrieving quote mode based on customer authorization
*
* @param string $cartHash
* @return CartInterface|Quote
* @throws NoSuchEntityException
*/
private function getCart(string $cartHash): CartInterface
{
$cartId = $this->maskedQuoteIdToQuoteId->execute((string) $cartHash);

return $this->cartRepository->get($cartId);
}
}
Loading