Skip to content

Commit

Permalink
ENGCOM-2967: #141 add simple product to cart #170
Browse files Browse the repository at this point in the history
 - Merge Pull Request magento/graphql-ce#170 from magento/graphql-ce:141-add-simple-product-to-cart
 - Merged commits:
   1. eea5cdd
   2. 50a928c
   3. 5105989
   4. 1734dfc
   5. 7e7e82c
   6. 01404a0
   7. 1064854
   8. 34e0106
   9. e8011a8
   10. 436dee7
   11. 4942c6f
   12. e901431
   13. 7990b83
   14. ada51dc
   15. 484c65c
   16. 6b7300a
   17. c21a016
   18. d39230e
   19. d43164b
   20. fa5940a
   21. c78b993
   22. 7e3c023
  • Loading branch information
magento-engcom-team committed Oct 2, 2018
2 parents 8460e4e + 7e3c023 commit 594f4b4
Show file tree
Hide file tree
Showing 21 changed files with 1,120 additions and 10 deletions.
67 changes: 67 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,67 @@
<?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;

/**
* @inheritdoc
*/
class DateType extends ProductDateOptionType
{
/**
* Make valid string as a value of date option type for GraphQl queries
*
* @param array $values All product option values, i.e. array (option_id => mixed, option_id => mixed...)
* @return ProductDateOptionType
*/
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
*/
private 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;
}
}
1 change: 1 addition & 0 deletions app/code/Magento/CatalogGraphQl/etc/graphql/di.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<preference for="Magento\Catalog\Model\Product\Option\Type\Date" type="Magento\CatalogGraphQl\Model\Product\Option\DateType" />
<type name="Magento\CatalogGraphQl\Model\ProductInterfaceTypeResolverComposite">
<arguments>
<argument name="productTypeNameResolvers" xsi:type="array">
Expand Down
138 changes: 138 additions & 0 deletions app/code/Magento/QuoteGraphQl/Model/Cart/AddProductsToCart.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\QuoteGraphQl\Model\Cart;

use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException;
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException;
use Magento\Framework\Message\AbstractMessage;
use Magento\Quote\Api\CartRepositoryInterface;
use Magento\Quote\Model\MaskedQuoteIdToQuoteIdInterface;
use Magento\Quote\Model\Quote;
use Magento\QuoteGraphQl\Model\Authorization\IsCartMutationAllowedForCurrentUser;

/**
* Add products to cart
*/
class AddProductsToCart
{
/**
* @var MaskedQuoteIdToQuoteIdInterface
*/
private $maskedQuoteIdToQuoteId;

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

/**
* @var IsCartMutationAllowedForCurrentUser
*/
private $isCartMutationAllowedForCurrentUser;

/**
* @var AddSimpleProductToCart
*/
private $addProductToCart;

/**
* @param MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId
* @param CartRepositoryInterface $cartRepository
* @param IsCartMutationAllowedForCurrentUser $isCartMutationAllowedForCurrentUser
* @param AddSimpleProductToCart $addProductToCart
*/
public function __construct(
MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId,
CartRepositoryInterface $cartRepository,
IsCartMutationAllowedForCurrentUser $isCartMutationAllowedForCurrentUser,
AddSimpleProductToCart $addProductToCart
) {
$this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId;
$this->cartRepository = $cartRepository;
$this->isCartMutationAllowedForCurrentUser = $isCartMutationAllowedForCurrentUser;
$this->addProductToCart = $addProductToCart;
}

/**
* Add products to cart
*
* @param string $cartHash
* @param array $cartItems
* @return Quote
* @throws GraphQlInputException
*/
public function execute(string $cartHash, array $cartItems): Quote
{
$cart = $this->getCart($cartHash);

foreach ($cartItems as $cartItemData) {
$this->addProductToCart->execute($cart, $cartItemData);
}

if ($cart->getData('has_error')) {
throw new GraphQlInputException(
__('Shopping cart error: %message', ['message' => $this->getCartErrors($cart)])
);
}

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

/**
* Get cart
*
* @param string $cartHash
* @return Quote
* @throws GraphQlNoSuchEntityException
* @throws GraphQlAuthorizationException
*/
private function getCart(string $cartHash): Quote
{
try {
$cartId = $this->maskedQuoteIdToQuoteId->execute($cartHash);
$cart = $this->cartRepository->get($cartId);
} catch (NoSuchEntityException $e) {
throw new GraphQlNoSuchEntityException(
__('Could not find a cart with ID "%masked_cart_id"', ['masked_cart_id' => $cartHash])
);
}

if (false === $this->isCartMutationAllowedForCurrentUser->execute($cartId)) {
throw new GraphQlAuthorizationException(
__(
'The current user cannot perform operations on cart "%masked_cart_id"',
['masked_cart_id' => $cartHash]
)
);
}

/** @var Quote $cart */
return $cart;
}

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

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

return implode(PHP_EOL, $errorMessages);
}
}
149 changes: 149 additions & 0 deletions app/code/Magento/QuoteGraphQl/Model/Cart/AddSimpleProductToCart.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
<?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\NoSuchEntityException;
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException;
use Magento\Framework\Stdlib\ArrayManager;
use Magento\Quote\Model\Quote;

/**
* Add simple product to cart
*
* TODO: should be replaced for different types resolver
*/
class AddSimpleProductToCart
{
/**
* @var ArrayManager
*/
private $arrayManager;

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

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

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

/**
* Add simple product to cart
*
* @param Quote $cart
* @param array $cartItemData
* @return void
* @throws GraphQlNoSuchEntityException
* @throws GraphQlInputException
*/
public function execute(Quote $cart, array $cartItemData): void
{
$sku = $this->extractSku($cartItemData);
$qty = $this->extractQty($cartItemData);
$customizableOptions = $this->extractCustomizableOptions($cartItemData);

try {
$product = $this->productRepository->get($sku);
} catch (NoSuchEntityException $e) {
throw new GraphQlNoSuchEntityException(__('Could not find a product with SKU "%sku"', ['sku' => $sku]));
}

$result = $cart->addProduct($product, $this->createBuyRequest($qty, $customizableOptions));

if (is_string($result)) {
throw new GraphQlInputException(__($result));
}
}

/**
* Extract SKU from cart item data
*
* @param array $cartItemData
* @return string
* @throws GraphQlInputException
*/
private function extractSku(array $cartItemData): string
{
$sku = $this->arrayManager->get('data/sku', $cartItemData);
if (!isset($sku)) {
throw new GraphQlInputException(__('Missing key "sku" in cart item data'));
}
return (string)$sku;
}

/**
* Extract Qty from cart item data
*
* @param array $cartItemData
* @return float
* @throws GraphQlInputException
*/
private function extractQty(array $cartItemData): float
{
$qty = $this->arrayManager->get('data/qty', $cartItemData);
if (!isset($qty)) {
throw new GraphQlInputException(__('Missing key "qty" in cart item data'));
}
return (float)$qty;
}

/**
* Extract Customizable Options from cart item data
*
* @param array $cartItemData
* @return array
*/
private function extractCustomizableOptions(array $cartItemData): array
{
$customizableOptions = $this->arrayManager->get('customizable_options', $cartItemData, []);

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

/**
* Format GraphQl input data to a shape that buy request has
*
* @param float $qty
* @param array $customOptions
* @return DataObject
*/
private function createBuyRequest(float $qty, array $customOptions): DataObject
{
return $this->dataObjectFactory->create([
'data' => [
'qty' => $qty,
'options' => $customOptions,
],
]);
}
}
Loading

0 comments on commit 594f4b4

Please sign in to comment.