Skip to content

Commit

Permalink
Merge pull request #6690 from magento-honey-badgers/PWA-1326
Browse files Browse the repository at this point in the history
[honey] PWA-1326: Implement the schema changes for Configurable Options Selection
  • Loading branch information
cpartica authored Mar 12, 2021
2 parents 7a72b6b + 03da376 commit 7d5967f
Show file tree
Hide file tree
Showing 16 changed files with 910 additions and 78 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/

declare(strict_types=1);

namespace Magento\ConfigurableProductGraphQl\Model\Formatter;

use Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute;
use Magento\Framework\GraphQl\Query\Uid;

/**
* Formatter for configurable product options
*/
class Option
{
/**
* @var Uid
*/
private $idEncoder;

/**
* @var OptionValue
*/
private $valueFormatter;

/**
* @param Uid $idEncoder
* @param OptionValue $valueFormatter
*/
public function __construct(
Uid $idEncoder,
OptionValue $valueFormatter
) {
$this->idEncoder = $idEncoder;
$this->valueFormatter = $valueFormatter;
}

/**
* Format configurable product options according to the GraphQL schema
*
* @param Attribute $attribute
* @param array $optionIds
* @return array|null
*/
public function format(Attribute $attribute, array $optionIds): ?array
{
$optionValues = [];

foreach ($attribute->getOptions() as $option) {
$optionValues[] = $this->valueFormatter->format($option, $attribute, $optionIds);
}

return [
'uid' => $this->idEncoder->encode($attribute->getProductSuperAttributeId()),
'attribute_code' => $attribute->getProductAttribute()->getAttributeCode(),
'label' => $attribute->getLabel(),
'values' => $optionValues,
];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/

declare(strict_types=1);

namespace Magento\ConfigurableProductGraphQl\Model\Formatter;

use Magento\CatalogInventory\Model\StockRegistry;
use Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute;
use Magento\ConfigurableProductGraphQl\Model\Options\SelectionUidFormatter;

/**
* Formatter for configurable product option values
*/
class OptionValue
{
/**
* @var SelectionUidFormatter
*/
private $selectionUidFormatter;

/**
* @var StockRegistry
*/
private $stockRegistry;

/**
* @param SelectionUidFormatter $selectionUidFormatter
* @param StockRegistry $stockRegistry
*/
public function __construct(
SelectionUidFormatter $selectionUidFormatter,
StockRegistry $stockRegistry
) {
$this->selectionUidFormatter = $selectionUidFormatter;
$this->stockRegistry = $stockRegistry;
}

/**
* Format configurable product option values according to the GraphQL schema
*
* @param array $optionValue
* @param Attribute $attribute
* @param array $optionIds
* @return array
*/
public function format(array $optionValue, Attribute $attribute, array $optionIds): array
{
$valueIndex = (int)$optionValue['value_index'];
$attributeId = (int)$attribute->getAttributeId();

return [
'uid' => $this->selectionUidFormatter->encode(
$attributeId,
$valueIndex
),
'is_available' => $this->getIsAvailable($optionIds[$valueIndex] ?? []),
'is_use_default' => (bool)$attribute->getIsUseDefault(),
'label' => $optionValue['label'],
'value_index' => $optionValue['value_index']
];
}

/**
* Get is variants available
*
* @param array $variantIds
* @return bool
*/
private function getIsAvailable(array $variantIds): bool
{
foreach ($variantIds as $variantId) {
if ($this->stockRegistry->getProductStockStatus($variantId)) {
return true;
}
}

return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/

declare(strict_types=1);

namespace Magento\ConfigurableProductGraphQl\Model\Formatter;

use Magento\Framework\GraphQl\Exception\GraphQlInputException;

/**
* Formatter for configurable product variant
*/
class Variant
{
/**
* Format selected variant of configurable product based on selected options
*
* @param array $options
* @param array $selectedOptions
* @param array $variants
* @return array|null
* @throws GraphQlInputException
*/
public function format(array $options, array $selectedOptions, array $variants): ?array
{
$variant = null;
$productIds = array_keys($variants);

foreach ($selectedOptions as $attributeId => $selectedValue) {
if (!isset($options[$attributeId][$selectedValue])) {
throw new GraphQlInputException(__('configurableOptionValueUids values are incorrect'));
}

$productIds = array_intersect($productIds, $options[$attributeId][$selectedValue]);
}

if (count($productIds) === 1) {
$variantProduct = $variants[array_pop($productIds)];
$variant = $variantProduct->getData();
$variant['url_path'] = $variantProduct->getProductUrl();
$variant['model'] = $variantProduct;
}

return $variant;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/

declare(strict_types=1);

namespace Magento\ConfigurableProductGraphQl\Model\Options;

use Magento\Catalog\Api\Data\ProductInterface;
use Magento\ConfigurableProduct\Helper\Data;
use Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute;
use Magento\ConfigurableProductGraphQl\Model\Formatter\Option;

/**
* Retrieve metadata for configurable option selection.
*/
class ConfigurableOptionsMetadata
{
/**
* @var Data
*/
private $configurableProductHelper;

/**
* @var Option
*/
private $configurableOptionsFormatter;

/**
* @param Data $configurableProductHelper
* @param Option $configurableOptionsFormatter
*/
public function __construct(
Data $configurableProductHelper,
Option $configurableOptionsFormatter
) {
$this->configurableProductHelper = $configurableProductHelper;
$this->configurableOptionsFormatter = $configurableOptionsFormatter;
}

/**
* Load available selections from configurable options and variant.
*
* @param ProductInterface $product
* @param array $options
* @param array $selectedOptions
* @return array
*/
public function getAvailableSelections(ProductInterface $product, array $options, array $selectedOptions): array
{
$attributes = $this->getAttributes($product);
$availableSelections = [];

foreach ($options as $attributeId => $option) {
if ($attributeId === 'index' || isset($selectedOptions[$attributeId])) {
continue;
}

$availableSelections[] = $this->configurableOptionsFormatter->format(
$attributes[$attributeId],
$options[$attributeId] ?? []
);
}

return $availableSelections;
}

/**
* Retrieve configurable attributes for the product
*
* @param ProductInterface $product
* @return Attribute[]
*/
private function getAttributes(ProductInterface $product): array
{
$allowedAttributes = $this->configurableProductHelper->getAllowAttributes($product);
$attributes = [];
foreach ($allowedAttributes as $attribute) {
$attributes[$attribute->getAttributeId()] = $attribute;
}

return $attributes;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Magento\Catalog\Api\Data\ProductInterface;
use Magento\CatalogInventory\Model\ResourceModel\Stock\StatusFactory;
use Magento\ConfigurableProduct\Model\Product\Type\Configurable;
use Magento\Framework\Exception\LocalizedException;

/**
* Retrieve child products
Expand Down Expand Up @@ -45,7 +46,7 @@ public function __construct(
* @return ProductInterface[]
* @throws \Magento\Framework\Exception\LocalizedException
*/
public function getSalableVariantsByParent(ProductInterface $product)
public function getSalableVariantsByParent(ProductInterface $product): array
{
$collection = $this->configurableType->getUsedProductCollection($product);
$collection
Expand All @@ -62,6 +63,6 @@ public function getSalableVariantsByParent(ProductInterface $product)
}
$collection->clear();

return $collection->getItems();
return $collection->getItems() ?? [];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/

namespace Magento\ConfigurableProductGraphQl\Model\Options;

use Magento\Framework\GraphQl\Exception\GraphQlInputException;
use Magento\Framework\GraphQl\Query\Uid;

/**
* Handle option selection uid.
*/
Expand All @@ -20,6 +24,19 @@ class SelectionUidFormatter
*/
private const UID_SEPARATOR = '/';

/**
* @var Uid
*/
private $idEncoder;

/**
* @param Uid $idEncoder
*/
public function __construct(Uid $idEncoder)
{
$this->idEncoder = $idEncoder;
}

/**
* Create uid and encode.
*
Expand All @@ -29,28 +46,22 @@ class SelectionUidFormatter
*/
public function encode(int $attributeId, int $indexId): string
{
// phpcs:ignore Magento2.Functions.DiscouragedFunction
return base64_encode(implode(self::UID_SEPARATOR, [
self::UID_PREFIX,
$attributeId,
$indexId
]));
return $this->idEncoder->encode(implode(self::UID_SEPARATOR, [self::UID_PREFIX, $attributeId, $indexId]));
}

/**
* Retrieve attribute and option index from uid. Array key is the id of attribute and value is the index of option
*
* @param string $selectionUids
* @param array $selectionUids
* @return array
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @throws GraphQlInputException
*/
public function extract(array $selectionUids): array
{
$attributeOption = [];
foreach ($selectionUids as $uid) {
// phpcs:ignore Magento2.Functions.DiscouragedFunction
$optionData = explode(self::UID_SEPARATOR, base64_decode($uid));
if (count($optionData) == 3) {
$optionData = explode(self::UID_SEPARATOR, $this->idEncoder->decode($uid));
if (count($optionData) === 3) {
$attributeOption[(int)$optionData[1]] = (int)$optionData[2];
}
}
Expand Down
Loading

0 comments on commit 7d5967f

Please sign in to comment.