Skip to content

Commit 6ca721c

Browse files
committed
ACP2E-1347: Bundle product save slow
1 parent d7a6f80 commit 6ca721c

File tree

1 file changed

+106
-15
lines changed

1 file changed

+106
-15
lines changed

app/code/Magento/Bundle/Model/Option/SaveAction.php

Lines changed: 106 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,24 @@
77

88
namespace Magento\Bundle\Model\Option;
99

10+
use Exception;
1011
use Magento\Bundle\Api\Data\LinkInterface;
1112
use Magento\Bundle\Api\Data\OptionInterface;
13+
use Magento\Bundle\Api\ProductLinkManagementInterface;
14+
use Magento\Bundle\Model\Product\Type;
1215
use Magento\Bundle\Model\ResourceModel\Option;
16+
use Magento\Bundle\Model\ResourceModel\Option\Collection;
1317
use Magento\Catalog\Api\Data\ProductInterface;
14-
use Magento\Framework\App\ObjectManager;
18+
use Magento\Framework\EntityManager\EntityMetadataInterface;
1519
use Magento\Framework\EntityManager\MetadataPool;
1620
use Magento\Framework\Exception\CouldNotSaveException;
17-
use Magento\Bundle\Model\Product\Type;
18-
use Magento\Bundle\Api\ProductLinkManagementInterface;
21+
use Magento\Framework\Exception\InputException;
1922
use Magento\Framework\Exception\NoSuchEntityException;
2023
use Magento\Store\Model\StoreManagerInterface;
2124

2225
/**
2326
* Encapsulates logic for saving a bundle option, including coalescing the parent product's data.
27+
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
2428
*/
2529
class SaveAction
2630
{
@@ -66,32 +70,56 @@ public function __construct(
6670
}
6771

6872
/**
69-
* Manage the logic of saving a bundle option, including the coalescence of its parent product data.
73+
* Bulk options save
7074
*
7175
* @param ProductInterface $bundleProduct
72-
* @param OptionInterface $option
73-
* @return OptionInterface
76+
* @param OptionInterface[] $options
77+
* @return void
7478
* @throws CouldNotSaveException
75-
* @throws \Exception
79+
* @throws NoSuchEntityException
80+
* @throws InputException
7681
*/
77-
public function save(ProductInterface $bundleProduct, OptionInterface $option)
82+
public function saveBulk(ProductInterface $bundleProduct, array $options): void
7883
{
7984
$metadata = $this->metadataPool->getMetadata(ProductInterface::class);
85+
$optionCollection = $this->type->getOptionsCollection($bundleProduct);
86+
87+
foreach ($options as $option) {
88+
$this->saveOptionItem($bundleProduct, $option, $optionCollection, $metadata);
89+
}
90+
91+
$bundleProduct->setIsRelationsChanged(true);
92+
}
93+
94+
/**
95+
* Process option save
96+
*
97+
* @param ProductInterface $bundleProduct
98+
* @param OptionInterface $option
99+
* @param Collection $optionCollection
100+
* @param EntityMetadataInterface $metadata
101+
* @return void
102+
* @throws CouldNotSaveException
103+
* @throws NoSuchEntityException
104+
* @throws InputException
105+
*/
106+
private function saveOptionItem(
107+
ProductInterface $bundleProduct,
108+
OptionInterface $option,
109+
Collection $optionCollection,
110+
EntityMetadataInterface $metadata
111+
) : void {
112+
$linksToAdd = [];
80113

81114
$option->setStoreId($bundleProduct->getStoreId());
82115
$parentId = $bundleProduct->getData($metadata->getLinkField());
83116
$option->setParentId($parentId);
84-
85117
$optionId = $option->getOptionId();
86-
$linksToAdd = [];
87-
$optionCollection = $this->type->getOptionsCollection($bundleProduct);
88118

89119
/** @var \Magento\Bundle\Model\Option $existingOption */
90120
$existingOption = $optionCollection->getItemById($option->getOptionId())
91121
?? $optionCollection->getNewEmptyItem();
92122
if (!$optionId || $existingOption->getParentId() != $parentId) {
93-
//If option ID is empty or existing option's parent ID is different
94-
//we'd need a new ID for the option.
95123
$option->setOptionId(null);
96124
$option->setDefaultTitle($option->getTitle());
97125
if (is_array($option->getProductLinks())) {
@@ -110,7 +138,7 @@ public function save(ProductInterface $bundleProduct, OptionInterface $option)
110138

111139
try {
112140
$this->optionResource->save($option);
113-
} catch (\Exception $e) {
141+
} catch (Exception $e) {
114142
throw new CouldNotSaveException(__("The option couldn't be saved."), $e);
115143
}
116144

@@ -120,7 +148,21 @@ public function save(ProductInterface $bundleProduct, OptionInterface $option)
120148
}
121149
}
122150

123-
$bundleProduct->setIsRelationsChanged(true);
151+
/**
152+
* Manage the logic of saving a bundle option, including the coalescence of its parent product data.
153+
*
154+
* @param ProductInterface $bundleProduct
155+
* @param OptionInterface $option
156+
* @return OptionInterface
157+
* @throws CouldNotSaveException
158+
* @throws Exception
159+
*/
160+
public function save(ProductInterface $bundleProduct, OptionInterface $option)
161+
{
162+
$metadata = $this->metadataPool->getMetadata(ProductInterface::class);
163+
$optionCollection = $this->type->getOptionsCollection($bundleProduct);
164+
165+
$this->saveOptionItem($bundleProduct, $option, $optionCollection, $metadata);
124166

125167
return $option;
126168
}
@@ -167,6 +209,55 @@ private function updateOptionSelection(ProductInterface $product, OptionInterfac
167209
}
168210
}
169211

212+
/**
213+
* Verify that updated data actually changed
214+
*
215+
* @param LinkInterface[] $existing
216+
* @param LinkInterface[] $updates
217+
* @return array
218+
*/
219+
private function verifyLinksToUpdate(array $existing, array $updates) : array
220+
{
221+
$linksToUpdate = [];
222+
$beforeLinksMap = [];
223+
224+
foreach ($existing as $beforeLink) {
225+
$beforeLinksMap[$beforeLink->getId()] = $beforeLink;
226+
}
227+
228+
foreach ($updates as $updatedLink) {
229+
if (array_key_exists($updatedLink->getId(), $beforeLinksMap)) {
230+
$beforeLink = $beforeLinksMap[$updatedLink->getId()];
231+
if ($this->isLinkChanged($beforeLink, $updatedLink)) {
232+
$linksToUpdate[] = $updatedLink;
233+
}
234+
} else {
235+
$linksToUpdate[] = $updatedLink;
236+
}
237+
}
238+
return $linksToUpdate;
239+
}
240+
241+
/**
242+
* Check is updated link actually updated
243+
*
244+
* @param LinkInterface $beforeLink
245+
* @param LinkInterface $updatedLink
246+
* @return bool
247+
*/
248+
private function isLinkChanged(LinkInterface $beforeLink, LinkInterface $updatedLink) : bool
249+
{
250+
return (int)$beforeLink->getOptionId() !== (int)$updatedLink->getOptionId()
251+
|| $beforeLink->getIsDefault() !== $updatedLink->getIsDefault()
252+
|| (float)$beforeLink->getQty() !== (float)$updatedLink->getQty()
253+
|| $beforeLink->getPrice() !== $updatedLink->getPrice()
254+
|| $beforeLink->getCanChangeQuantity() !== $updatedLink->getCanChangeQuantity()
255+
|| (array)$beforeLink->getExtensionAttributes() !== (array)$updatedLink->getExtensionAttributes()
256+
|| (int)$beforeLink->getPosition() !== (int)$updatedLink->getPosition()
257+
|| $beforeLink->getSku() !== $updatedLink->getSku()
258+
|| $beforeLink->getPriceType() !== $updatedLink->getPriceType();
259+
}
260+
170261
/**
171262
* Compute the difference between given arrays.
172263
*

0 commit comments

Comments
 (0)