Skip to content

Commit 7d5967f

Browse files
authored
Merge pull request #6690 from magento-honey-badgers/PWA-1326
[honey] PWA-1326: Implement the schema changes for Configurable Options Selection
2 parents 7a72b6b + 03da376 commit 7d5967f

File tree

16 files changed

+910
-78
lines changed

16 files changed

+910
-78
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace Magento\ConfigurableProductGraphQl\Model\Formatter;
10+
11+
use Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute;
12+
use Magento\Framework\GraphQl\Query\Uid;
13+
14+
/**
15+
* Formatter for configurable product options
16+
*/
17+
class Option
18+
{
19+
/**
20+
* @var Uid
21+
*/
22+
private $idEncoder;
23+
24+
/**
25+
* @var OptionValue
26+
*/
27+
private $valueFormatter;
28+
29+
/**
30+
* @param Uid $idEncoder
31+
* @param OptionValue $valueFormatter
32+
*/
33+
public function __construct(
34+
Uid $idEncoder,
35+
OptionValue $valueFormatter
36+
) {
37+
$this->idEncoder = $idEncoder;
38+
$this->valueFormatter = $valueFormatter;
39+
}
40+
41+
/**
42+
* Format configurable product options according to the GraphQL schema
43+
*
44+
* @param Attribute $attribute
45+
* @param array $optionIds
46+
* @return array|null
47+
*/
48+
public function format(Attribute $attribute, array $optionIds): ?array
49+
{
50+
$optionValues = [];
51+
52+
foreach ($attribute->getOptions() as $option) {
53+
$optionValues[] = $this->valueFormatter->format($option, $attribute, $optionIds);
54+
}
55+
56+
return [
57+
'uid' => $this->idEncoder->encode($attribute->getProductSuperAttributeId()),
58+
'attribute_code' => $attribute->getProductAttribute()->getAttributeCode(),
59+
'label' => $attribute->getLabel(),
60+
'values' => $optionValues,
61+
];
62+
}
63+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace Magento\ConfigurableProductGraphQl\Model\Formatter;
10+
11+
use Magento\CatalogInventory\Model\StockRegistry;
12+
use Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute;
13+
use Magento\ConfigurableProductGraphQl\Model\Options\SelectionUidFormatter;
14+
15+
/**
16+
* Formatter for configurable product option values
17+
*/
18+
class OptionValue
19+
{
20+
/**
21+
* @var SelectionUidFormatter
22+
*/
23+
private $selectionUidFormatter;
24+
25+
/**
26+
* @var StockRegistry
27+
*/
28+
private $stockRegistry;
29+
30+
/**
31+
* @param SelectionUidFormatter $selectionUidFormatter
32+
* @param StockRegistry $stockRegistry
33+
*/
34+
public function __construct(
35+
SelectionUidFormatter $selectionUidFormatter,
36+
StockRegistry $stockRegistry
37+
) {
38+
$this->selectionUidFormatter = $selectionUidFormatter;
39+
$this->stockRegistry = $stockRegistry;
40+
}
41+
42+
/**
43+
* Format configurable product option values according to the GraphQL schema
44+
*
45+
* @param array $optionValue
46+
* @param Attribute $attribute
47+
* @param array $optionIds
48+
* @return array
49+
*/
50+
public function format(array $optionValue, Attribute $attribute, array $optionIds): array
51+
{
52+
$valueIndex = (int)$optionValue['value_index'];
53+
$attributeId = (int)$attribute->getAttributeId();
54+
55+
return [
56+
'uid' => $this->selectionUidFormatter->encode(
57+
$attributeId,
58+
$valueIndex
59+
),
60+
'is_available' => $this->getIsAvailable($optionIds[$valueIndex] ?? []),
61+
'is_use_default' => (bool)$attribute->getIsUseDefault(),
62+
'label' => $optionValue['label'],
63+
'value_index' => $optionValue['value_index']
64+
];
65+
}
66+
67+
/**
68+
* Get is variants available
69+
*
70+
* @param array $variantIds
71+
* @return bool
72+
*/
73+
private function getIsAvailable(array $variantIds): bool
74+
{
75+
foreach ($variantIds as $variantId) {
76+
if ($this->stockRegistry->getProductStockStatus($variantId)) {
77+
return true;
78+
}
79+
}
80+
81+
return false;
82+
}
83+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace Magento\ConfigurableProductGraphQl\Model\Formatter;
10+
11+
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
12+
13+
/**
14+
* Formatter for configurable product variant
15+
*/
16+
class Variant
17+
{
18+
/**
19+
* Format selected variant of configurable product based on selected options
20+
*
21+
* @param array $options
22+
* @param array $selectedOptions
23+
* @param array $variants
24+
* @return array|null
25+
* @throws GraphQlInputException
26+
*/
27+
public function format(array $options, array $selectedOptions, array $variants): ?array
28+
{
29+
$variant = null;
30+
$productIds = array_keys($variants);
31+
32+
foreach ($selectedOptions as $attributeId => $selectedValue) {
33+
if (!isset($options[$attributeId][$selectedValue])) {
34+
throw new GraphQlInputException(__('configurableOptionValueUids values are incorrect'));
35+
}
36+
37+
$productIds = array_intersect($productIds, $options[$attributeId][$selectedValue]);
38+
}
39+
40+
if (count($productIds) === 1) {
41+
$variantProduct = $variants[array_pop($productIds)];
42+
$variant = $variantProduct->getData();
43+
$variant['url_path'] = $variantProduct->getProductUrl();
44+
$variant['model'] = $variantProduct;
45+
}
46+
47+
return $variant;
48+
}
49+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace Magento\ConfigurableProductGraphQl\Model\Options;
10+
11+
use Magento\Catalog\Api\Data\ProductInterface;
12+
use Magento\ConfigurableProduct\Helper\Data;
13+
use Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute;
14+
use Magento\ConfigurableProductGraphQl\Model\Formatter\Option;
15+
16+
/**
17+
* Retrieve metadata for configurable option selection.
18+
*/
19+
class ConfigurableOptionsMetadata
20+
{
21+
/**
22+
* @var Data
23+
*/
24+
private $configurableProductHelper;
25+
26+
/**
27+
* @var Option
28+
*/
29+
private $configurableOptionsFormatter;
30+
31+
/**
32+
* @param Data $configurableProductHelper
33+
* @param Option $configurableOptionsFormatter
34+
*/
35+
public function __construct(
36+
Data $configurableProductHelper,
37+
Option $configurableOptionsFormatter
38+
) {
39+
$this->configurableProductHelper = $configurableProductHelper;
40+
$this->configurableOptionsFormatter = $configurableOptionsFormatter;
41+
}
42+
43+
/**
44+
* Load available selections from configurable options and variant.
45+
*
46+
* @param ProductInterface $product
47+
* @param array $options
48+
* @param array $selectedOptions
49+
* @return array
50+
*/
51+
public function getAvailableSelections(ProductInterface $product, array $options, array $selectedOptions): array
52+
{
53+
$attributes = $this->getAttributes($product);
54+
$availableSelections = [];
55+
56+
foreach ($options as $attributeId => $option) {
57+
if ($attributeId === 'index' || isset($selectedOptions[$attributeId])) {
58+
continue;
59+
}
60+
61+
$availableSelections[] = $this->configurableOptionsFormatter->format(
62+
$attributes[$attributeId],
63+
$options[$attributeId] ?? []
64+
);
65+
}
66+
67+
return $availableSelections;
68+
}
69+
70+
/**
71+
* Retrieve configurable attributes for the product
72+
*
73+
* @param ProductInterface $product
74+
* @return Attribute[]
75+
*/
76+
private function getAttributes(ProductInterface $product): array
77+
{
78+
$allowedAttributes = $this->configurableProductHelper->getAllowAttributes($product);
79+
$attributes = [];
80+
foreach ($allowedAttributes as $attribute) {
81+
$attributes[$attribute->getAttributeId()] = $attribute;
82+
}
83+
84+
return $attributes;
85+
}
86+
}

app/code/Magento/ConfigurableProductGraphQl/Model/Options/DataProvider/Variant.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Magento\Catalog\Api\Data\ProductInterface;
1111
use Magento\CatalogInventory\Model\ResourceModel\Stock\StatusFactory;
1212
use Magento\ConfigurableProduct\Model\Product\Type\Configurable;
13+
use Magento\Framework\Exception\LocalizedException;
1314

1415
/**
1516
* Retrieve child products
@@ -45,7 +46,7 @@ public function __construct(
4546
* @return ProductInterface[]
4647
* @throws \Magento\Framework\Exception\LocalizedException
4748
*/
48-
public function getSalableVariantsByParent(ProductInterface $product)
49+
public function getSalableVariantsByParent(ProductInterface $product): array
4950
{
5051
$collection = $this->configurableType->getUsedProductCollection($product);
5152
$collection
@@ -62,6 +63,6 @@ public function getSalableVariantsByParent(ProductInterface $product)
6263
}
6364
$collection->clear();
6465

65-
return $collection->getItems();
66+
return $collection->getItems() ?? [];
6667
}
6768
}

app/code/Magento/ConfigurableProductGraphQl/Model/Options/SelectionUidFormatter.php

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,12 @@
33
* Copyright © Magento, Inc. All rights reserved.
44
* See COPYING.txt for license details.
55
*/
6+
67
namespace Magento\ConfigurableProductGraphQl\Model\Options;
78

9+
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
10+
use Magento\Framework\GraphQl\Query\Uid;
11+
812
/**
913
* Handle option selection uid.
1014
*/
@@ -20,6 +24,19 @@ class SelectionUidFormatter
2024
*/
2125
private const UID_SEPARATOR = '/';
2226

27+
/**
28+
* @var Uid
29+
*/
30+
private $idEncoder;
31+
32+
/**
33+
* @param Uid $idEncoder
34+
*/
35+
public function __construct(Uid $idEncoder)
36+
{
37+
$this->idEncoder = $idEncoder;
38+
}
39+
2340
/**
2441
* Create uid and encode.
2542
*
@@ -29,28 +46,22 @@ class SelectionUidFormatter
2946
*/
3047
public function encode(int $attributeId, int $indexId): string
3148
{
32-
// phpcs:ignore Magento2.Functions.DiscouragedFunction
33-
return base64_encode(implode(self::UID_SEPARATOR, [
34-
self::UID_PREFIX,
35-
$attributeId,
36-
$indexId
37-
]));
49+
return $this->idEncoder->encode(implode(self::UID_SEPARATOR, [self::UID_PREFIX, $attributeId, $indexId]));
3850
}
3951

4052
/**
4153
* Retrieve attribute and option index from uid. Array key is the id of attribute and value is the index of option
4254
*
43-
* @param string $selectionUids
55+
* @param array $selectionUids
4456
* @return array
45-
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
57+
* @throws GraphQlInputException
4658
*/
4759
public function extract(array $selectionUids): array
4860
{
4961
$attributeOption = [];
5062
foreach ($selectionUids as $uid) {
51-
// phpcs:ignore Magento2.Functions.DiscouragedFunction
52-
$optionData = explode(self::UID_SEPARATOR, base64_decode($uid));
53-
if (count($optionData) == 3) {
63+
$optionData = explode(self::UID_SEPARATOR, $this->idEncoder->decode($uid));
64+
if (count($optionData) === 3) {
5465
$attributeOption[(int)$optionData[1]] = (int)$optionData[2];
5566
}
5667
}

0 commit comments

Comments
 (0)