Skip to content

Commit

Permalink
Merge branch '2.4-develop' into Refactoring-AdminCreateSimpleProductZ…
Browse files Browse the repository at this point in the history
…eroPriceTest
  • Loading branch information
Gabriel da Gama authored Apr 7, 2021
2 parents 3dc6542 + a4071d4 commit 99cc467
Show file tree
Hide file tree
Showing 30 changed files with 1,157 additions and 64 deletions.
26 changes: 21 additions & 5 deletions app/code/Magento/Bundle/Model/Product/SaveHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@
namespace Magento\Bundle\Model\Product;

use Magento\Bundle\Api\Data\OptionInterface;
use Magento\Bundle\Api\ProductLinkManagementInterface;
use Magento\Bundle\Api\ProductOptionRepositoryInterface as OptionRepository;
use Magento\Bundle\Model\Option\SaveAction;
use Magento\Bundle\Model\ProductRelationsProcessorComposite;
use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Bundle\Api\ProductOptionRepositoryInterface as OptionRepository;
use Magento\Bundle\Api\ProductLinkManagementInterface;
use Magento\Framework\App\ObjectManager;
use Magento\Framework\EntityManager\MetadataPool;
use Magento\Framework\EntityManager\Operation\ExtensionInterface;
Expand Down Expand Up @@ -48,26 +49,35 @@ class SaveHandler implements ExtensionInterface
*/
private $checkOptionLinkIfExist;

/**
* @var ProductRelationsProcessorComposite
*/
private $productRelationsProcessorComposite;

/**
* @param OptionRepository $optionRepository
* @param ProductLinkManagementInterface $productLinkManagement
* @param SaveAction $optionSave
* @param MetadataPool $metadataPool
* @param CheckOptionLinkIfExist|null $checkOptionLinkIfExist
* @param ProductRelationsProcessorComposite|null $productRelationsProcessorComposite
*/
public function __construct(
OptionRepository $optionRepository,
ProductLinkManagementInterface $productLinkManagement,
SaveAction $optionSave,
MetadataPool $metadataPool,
?CheckOptionLinkIfExist $checkOptionLinkIfExist = null
?CheckOptionLinkIfExist $checkOptionLinkIfExist = null,
?ProductRelationsProcessorComposite $productRelationsProcessorComposite = null
) {
$this->optionRepository = $optionRepository;
$this->productLinkManagement = $productLinkManagement;
$this->optionSave = $optionSave;
$this->metadataPool = $metadataPool;
$this->checkOptionLinkIfExist = $checkOptionLinkIfExist ??
ObjectManager::getInstance()->get(CheckOptionLinkIfExist::class);
$this->checkOptionLinkIfExist = $checkOptionLinkIfExist
?? ObjectManager::getInstance()->get(CheckOptionLinkIfExist::class);
$this->productRelationsProcessorComposite = $productRelationsProcessorComposite
?? ObjectManager::getInstance()->get(ProductRelationsProcessorComposite::class);
}

/**
Expand Down Expand Up @@ -107,6 +117,12 @@ public function execute($entity, $arguments = [])
$entity->setCopyFromView(false);
}

$this->productRelationsProcessorComposite->process(
$entity,
$existingBundleProductOptions,
$bundleProductOptions
);

return $entity;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\Bundle\Model;

use Magento\Catalog\Api\Data\ProductInterface;

/**
* Composite processor to handle bundle product relations.
*/
class ProductRelationsProcessorComposite implements ProductRelationsProcessorInterface
{
/**
* @var ProductRelationsProcessorInterface[]
*/
private $processors;

/**
* @param ProductRelationsProcessorInterface[] $processors
*/
public function __construct(array $processors = [])
{
foreach ($processors as $processor) {
if (!$processor instanceof ProductRelationsProcessorInterface) {
throw new \InvalidArgumentException(
__('Product relations processor must implement %1.', ProductRelationsProcessorInterface::class)
);
}
}

$this->processors = $processors;
}

/**
* @inheritDoc
*/
public function process(
ProductInterface $product,
array $existingProductOptions,
array $expectedProductOptions
): void {
foreach ($this->processors as $processor) {
$processor->process($product, $existingProductOptions, $expectedProductOptions);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\Bundle\Model;

/**
* Processor to handle bundle product relations.
*/
interface ProductRelationsProcessorInterface
{
/**
* Process bundle product relations.
*
* @param \Magento\Catalog\Api\Data\ProductInterface $product
* @param array $existingProductOptions
* @param array $expectedProductOptions
* @return void
*/
public function process(
\Magento\Catalog\Api\Data\ProductInterface $product,
array $existingProductOptions,
array $expectedProductOptions
): void;
}
149 changes: 149 additions & 0 deletions app/code/Magento/Bundle/Model/QuoteRecollectProcessor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\Bundle\Model;

use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Framework\Reflection\TypeCaster;
use Magento\Quote\Model\ResourceModel\Quote as QuoteResource;

/**
* Recollect quota after handle product relations.
*/
class QuoteRecollectProcessor implements ProductRelationsProcessorInterface
{
/**
* @var TypeCaster
*/
private $typeCaster;

/**
* @var QuoteResource
*/
private $quoteResource;

/**
* @var array
*/
private $comparisonFieldsTypeMapper;

/**
* @param TypeCaster $typeCaster
* @param QuoteResource $quoteResource
* @param array $comparisonFieldsTypeMapper
*/
public function __construct(
TypeCaster $typeCaster,
QuoteResource $quoteResource,
array $comparisonFieldsTypeMapper = []
) {
$this->typeCaster = $typeCaster;
$this->quoteResource = $quoteResource;
$this->comparisonFieldsTypeMapper = $comparisonFieldsTypeMapper;
}

/**
* Mark quotes to recollect if product options or links are changed.
*
* @param ProductInterface $product
* @param array $existingProductOptions
* @param array $expectedProductOptions
* @return void
*/
public function process(
ProductInterface $product,
array $existingProductOptions,
array $expectedProductOptions
): void {
if (empty($existingProductOptions)) {
return;
}

if ($this->isProductOptionsChanged($existingProductOptions, $expectedProductOptions)
|| $this->isProductLinksChanged($existingProductOptions, $expectedProductOptions)
) {
$this->quoteResource->markQuotesRecollect($product->getId());
}
}

/**
* Check product options change.
*
* @param array $existingProductOptions
* @param array $expectedProductOptions
* @return bool
*/
private function isProductOptionsChanged(
array $existingProductOptions,
array $expectedProductOptions
): bool {
if (count($existingProductOptions) !== count($expectedProductOptions)) {
return true;
}

$productOptionsDiff = array_udiff(
$expectedProductOptions,
$existingProductOptions,
function ($expectedProductOption, $existingProductOption) {
if ($expectedProductOption->getOptionId() === $existingProductOption->getOptionId()) {
return $expectedProductOption->getRequired() - $existingProductOption->getRequired();
}

return $expectedProductOption->getOptionId() - $existingProductOption->getOptionId();
}
);

return (bool)count($productOptionsDiff);
}

/**
* Check product links change.
*
* @param array $existingProductOptions
* @param array $expectedProductOptions
* @return bool
*/
private function isProductLinksChanged(
array $existingProductOptions,
array $expectedProductOptions
): bool {
$existingProductLinks = $this->flattenProductLinksData($existingProductOptions);
$expectedProductLinks = $this->flattenProductLinksData($expectedProductOptions);

return $existingProductLinks != $expectedProductLinks;
}

/**
* Simplify product links data.
*
* @param array $productOptions
* @return array
*/
private function flattenProductLinksData(array $productOptions): array
{
return array_reduce($productOptions, function ($result, $productOption) {
$optionId = $productOption->getOptionId();
$productLinks = [];
foreach ($productOption->getProductLinks() as $productLink) {
$productLinkData = $productLink->getData();
$productLinkFilteredData = [];
foreach ($this->comparisonFieldsTypeMapper as $fieldName => $fieldType) {
if (isset($productLinkData[$fieldName])) {
$productLinkFilteredData[$fieldName] = $this->typeCaster->castValueToType(
$productLinkData[$fieldName],
$fieldType
);
}
}
$productLinks[$productLink->getId()] = $productLinkFilteredData;
}
$result[$optionId] = $productLinks;

return $result;
});
}
}
18 changes: 18 additions & 0 deletions app/code/Magento/Bundle/etc/di.xml
Original file line number Diff line number Diff line change
Expand Up @@ -253,4 +253,22 @@
type="Magento\Bundle\Plugin\Quote\UpdateBundleQuoteItemOptions"
sortOrder="10"/>
</type>
<type name="Magento\Bundle\Model\ProductRelationsProcessorComposite">
<arguments>
<argument name="processors" xsi:type="array">
<item name="quote_recollect" xsi:type="object">Magento\Bundle\Model\QuoteRecollectProcessor</item>
</argument>
</arguments>
</type>
<type name="Magento\Bundle\Model\QuoteRecollectProcessor">
<arguments>
<argument name="comparisonFieldsTypeMapper" xsi:type="array">
<item name="sku" xsi:type="string">string</item>
<item name="price" xsi:type="string">float</item>
<item name="price_type" xsi:type="string">string</item>
<item name="qty" xsi:type="string">float</item>
<item name="selection_can_change_quantity" xsi:type="string">string</item>
</argument>
</arguments>
</type>
</config>
1 change: 1 addition & 0 deletions app/code/Magento/Bundle/i18n/en_US.csv
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,4 @@ Type,Type
"Cannot create shipment as bundle product ""%1"" has shipment type ""%2"". %3 should be shipped instead.","Cannot create shipment as bundle product ""%1"" has shipment type ""%2"". %3 should be shipped instead."
"Bundle product itself","Bundle product itself"
"Bundle product options","Bundle product options"
"Product relations processor must implement %1.","Product relations processor must implement %1."
Loading

0 comments on commit 99cc467

Please sign in to comment.