Skip to content

Commit 594f4b4

Browse files
ENGCOM-2967: #141 add simple product to cart #170
- 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
2 parents 8460e4e + 7e3c023 commit 594f4b4

21 files changed

+1120
-10
lines changed
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\CatalogGraphQl\Model\Product\Option;
9+
10+
use Magento\Catalog\Model\Product\Option\Type\Date as ProductDateOptionType;
11+
use Magento\Framework\Exception\LocalizedException;
12+
use Magento\Framework\Stdlib\DateTime;
13+
14+
/**
15+
* @inheritdoc
16+
*/
17+
class DateType extends ProductDateOptionType
18+
{
19+
/**
20+
* Make valid string as a value of date option type for GraphQl queries
21+
*
22+
* @param array $values All product option values, i.e. array (option_id => mixed, option_id => mixed...)
23+
* @return ProductDateOptionType
24+
*/
25+
public function validateUserValue($values)
26+
{
27+
if ($this->_dateExists() || $this->_timeExists()) {
28+
return parent::validateUserValue($this->formatValues($values));
29+
}
30+
31+
return $this;
32+
}
33+
34+
/**
35+
* Format date value from string to date array
36+
*
37+
* @param [] $values
38+
* @return []
39+
* @throws LocalizedException
40+
*/
41+
private function formatValues($values)
42+
{
43+
if (isset($values[$this->getOption()->getId()])) {
44+
$value = $values[$this->getOption()->getId()];
45+
$dateTime = \DateTime::createFromFormat(DateTime::DATETIME_PHP_FORMAT, $value);
46+
$values[$this->getOption()->getId()] = [
47+
'date' => $value,
48+
'year' => $dateTime->format('Y'),
49+
'month' => $dateTime->format('m'),
50+
'day' => $dateTime->format('d'),
51+
'hour' => $dateTime->format('H'),
52+
'minute' => $dateTime->format('i'),
53+
'day_part' => $dateTime->format('a'),
54+
];
55+
}
56+
57+
return $values;
58+
}
59+
60+
/**
61+
* @inheritdoc
62+
*/
63+
public function useCalendar()
64+
{
65+
return false;
66+
}
67+
}

app/code/Magento/CatalogGraphQl/etc/graphql/di.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77
-->
88
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
9+
<preference for="Magento\Catalog\Model\Product\Option\Type\Date" type="Magento\CatalogGraphQl\Model\Product\Option\DateType" />
910
<type name="Magento\CatalogGraphQl\Model\ProductInterfaceTypeResolverComposite">
1011
<arguments>
1112
<argument name="productTypeNameResolvers" xsi:type="array">
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\QuoteGraphQl\Model\Cart;
9+
10+
use Magento\Framework\Exception\NoSuchEntityException;
11+
use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException;
12+
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
13+
use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException;
14+
use Magento\Framework\Message\AbstractMessage;
15+
use Magento\Quote\Api\CartRepositoryInterface;
16+
use Magento\Quote\Model\MaskedQuoteIdToQuoteIdInterface;
17+
use Magento\Quote\Model\Quote;
18+
use Magento\QuoteGraphQl\Model\Authorization\IsCartMutationAllowedForCurrentUser;
19+
20+
/**
21+
* Add products to cart
22+
*/
23+
class AddProductsToCart
24+
{
25+
/**
26+
* @var MaskedQuoteIdToQuoteIdInterface
27+
*/
28+
private $maskedQuoteIdToQuoteId;
29+
30+
/**
31+
* @var CartRepositoryInterface
32+
*/
33+
private $cartRepository;
34+
35+
/**
36+
* @var IsCartMutationAllowedForCurrentUser
37+
*/
38+
private $isCartMutationAllowedForCurrentUser;
39+
40+
/**
41+
* @var AddSimpleProductToCart
42+
*/
43+
private $addProductToCart;
44+
45+
/**
46+
* @param MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId
47+
* @param CartRepositoryInterface $cartRepository
48+
* @param IsCartMutationAllowedForCurrentUser $isCartMutationAllowedForCurrentUser
49+
* @param AddSimpleProductToCart $addProductToCart
50+
*/
51+
public function __construct(
52+
MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId,
53+
CartRepositoryInterface $cartRepository,
54+
IsCartMutationAllowedForCurrentUser $isCartMutationAllowedForCurrentUser,
55+
AddSimpleProductToCart $addProductToCart
56+
) {
57+
$this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId;
58+
$this->cartRepository = $cartRepository;
59+
$this->isCartMutationAllowedForCurrentUser = $isCartMutationAllowedForCurrentUser;
60+
$this->addProductToCart = $addProductToCart;
61+
}
62+
63+
/**
64+
* Add products to cart
65+
*
66+
* @param string $cartHash
67+
* @param array $cartItems
68+
* @return Quote
69+
* @throws GraphQlInputException
70+
*/
71+
public function execute(string $cartHash, array $cartItems): Quote
72+
{
73+
$cart = $this->getCart($cartHash);
74+
75+
foreach ($cartItems as $cartItemData) {
76+
$this->addProductToCart->execute($cart, $cartItemData);
77+
}
78+
79+
if ($cart->getData('has_error')) {
80+
throw new GraphQlInputException(
81+
__('Shopping cart error: %message', ['message' => $this->getCartErrors($cart)])
82+
);
83+
}
84+
85+
$this->cartRepository->save($cart);
86+
return $cart;
87+
}
88+
89+
/**
90+
* Get cart
91+
*
92+
* @param string $cartHash
93+
* @return Quote
94+
* @throws GraphQlNoSuchEntityException
95+
* @throws GraphQlAuthorizationException
96+
*/
97+
private function getCart(string $cartHash): Quote
98+
{
99+
try {
100+
$cartId = $this->maskedQuoteIdToQuoteId->execute($cartHash);
101+
$cart = $this->cartRepository->get($cartId);
102+
} catch (NoSuchEntityException $e) {
103+
throw new GraphQlNoSuchEntityException(
104+
__('Could not find a cart with ID "%masked_cart_id"', ['masked_cart_id' => $cartHash])
105+
);
106+
}
107+
108+
if (false === $this->isCartMutationAllowedForCurrentUser->execute($cartId)) {
109+
throw new GraphQlAuthorizationException(
110+
__(
111+
'The current user cannot perform operations on cart "%masked_cart_id"',
112+
['masked_cart_id' => $cartHash]
113+
)
114+
);
115+
}
116+
117+
/** @var Quote $cart */
118+
return $cart;
119+
}
120+
121+
/**
122+
* Collecting cart errors
123+
*
124+
* @param Quote $cart
125+
* @return string
126+
*/
127+
private function getCartErrors(Quote $cart): string
128+
{
129+
$errorMessages = [];
130+
131+
/** @var AbstractMessage $error */
132+
foreach ($cart->getErrors() as $error) {
133+
$errorMessages[] = $error->getText();
134+
}
135+
136+
return implode(PHP_EOL, $errorMessages);
137+
}
138+
}
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\QuoteGraphQl\Model\Cart;
9+
10+
use Magento\Catalog\Api\ProductRepositoryInterface;
11+
use Magento\Framework\DataObject;
12+
use Magento\Framework\DataObjectFactory;
13+
use Magento\Framework\Exception\NoSuchEntityException;
14+
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
15+
use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException;
16+
use Magento\Framework\Stdlib\ArrayManager;
17+
use Magento\Quote\Model\Quote;
18+
19+
/**
20+
* Add simple product to cart
21+
*
22+
* TODO: should be replaced for different types resolver
23+
*/
24+
class AddSimpleProductToCart
25+
{
26+
/**
27+
* @var ArrayManager
28+
*/
29+
private $arrayManager;
30+
31+
/**
32+
* @var DataObjectFactory
33+
*/
34+
private $dataObjectFactory;
35+
36+
/**
37+
* @var ProductRepositoryInterface
38+
*/
39+
private $productRepository;
40+
41+
/**
42+
* @param ArrayManager $arrayManager
43+
* @param DataObjectFactory $dataObjectFactory
44+
* @param ProductRepositoryInterface $productRepository
45+
*/
46+
public function __construct(
47+
ArrayManager $arrayManager,
48+
DataObjectFactory $dataObjectFactory,
49+
ProductRepositoryInterface $productRepository
50+
) {
51+
$this->arrayManager = $arrayManager;
52+
$this->dataObjectFactory = $dataObjectFactory;
53+
$this->productRepository = $productRepository;
54+
}
55+
56+
/**
57+
* Add simple product to cart
58+
*
59+
* @param Quote $cart
60+
* @param array $cartItemData
61+
* @return void
62+
* @throws GraphQlNoSuchEntityException
63+
* @throws GraphQlInputException
64+
*/
65+
public function execute(Quote $cart, array $cartItemData): void
66+
{
67+
$sku = $this->extractSku($cartItemData);
68+
$qty = $this->extractQty($cartItemData);
69+
$customizableOptions = $this->extractCustomizableOptions($cartItemData);
70+
71+
try {
72+
$product = $this->productRepository->get($sku);
73+
} catch (NoSuchEntityException $e) {
74+
throw new GraphQlNoSuchEntityException(__('Could not find a product with SKU "%sku"', ['sku' => $sku]));
75+
}
76+
77+
$result = $cart->addProduct($product, $this->createBuyRequest($qty, $customizableOptions));
78+
79+
if (is_string($result)) {
80+
throw new GraphQlInputException(__($result));
81+
}
82+
}
83+
84+
/**
85+
* Extract SKU from cart item data
86+
*
87+
* @param array $cartItemData
88+
* @return string
89+
* @throws GraphQlInputException
90+
*/
91+
private function extractSku(array $cartItemData): string
92+
{
93+
$sku = $this->arrayManager->get('data/sku', $cartItemData);
94+
if (!isset($sku)) {
95+
throw new GraphQlInputException(__('Missing key "sku" in cart item data'));
96+
}
97+
return (string)$sku;
98+
}
99+
100+
/**
101+
* Extract Qty from cart item data
102+
*
103+
* @param array $cartItemData
104+
* @return float
105+
* @throws GraphQlInputException
106+
*/
107+
private function extractQty(array $cartItemData): float
108+
{
109+
$qty = $this->arrayManager->get('data/qty', $cartItemData);
110+
if (!isset($qty)) {
111+
throw new GraphQlInputException(__('Missing key "qty" in cart item data'));
112+
}
113+
return (float)$qty;
114+
}
115+
116+
/**
117+
* Extract Customizable Options from cart item data
118+
*
119+
* @param array $cartItemData
120+
* @return array
121+
*/
122+
private function extractCustomizableOptions(array $cartItemData): array
123+
{
124+
$customizableOptions = $this->arrayManager->get('customizable_options', $cartItemData, []);
125+
126+
$customizableOptionsData = [];
127+
foreach ($customizableOptions as $customizableOption) {
128+
$customizableOptionsData[$customizableOption['id']] = $customizableOption['value'];
129+
}
130+
return $customizableOptionsData;
131+
}
132+
133+
/**
134+
* Format GraphQl input data to a shape that buy request has
135+
*
136+
* @param float $qty
137+
* @param array $customOptions
138+
* @return DataObject
139+
*/
140+
private function createBuyRequest(float $qty, array $customOptions): DataObject
141+
{
142+
return $this->dataObjectFactory->create([
143+
'data' => [
144+
'qty' => $qty,
145+
'options' => $customOptions,
146+
],
147+
]);
148+
}
149+
}

0 commit comments

Comments
 (0)