Skip to content

Commit 794aee6

Browse files
ENGCOM-5796: GraphQl-143: [Checkout] Add bundled product to Cart #396
- Merge Pull Request magento/graphql-ce#396 from pmclain/graphql-ce:feature/bundle-cart - Merged commits: 1. 8b56ad0 2. 77b636e 3. 82fc826 4. 9077c1c 5. 4387331 6. 840fbed 7. 989038c 8. 226cbf7 9. 1f2aeda 10. 76a8f29 11. 5525815 12. 019f8dc 13. ff59f00 14. be26487 15. a3fe77f 16. 45742cd 17. 9e3975c 18. f3f882a 19. 22489c2
2 parents 6f1c7e3 + 22489c2 commit 794aee6

File tree

13 files changed

+589
-4
lines changed

13 files changed

+589
-4
lines changed
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
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\BundleGraphQl\Model\Cart;
9+
10+
use Magento\Bundle\Helper\Catalog\Product\Configuration;
11+
use Magento\Catalog\Model\Product;
12+
use Magento\Quote\Model\Quote\Item;
13+
use Magento\Framework\Pricing\Helper\Data;
14+
use Magento\Framework\Serialize\SerializerInterface;
15+
16+
/**
17+
* Data provider for bundled product options
18+
*/
19+
class BundleOptionDataProvider
20+
{
21+
/**
22+
* @var Data
23+
*/
24+
private $pricingHelper;
25+
26+
/**
27+
* @var SerializerInterface
28+
*/
29+
private $serializer;
30+
31+
/**
32+
* @var Configuration
33+
*/
34+
private $configuration;
35+
36+
/**
37+
* @param Data $pricingHelper
38+
* @param SerializerInterface $serializer
39+
* @param Configuration $configuration
40+
*/
41+
public function __construct(
42+
Data $pricingHelper,
43+
SerializerInterface $serializer,
44+
Configuration $configuration
45+
) {
46+
$this->pricingHelper = $pricingHelper;
47+
$this->serializer = $serializer;
48+
$this->configuration = $configuration;
49+
}
50+
51+
/**
52+
* Extract data for a bundled cart item
53+
*
54+
* @param Item $item
55+
* @return array
56+
*/
57+
public function getData(Item $item): array
58+
{
59+
$options = [];
60+
$product = $item->getProduct();
61+
62+
/** @var \Magento\Bundle\Model\Product\Type $typeInstance */
63+
$typeInstance = $product->getTypeInstance();
64+
65+
$optionsQuoteItemOption = $item->getOptionByCode('bundle_option_ids');
66+
$bundleOptionsIds = $optionsQuoteItemOption
67+
? $this->serializer->unserialize($optionsQuoteItemOption->getValue())
68+
: [];
69+
70+
if ($bundleOptionsIds) {
71+
/** @var \Magento\Bundle\Model\ResourceModel\Option\Collection $optionsCollection */
72+
$optionsCollection = $typeInstance->getOptionsByIds($bundleOptionsIds, $product);
73+
74+
$selectionsQuoteItemOption = $item->getOptionByCode('bundle_selection_ids');
75+
76+
$bundleSelectionIds = $this->serializer->unserialize($selectionsQuoteItemOption->getValue());
77+
78+
if (!empty($bundleSelectionIds)) {
79+
$selectionsCollection = $typeInstance->getSelectionsByIds($bundleSelectionIds, $product);
80+
$bundleOptions = $optionsCollection->appendSelections($selectionsCollection, true);
81+
82+
$options = $this->buildBundleOptions($bundleOptions, $item);
83+
}
84+
}
85+
86+
return $options;
87+
}
88+
89+
/**
90+
* Build bundle product options based on current selection
91+
*
92+
* @param \Magento\Bundle\Model\Option[] $bundleOptions
93+
* @param Item $item
94+
* @return array
95+
*/
96+
private function buildBundleOptions(array $bundleOptions, Item $item): array
97+
{
98+
$options = [];
99+
foreach ($bundleOptions as $bundleOption) {
100+
if (!$bundleOption->getSelections()) {
101+
continue;
102+
}
103+
104+
$options[] = [
105+
'id' => $bundleOption->getId(),
106+
'label' => $bundleOption->getTitle(),
107+
'type' => $bundleOption->getType(),
108+
'values' => $this->buildBundleOptionValues($bundleOption->getSelections(), $item),
109+
];
110+
}
111+
112+
return $options;
113+
}
114+
115+
/**
116+
* Build bundle product option values based on current selection
117+
*
118+
* @param Product[] $selections
119+
* @param Item $item
120+
* @return array
121+
*/
122+
private function buildBundleOptionValues(array $selections, Item $item): array
123+
{
124+
$values = [];
125+
126+
$product = $item->getProduct();
127+
foreach ($selections as $selection) {
128+
$qty = (float) $this->configuration->getSelectionQty($product, $selection->getSelectionId());
129+
if (!$qty) {
130+
continue;
131+
}
132+
133+
$selectionPrice = $this->configuration->getSelectionFinalPrice($item, $selection);
134+
135+
$values[] = [
136+
'id' => $selection->getSelectionId(),
137+
'label' => $selection->getName(),
138+
'quantity' => $qty,
139+
'price' => $this->pricingHelper->currency($selectionPrice, false, false),
140+
];
141+
}
142+
143+
return $values;
144+
}
145+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
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\BundleGraphQl\Model\Cart\BuyRequest;
9+
10+
use Magento\Framework\Stdlib\ArrayManager;
11+
use Magento\QuoteGraphQl\Model\Cart\BuyRequest\BuyRequestDataProviderInterface;
12+
13+
/**
14+
* Data provider for bundle product buy requests
15+
*/
16+
class BundleDataProvider implements BuyRequestDataProviderInterface
17+
{
18+
/**
19+
* @var ArrayManager
20+
*/
21+
private $arrayManager;
22+
23+
/**
24+
* @param ArrayManager $arrayManager
25+
*/
26+
public function __construct(
27+
ArrayManager $arrayManager
28+
) {
29+
$this->arrayManager = $arrayManager;
30+
}
31+
32+
/**
33+
* @inheritdoc
34+
*/
35+
public function execute(array $cartItemData): array
36+
{
37+
$bundleOptions = [];
38+
$bundleInputs = $this->arrayManager->get('bundle_options', $cartItemData) ?? [];
39+
foreach ($bundleInputs as $bundleInput) {
40+
$bundleOptions['bundle_option'][$bundleInput['id']] = $bundleInput['value'];
41+
$bundleOptions['bundle_option_qty'][$bundleInput['id']] = $bundleInput['quantity'];
42+
}
43+
44+
return $bundleOptions;
45+
}
46+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
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\BundleGraphQl\Model\Resolver;
9+
10+
use Magento\BundleGraphQl\Model\Cart\BundleOptionDataProvider;
11+
use Magento\Framework\GraphQl\Config\Element\Field;
12+
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
13+
use Magento\Framework\GraphQl\Query\ResolverInterface;
14+
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
15+
16+
/**
17+
* Resolver for bundle product options
18+
*/
19+
class BundleOption implements ResolverInterface
20+
{
21+
/**
22+
* @var BundleOptionDataProvider
23+
*/
24+
private $dataProvider;
25+
26+
/**
27+
* @param BundleOptionDataProvider $bundleOptionDataProvider
28+
*/
29+
public function __construct(
30+
BundleOptionDataProvider $bundleOptionDataProvider
31+
) {
32+
$this->dataProvider = $bundleOptionDataProvider;
33+
}
34+
35+
/**
36+
* @inheritdoc
37+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
38+
*/
39+
public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null)
40+
{
41+
if (!isset($value['model'])) {
42+
throw new GraphQlInputException(__('Value must contain "model" property.'));
43+
}
44+
return $this->dataProvider->getData($value['model']);
45+
}
46+
}

app/code/Magento/BundleGraphQl/composer.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
"magento/module-catalog": "*",
88
"magento/module-bundle": "*",
99
"magento/module-catalog-graph-ql": "*",
10+
"magento/module-quote": "*",
11+
"magento/module-quote-graph-ql": "*",
1012
"magento/module-store": "*",
1113
"magento/framework": "*"
1214
},

app/code/Magento/BundleGraphQl/etc/di.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,11 @@
1616
</argument>
1717
</arguments>
1818
</type>
19+
<type name="Magento\QuoteGraphQl\Model\Resolver\CartItemTypeResolver">
20+
<arguments>
21+
<argument name="supportedTypes" xsi:type="array">
22+
<item name="bundle" xsi:type="string">BundleCartItem</item>
23+
</argument>
24+
</arguments>
25+
</type>
1926
</config>

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,13 @@
1313
</argument>
1414
</arguments>
1515
</type>
16+
<type name="Magento\QuoteGraphQl\Model\Cart\BuyRequest\BuyRequestBuilder">
17+
<arguments>
18+
<argument name="providers" xsi:type="array">
19+
<item name="bundle" xsi:type="object">Magento\BundleGraphQl\Model\Cart\BuyRequest\BundleDataProvider</item>
20+
</argument>
21+
</arguments>
22+
</type>
1623
<type name="Magento\Framework\GraphQl\Schema\Type\Enum\DefaultDataMapper">
1724
<arguments>
1825
<argument name="map" xsi:type="array">

app/code/Magento/BundleGraphQl/etc/module.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
99
<module name="Magento_BundleGraphQl" >
1010
<sequence>
11+
<module name="Magento_QuoteGraphQl"/>
1112
<module name="Magento_Catalog"/>
1213
<module name="Magento_BundleProduct"/>
1314
<module name="Magento_Store"/>

app/code/Magento/BundleGraphQl/etc/schema.graphqls

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,50 @@
11
# Copyright © Magento, Inc. All rights reserved.
22
# See COPYING.txt for license details.
33

4+
type Mutation {
5+
addBundleProductsToCart(input: AddBundleProductsToCartInput): AddBundleProductsToCartOutput @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\AddSimpleProductsToCart")
6+
}
7+
8+
input AddBundleProductsToCartInput {
9+
cart_id: String!
10+
cart_items: [BundleProductCartItemInput!]!
11+
}
12+
13+
input BundleProductCartItemInput {
14+
data: CartItemInput!
15+
bundle_options:[BundleOptionInput!]!
16+
customizable_options:[CustomizableOptionInput!]
17+
}
18+
19+
input BundleOptionInput {
20+
id: Int!
21+
quantity: Float!
22+
value: [String!]!
23+
}
24+
25+
type AddBundleProductsToCartOutput {
26+
cart: Cart!
27+
}
28+
29+
type BundleCartItem implements CartItemInterface {
30+
customizable_options: [SelectedCustomizableOption]! @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\CustomizableOptions")
31+
bundle_options: [SelectedBundleOption!]! @resolver(class: "Magento\\BundleGraphQl\\Model\\Resolver\\BundleOption")
32+
}
33+
34+
type SelectedBundleOption {
35+
id: Int!
36+
label: String!
37+
type: String!
38+
values: [SelectedBundleOptionValue!]!
39+
}
40+
41+
type SelectedBundleOptionValue {
42+
id: Int!
43+
label: String!
44+
quantity: Float!
45+
price: Float!
46+
}
47+
448
type BundleItem @doc(description: "BundleItem defines an individual item in a bundle product.") {
549
option_id: Int @doc(description: "An ID assigned to each type of item in a bundle product.")
650
title: String @doc(description: "The display name of the item.")

app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/BuyRequestDataProviderInterface.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@
1313
interface BuyRequestDataProviderInterface
1414
{
1515
/**
16-
* Build buy request for adding product to cart
16+
* Provide buy request data from add to cart item request
1717
*
1818
* @param array $cartItemData
19-
* @return DataObject
19+
* @return array
2020
*/
2121
public function execute(array $cartItemData): array;
2222
}

app/code/Magento/QuoteGraphQl/Model/Resolver/UpdateCartItems.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,11 @@ private function processCartItems(Quote $cart, array $items): void
116116
$itemId = (int)$item['cart_item_id'];
117117
$customizableOptions = $item['customizable_options'] ?? [];
118118

119+
$cartItem = $cart->getItemById($itemId);
120+
if ($cartItem && $cartItem->getParentItemId()) {
121+
throw new GraphQlInputException(__('Child items may not be updated.'));
122+
}
123+
119124
if (count($customizableOptions) === 0 && !isset($item['quantity'])) {
120125
throw new GraphQlInputException(__('Required parameter "quantity" for "cart_items" is missing.'));
121126
}

0 commit comments

Comments
 (0)