diff --git a/vendor/magento/module-catalog-graph-ql/Model/Resolver/Products/DataProvider/Product/CollectionProcessor/StockProcessor.php b/vendor/magento/module-catalog-graph-ql/Model/Resolver/Products/DataProvider/Product/CollectionProcessor/StockProcessor.php index 61085c10a733..2cfb96b0fabb 100644 --- a/vendor/magento/module-catalog-graph-ql/Model/Resolver/Products/DataProvider/Product/CollectionProcessor/StockProcessor.php +++ b/vendor/magento/module-catalog-graph-ql/Model/Resolver/Products/DataProvider/Product/CollectionProcessor/StockProcessor.php @@ -57,8 +57,10 @@ public function process( array $attributeNames, ContextInterface $context = null ): Collection { - if (!$this->stockConfig->isShowOutOfStock()) { - $this->stockStatusResource->addIsInStockFilterToCollection($collection); + $stockFlag = 'has_stock_status_filter'; + if (!$collection->hasFlag($stockFlag)) { + $this->stockStatusResource->addStockDataToCollection($collection, !$this->stockConfig->isShowOutOfStock()); + $collection->setFlag($stockFlag, true); } return $collection; diff --git a/vendor/magento/module-configurable-product/Pricing/Price/ConfigurableOptionsCompositeFilter.php b/vendor/magento/module-configurable-product/Pricing/Price/ConfigurableOptionsCompositeFilter.php new file mode 100644 index 000000000000..c6bc7cbd2963 --- /dev/null +++ b/vendor/magento/module-configurable-product/Pricing/Price/ConfigurableOptionsCompositeFilter.php @@ -0,0 +1,51 @@ +configurableOptionsFilters = $configurableOptionsFilters; + } + + /** + * @inheritdoc + */ + public function filter(ProductInterface $parentProduct, array $childProducts): array + { + foreach ($this->configurableOptionsFilters as $configurableOptionsFilter) { + $childProducts = $configurableOptionsFilter->filter($parentProduct, $childProducts); + } + return $childProducts; + } +} diff --git a/vendor/magento/module-configurable-product/Pricing/Price/ConfigurableOptionsFilterInterface.php b/vendor/magento/module-configurable-product/Pricing/Price/ConfigurableOptionsFilterInterface.php new file mode 100644 index 000000000000..cc38764b6839 --- /dev/null +++ b/vendor/magento/module-configurable-product/Pricing/Price/ConfigurableOptionsFilterInterface.php @@ -0,0 +1,25 @@ +configurable = $configurable; + $this->configurableOptionsFilter = $configurableOptionsFilter + ?? ObjectManager::getInstance()->get(ConfigurableOptionsFilterInterface::class); } /** @@ -39,7 +49,10 @@ public function __construct( public function getProducts(ProductInterface $product) { if (!isset($this->products[$product->getId()])) { - $this->products[$product->getId()] = $this->configurable->getUsedProducts($product); + $this->products[$product->getId()] = $this->configurableOptionsFilter->filter( + $product, + $this->configurable->getUsedProducts($product) + ); } return $this->products[$product->getId()]; } diff --git a/vendor/magento/module-configurable-product/Pricing/Price/ConfigurableOptionsStatusFilter.php b/vendor/magento/module-configurable-product/Pricing/Price/ConfigurableOptionsStatusFilter.php new file mode 100644 index 000000000000..5394b27ed96b --- /dev/null +++ b/vendor/magento/module-configurable-product/Pricing/Price/ConfigurableOptionsStatusFilter.php @@ -0,0 +1,29 @@ +getStatus() === Status::STATUS_ENABLED) { + $result[] = $childProduct; + } + } + + return $result; + } +} diff --git a/vendor/magento/module-configurable-product/Pricing/Price/ConfigurableOptionsStockStatusFilter.php b/vendor/magento/module-configurable-product/Pricing/Price/ConfigurableOptionsStockStatusFilter.php new file mode 100644 index 000000000000..fb3d87c9ddba --- /dev/null +++ b/vendor/magento/module-configurable-product/Pricing/Price/ConfigurableOptionsStockStatusFilter.php @@ -0,0 +1,62 @@ +stockConfig = $stockConfig; + } + + /** + * @inheritdoc + */ + public function filter(ProductInterface $parentProduct, array $childProducts): array + { + if ($this->stockConfig->isShowOutOfStock()) { + $result = $childProducts; + if ($parentProduct->getIsSalable()) { + $result = $this->filterInStockProducts($childProducts) ?: $childProducts; + } + } else { + $result = $this->filterInStockProducts($childProducts); + } + + return $result; + } + + /** + * Returns in-stock products + * + * @param ProductInterface[] $childProducts + * @return ProductInterface[] + */ + private function filterInStockProducts(array $childProducts): array + { + $result = []; + foreach ($childProducts as $childProduct) { + if ($childProduct->getIsSalable()) { + $result[] = $childProduct; + } + } + return $result; + } +} diff --git a/vendor/magento/module-configurable-product/etc/di.xml b/vendor/magento/module-configurable-product/etc/di.xml index 270e8ec74609..14ad2f323050 100644 --- a/vendor/magento/module-configurable-product/etc/di.xml +++ b/vendor/magento/module-configurable-product/etc/di.xml @@ -19,6 +19,7 @@ + diff --git a/vendor/magento/module-configurable-product/etc/frontend/di.xml b/vendor/magento/module-configurable-product/etc/frontend/di.xml index 56418bbaad12..49c93ca8b627 100644 --- a/vendor/magento/module-configurable-product/etc/frontend/di.xml +++ b/vendor/magento/module-configurable-product/etc/frontend/di.xml @@ -15,4 +15,12 @@ + + + + Magento\ConfigurableProduct\Pricing\Price\ConfigurableOptionsStatusFilter + Magento\ConfigurableProduct\Pricing\Price\ConfigurableOptionsStockStatusFilter + + + diff --git a/vendor/magento/module-configurable-product-graph-ql/Model/Options/DataProvider/Variant.php b/vendor/magento/module-configurable-product-graph-ql/Model/Options/DataProvider/Variant.php index 7b5a3fb806a5..32b9b2a48a3c 100644 --- a/vendor/magento/module-configurable-product-graph-ql/Model/Options/DataProvider/Variant.php +++ b/vendor/magento/module-configurable-product-graph-ql/Model/Options/DataProvider/Variant.php @@ -52,8 +52,6 @@ public function getSalableVariantsByParent(ProductInterface $product): array $collection ->addAttributeToSelect('*') ->addFilterByRequiredOptions(); - $collection->addMediaGalleryData(); - $collection->addTierPriceData(); $stockFlag = 'has_stock_status_filter'; if (!$collection->hasFlag($stockFlag)) { @@ -61,7 +59,8 @@ public function getSalableVariantsByParent(ProductInterface $product): array $stockStatusResource->addStockDataToCollection($collection, true); $collection->setFlag($stockFlag, true); } - $collection->clear(); + $collection->addMediaGalleryData(); + $collection->addTierPriceData(); return $collection->getItems() ?? []; } diff --git a/vendor/magento/module-configurable-product-graph-ql/Model/Resolver/Product/Price/Provider.php b/vendor/magento/module-configurable-product-graph-ql/Model/Resolver/Product/Price/Provider.php index 4dfa09d77cec..f3d13c444212 100644 --- a/vendor/magento/module-configurable-product-graph-ql/Model/Resolver/Product/Price/Provider.php +++ b/vendor/magento/module-configurable-product-graph-ql/Model/Resolver/Product/Price/Provider.php @@ -7,13 +7,14 @@ namespace Magento\ConfigurableProductGraphQl\Model\Resolver\Product\Price; +use Magento\Catalog\Model\Product\Attribute\Source\Status as ProductStatus; use Magento\Catalog\Pricing\Price\FinalPrice; use Magento\Catalog\Pricing\Price\RegularPrice; -use Magento\ConfigurableProduct\Pricing\Price\ConfigurableRegularPrice; -use Magento\Framework\Pricing\Amount\AmountInterface; -use Magento\Framework\Pricing\SaleableInterface; use Magento\CatalogGraphQl\Model\Resolver\Product\Price\ProviderInterface; use Magento\ConfigurableProduct\Pricing\Price\ConfigurableOptionsProviderInterface; +use Magento\Framework\Pricing\Amount\AmountInterface; +use Magento\Framework\Pricing\Amount\BaseFactory; +use Magento\Framework\Pricing\SaleableInterface; /** * Provides product prices for configurable products @@ -25,23 +26,37 @@ class Provider implements ProviderInterface */ private $optionsProvider; + /** + * @var BaseFactory + */ + private $amountFactory; + /** * @var array */ - private $minimumFinalAmounts = []; + private $minimalPrice = [ + FinalPrice::PRICE_CODE => [], + RegularPrice::PRICE_CODE => [] + ]; /** * @var array */ - private $maximumFinalAmounts = []; + private $maximalPrice = [ + FinalPrice::PRICE_CODE => [], + RegularPrice::PRICE_CODE => [] + ]; /** * @param ConfigurableOptionsProviderInterface $optionsProvider + * @param BaseFactory $amountFactory */ public function __construct( - ConfigurableOptionsProviderInterface $optionsProvider + ConfigurableOptionsProviderInterface $optionsProvider, + BaseFactory $amountFactory ) { $this->optionsProvider = $optionsProvider; + $this->amountFactory = $amountFactory; } /** @@ -49,18 +64,7 @@ public function __construct( */ public function getMinimalFinalPrice(SaleableInterface $product): AmountInterface { - if (!isset($this->minimumFinalAmounts[$product->getId()])) { - $minimumAmount = null; - foreach ($this->optionsProvider->getProducts($product) as $variant) { - $variantAmount = $variant->getPriceInfo()->getPrice(FinalPrice::PRICE_CODE)->getAmount(); - if (!$minimumAmount || ($variantAmount->getValue() < $minimumAmount->getValue())) { - $minimumAmount = $variantAmount; - $this->minimumFinalAmounts[$product->getId()] = $variantAmount; - } - } - } - - return $this->minimumFinalAmounts[$product->getId()]; + return $this->getMinimalPrice($product, FinalPrice::PRICE_CODE); } /** @@ -68,9 +72,7 @@ public function getMinimalFinalPrice(SaleableInterface $product): AmountInterfac */ public function getMinimalRegularPrice(SaleableInterface $product): AmountInterface { - /** @var ConfigurableRegularPrice $regularPrice */ - $regularPrice = $product->getPriceInfo()->getPrice(RegularPrice::PRICE_CODE); - return $regularPrice->getMinRegularAmount(); + return $this->getMinimalPrice($product, RegularPrice::PRICE_CODE); } /** @@ -78,18 +80,7 @@ public function getMinimalRegularPrice(SaleableInterface $product): AmountInterf */ public function getMaximalFinalPrice(SaleableInterface $product): AmountInterface { - if (!isset($this->maximumFinalAmounts[$product->getId()])) { - $maximumAmount = null; - foreach ($this->optionsProvider->getProducts($product) as $variant) { - $variantAmount = $variant->getPriceInfo()->getPrice(FinalPrice::PRICE_CODE)->getAmount(); - if (!$maximumAmount || ($variantAmount->getValue() > $maximumAmount->getValue())) { - $maximumAmount = $variantAmount; - $this->maximumFinalAmounts[$product->getId()] = $variantAmount; - } - } - } - - return $this->maximumFinalAmounts[$product->getId()]; + return $this->getMaximalPrice($product, FinalPrice::PRICE_CODE); } /** @@ -97,9 +88,7 @@ public function getMaximalFinalPrice(SaleableInterface $product): AmountInterfac */ public function getMaximalRegularPrice(SaleableInterface $product): AmountInterface { - /** @var ConfigurableRegularPrice $regularPrice */ - $regularPrice = $product->getPriceInfo()->getPrice(RegularPrice::PRICE_CODE); - return $regularPrice->getMaxRegularAmount(); + return $this->getMaximalPrice($product, RegularPrice::PRICE_CODE); } /** @@ -109,4 +98,50 @@ public function getRegularPrice(SaleableInterface $product): AmountInterface { return $product->getPriceInfo()->getPrice(RegularPrice::PRICE_CODE)->getAmount(); } -} + + /** + * Get minimal price from child products + * + * @param SaleableInterface $product + * @param string $code + * @return AmountInterface + */ + private function getMinimalPrice(SaleableInterface $product, string $code): AmountInterface + { + if (!isset($this->minimalPrice[$code][$product->getId()])) { + $minimumAmount = null; + foreach ($this->optionsProvider->getProducts($product) as $variant) { + $variantAmount = $variant->getPriceInfo()->getPrice($code)->getAmount(); + if (!$minimumAmount || ($variantAmount->getValue() < $minimumAmount->getValue())) { + $minimumAmount = $variantAmount; + $this->minimalPrice[$code][$product->getId()] = $variantAmount; + } + } + } + + return $this->minimalPrice[$code][$product->getId()] ?? $this->amountFactory->create(['amount' => null]); + } + + /** + * Get maximal price from child products + * + * @param SaleableInterface $product + * @param string $code + * @return AmountInterface + */ + private function getMaximalPrice(SaleableInterface $product, string $code): AmountInterface + { + if (!isset($this->maximalPrice[$code][$product->getId()])) { + $maximumAmount = null; + foreach ($this->optionsProvider->getProducts($product) as $variant) { + $variantAmount = $variant->getPriceInfo()->getPrice($code)->getAmount(); + if (!$maximumAmount || ($variantAmount->getValue() > $maximumAmount->getValue())) { + $maximumAmount = $variantAmount; + $this->maximalPrice[$code][$product->getId()] = $variantAmount; + } + } + } + + return $this->maximalPrice[$code][$product->getId()] ?? $this->amountFactory->create(['amount' => null]); + } +} \ No newline at end of file diff --git a/vendor/magento/module-configurable-product-graph-ql/Plugin/AddStockStatusToCollection.php b/vendor/magento/module-configurable-product-graph-ql/Plugin/AddStockStatusToCollection.php new file mode 100644 index 000000000000..c12847d20827 --- /dev/null +++ b/vendor/magento/module-configurable-product-graph-ql/Plugin/AddStockStatusToCollection.php @@ -0,0 +1,49 @@ +stockStatusResourceModel = $stockStatusResourceModel; + } + + /** + * Add stock data to the collection. + * + * @param Collection $productCollection + * @param bool $printQuery + * @param bool $logQuery + * @return array + */ + public function beforeLoad(Collection $productCollection, $printQuery = false, $logQuery = false): array + { + $stockFlag = 'has_stock_status_filter'; + if (!$productCollection->hasFlag($stockFlag)) { + $this->stockStatusResourceModel->addStockDataToCollection($productCollection, false); + $productCollection->setFlag($stockFlag, true); + } + return [$printQuery, $logQuery]; + } +} diff --git a/vendor/magento/module-configurable-product-graph-ql/etc/graphql/di.xml b/vendor/magento/module-configurable-product-graph-ql/etc/graphql/di.xml index 56cff716ab6f..d291e05c3041 100644 --- a/vendor/magento/module-configurable-product-graph-ql/etc/graphql/di.xml +++ b/vendor/magento/module-configurable-product-graph-ql/etc/graphql/di.xml @@ -74,4 +74,15 @@ + + + + Magento\ConfigurableProduct\Pricing\Price\ConfigurableOptionsStatusFilter + Magento\ConfigurableProduct\Pricing\Price\ConfigurableOptionsStockStatusFilter + + + + + +