Skip to content

Commit f467d9d

Browse files
author
Valeriy Naida
authored
ENGCOM-2967: #141 add simple product to cart #170
2 parents e49cb7d + 594f4b4 commit f467d9d

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)