Skip to content

Commit 8868a95

Browse files
Merge pull request #1235 from magento-firedrakes/MAGETWO-64547
Fixed issues: - MAGETWO-64547: Bundle Products - The options you selected are not available
2 parents c350871 + 19ecf05 commit 8868a95

File tree

41 files changed

+1711
-218
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1711
-218
lines changed

app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle/Option.php

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,14 +143,16 @@ protected function _getSelectedOptions()
143143
{
144144
if (is_null($this->_selectedOptions)) {
145145
$this->_selectedOptions = [];
146+
147+
/** @var \Magento\Bundle\Model\Option $option */
146148
$option = $this->getOption();
147149

148150
if ($this->getProduct()->hasPreconfiguredValues()) {
149-
$configValue = $this->getProduct()->getPreconfiguredValues()->getData(
151+
$selectionId = $this->getProduct()->getPreconfiguredValues()->getData(
150152
'bundle_option/' . $option->getId()
151153
);
152-
if ($configValue) {
153-
$this->_selectedOptions = $configValue;
154+
if ($selectionId && $option->getSelectionById($selectionId)) {
155+
$this->_selectedOptions = $selectionId;
154156
} elseif (!$option->getRequired()) {
155157
$this->_selectedOptions = 'None';
156158
}

app/code/Magento/Bundle/Controller/Adminhtml/Product/Initialization/Helper/Plugin/Bundle.php

Lines changed: 36 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,6 @@ protected function processBundleOptionsData(\Magento\Catalog\Model\Product $prod
133133

134134
$option = $this->optionFactory->create(['data' => $optionData]);
135135
$option->setSku($product->getSku());
136-
$option->setOptionId(null);
137136

138137
$links = [];
139138
$bundleLinks = $product->getBundleSelectionsData();
@@ -142,28 +141,13 @@ protected function processBundleOptionsData(\Magento\Catalog\Model\Product $prod
142141
}
143142

144143
foreach ($bundleLinks[$key] as $linkData) {
145-
if ((bool)$linkData['delete']) {
144+
if (!empty($linkData['delete'])) {
146145
continue;
147146
}
148-
$link = $this->linkFactory->create(['data' => $linkData]);
149-
150-
if ((int)$product->getPriceType() !== \Magento\Bundle\Model\Product\Price::PRICE_TYPE_DYNAMIC) {
151-
if (array_key_exists('selection_price_value', $linkData)) {
152-
$link->setPrice($linkData['selection_price_value']);
153-
}
154-
if (array_key_exists('selection_price_type', $linkData)) {
155-
$link->setPriceType($linkData['selection_price_type']);
156-
}
147+
if (!empty($linkData['selection_id'])) {
148+
$linkData['id'] = $linkData['selection_id'];
157149
}
158-
159-
$linkProduct = $this->productRepository->getById($linkData['product_id']);
160-
$link->setSku($linkProduct->getSku());
161-
$link->setQty($linkData['selection_qty']);
162-
163-
if (array_key_exists('selection_can_change_qty', $linkData)) {
164-
$link->setCanChangeQuantity($linkData['selection_can_change_qty']);
165-
}
166-
$links[] = $link;
150+
$links[] = $this->buildLink($product, $linkData);
167151
}
168152
$option->setProductLinks($links);
169153
$options[] = $option;
@@ -203,9 +187,40 @@ protected function processDynamicOptionsData(\Magento\Catalog\Model\Product $pro
203187
}
204188
$customOption = $this->customOptionFactory->create(['data' => $customOptionData]);
205189
$customOption->setProductSku($product->getSku());
206-
$customOption->setOptionId(null);
207190
$newOptions[] = $customOption;
208191
}
209192
$product->setOptions($newOptions);
210193
}
194+
195+
/**
196+
* @param \Magento\Catalog\Model\Product $product
197+
* @param array $linkData
198+
*
199+
* @return \Magento\Bundle\Api\Data\LinkInterface
200+
*/
201+
private function buildLink(
202+
\Magento\Catalog\Model\Product $product,
203+
array $linkData
204+
) {
205+
$link = $this->linkFactory->create(['data' => $linkData]);
206+
207+
if ((int)$product->getPriceType() !== \Magento\Bundle\Model\Product\Price::PRICE_TYPE_DYNAMIC) {
208+
if (array_key_exists('selection_price_value', $linkData)) {
209+
$link->setPrice($linkData['selection_price_value']);
210+
}
211+
if (array_key_exists('selection_price_type', $linkData)) {
212+
$link->setPriceType($linkData['selection_price_type']);
213+
}
214+
}
215+
216+
$linkProduct = $this->productRepository->getById($linkData['product_id']);
217+
$link->setSku($linkProduct->getSku());
218+
$link->setQty($linkData['selection_qty']);
219+
220+
if (array_key_exists('selection_can_change_qty', $linkData)) {
221+
$link->setCanChangeQuantity($linkData['selection_can_change_qty']);
222+
}
223+
224+
return $link;
225+
}
211226
}

app/code/Magento/Bundle/Model/LinkManagement.php

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,8 @@ public function saveChild(
168168
* @param string $linkedProductId
169169
* @param string $parentProductId
170170
* @return \Magento\Bundle\Model\Selection
171+
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
172+
* @SuppressWarnings(PHPMD.NPathComplexity)
171173
*/
172174
protected function mapProductLinkToSelectionModel(
173175
\Magento\Bundle\Model\Selection $selectionModel,
@@ -177,7 +179,10 @@ protected function mapProductLinkToSelectionModel(
177179
) {
178180
$selectionModel->setProductId($linkedProductId);
179181
$selectionModel->setParentProductId($parentProductId);
180-
if (($productLink->getOptionId() !== null)) {
182+
if ($productLink->getSelectionId() !== null) {
183+
$selectionModel->setSelectionId($productLink->getSelectionId());
184+
}
185+
if ($productLink->getOptionId() !== null) {
181186
$selectionModel->setOptionId($productLink->getOptionId());
182187
}
183188
if ($productLink->getPosition() !== null) {
@@ -217,8 +222,13 @@ public function addChild(
217222
);
218223
}
219224

225+
$linkField = $this->getMetadataPool()->getMetadata(ProductInterface::class)->getLinkField();
226+
220227
$options = $this->optionCollection->create();
228+
221229
$options->setIdFilter($optionId);
230+
$options->setProductLinkFilter($product->getData($linkField));
231+
222232
$existingOption = $options->getFirstItem();
223233

224234
if (!$existingOption->getId()) {
@@ -230,7 +240,6 @@ public function addChild(
230240
);
231241
}
232242

233-
$linkField = $this->getMetadataPool()->getMetadata(ProductInterface::class)->getLinkField();
234243
/* @var $resource \Magento\Bundle\Model\ResourceModel\Bundle */
235244
$resource = $this->bundleFactory->create();
236245
$selections = $resource->getSelectionsData($product->getData($linkField));
@@ -243,7 +252,8 @@ public function addChild(
243252
if ($selections) {
244253
foreach ($selections as $selection) {
245254
if ($selection['option_id'] == $optionId &&
246-
$selection['product_id'] == $linkProductModel->getEntityId()) {
255+
$selection['product_id'] == $linkProductModel->getEntityId() &&
256+
$selection['parent_product_id'] == $product->getData($linkField)) {
247257
if (!$product->getCopyFromView()) {
248258
throw new CouldNotSaveException(
249259
__(
@@ -300,7 +310,6 @@ public function removeChild($sku, $optionId, $childSku)
300310
continue;
301311
}
302312
$excludeSelectionIds[] = $selection->getSelectionId();
303-
$usedProductIds[] = $selection->getProductId();
304313
}
305314
}
306315
if (empty($removeSelectionIds)) {

app/code/Magento/Bundle/Model/OptionRepository.php

Lines changed: 62 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -187,12 +187,34 @@ public function save(
187187
$metadata = $this->getMetadataPool()->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class);
188188

189189
$option->setStoreId($product->getStoreId());
190-
$option->setParentId($product->getData($metadata->getLinkField()));
190+
$parentId = $product->getData($metadata->getLinkField());
191+
$option->setParentId($parentId);
192+
193+
$optionId = $option->getOptionId();
191194
$linksToAdd = [];
192-
$option->setDefaultTitle($option->getDefaultTitle() ?: $option->getTitle());
193-
if (is_array($option->getProductLinks())) {
194-
$linksToAdd = $option->getProductLinks();
195+
$optionCollection = $this->type->getOptionsCollection($product);
196+
$optionCollection->setIdFilter($option->getOptionId());
197+
$optionCollection->setProductLinkFilter($parentId);
198+
199+
/** @var \Magento\Bundle\Model\Option $existingOption */
200+
$existingOption = $optionCollection->getFirstItem();
201+
if (!$optionId) {
202+
$option->setOptionId(null);
203+
}
204+
if (!$optionId || $existingOption->getParentId() != $parentId) {
205+
$option->setDefaultTitle($option->getTitle());
206+
if (is_array($option->getProductLinks())) {
207+
$linksToAdd = $option->getProductLinks();
208+
}
209+
} else {
210+
if (!$existingOption->getOptionId()) {
211+
throw new NoSuchEntityException('Requested option doesn\'t exist');
212+
}
213+
214+
$option->setData(array_merge($existingOption->getData(), $option->getData()));
215+
$this->updateOptionSelection($product, $option);
195216
}
217+
196218
try {
197219
$this->optionResource->save($option);
198220
} catch (\Exception $e) {
@@ -218,16 +240,25 @@ protected function updateOptionSelection(
218240
\Magento\Catalog\Api\Data\ProductInterface $product,
219241
\Magento\Bundle\Api\Data\OptionInterface $option
220242
) {
221-
$existingLinks = [];
243+
$optionId = $option->getOptionId();
244+
$existingLinks = $this->linkManagement->getChildren($product->getSku(), $optionId);
222245
$linksToAdd = [];
246+
$linksToUpdate = [];
223247
$linksToDelete = [];
224248
if (is_array($option->getProductLinks())) {
225249
$productLinks = $option->getProductLinks();
226250
foreach ($productLinks as $productLink) {
227-
$linksToAdd[] = $productLink;
251+
if (!$productLink->getId() && !$productLink->getSelectionId()) {
252+
$linksToAdd[] = $productLink;
253+
} else {
254+
$linksToUpdate[] = $productLink;
255+
}
228256
}
229257
/** @var \Magento\Bundle\Api\Data\LinkInterface[] $linksToDelete */
230-
$linksToDelete = $this->compareLinks([], $existingLinks);
258+
$linksToDelete = $this->compareLinks($existingLinks, $linksToUpdate);
259+
}
260+
foreach ($linksToUpdate as $linkedProduct) {
261+
$this->linkManagement->saveChild($product->getSku(), $linkedProduct);
231262
}
232263
foreach ($linksToDelete as $linkedProduct) {
233264
$this->linkManagement->removeChild(
@@ -257,33 +288,36 @@ private function getProduct($sku)
257288
}
258289

259290
/**
260-
* Computes the difference of arrays
291+
* Computes the difference between given arrays.
292+
*
293+
* @param \Magento\Bundle\Api\Data\LinkInterface[] $firstArray
294+
* @param \Magento\Bundle\Api\Data\LinkInterface[] $secondArray
261295
*
262-
* @param array $firstArray of \Magento\Bundle\Api\Data\LinkInterface
263-
* @param array $secondArray of \Magento\Bundle\Api\Data\LinkInterface
264296
* @return array
265-
* @SuppressWarnings(PHPMD.UnusedPrivateMethod)
266297
*/
267-
private function compareLinks(
268-
array $firstArray,
269-
array $secondArray
270-
) {
298+
private function compareLinks(array $firstArray, array $secondArray)
299+
{
271300
$result = [];
272-
if (count($firstArray) < count($secondArray)) {
273-
$holder = $firstArray;
274-
$firstArray = $secondArray;
275-
$secondArray = $holder;
301+
302+
$firstArrayIds = [];
303+
$firstArrayMap = [];
304+
305+
$secondArrayIds = [];
306+
307+
foreach ($firstArray as $item) {
308+
$firstArrayIds[] = $item->getId();
309+
310+
$firstArrayMap[$item->getId()] = $item;
276311
}
277-
foreach ($firstArray as $obj) {
278-
foreach ($secondArray as $objToCompare) {
279-
if ($obj->getId() != $objToCompare->getId()
280-
&& $obj instanceof \Magento\Bundle\Api\Data\LinkInterface
281-
&& $objToCompare instanceof \Magento\Bundle\Api\Data\LinkInterface
282-
) {
283-
$result[] = $obj;
284-
}
285-
}
312+
313+
foreach ($secondArray as $item) {
314+
$secondArrayIds[] = $item->getId();
286315
}
316+
317+
foreach (array_diff($firstArrayIds, $secondArrayIds) as $id) {
318+
$result[] = $firstArrayMap[$id];
319+
}
320+
287321
return $result;
288322
}
289323

app/code/Magento/Bundle/Model/Product/SaveHandler.php

Lines changed: 39 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@
55
*/
66
namespace Magento\Bundle\Model\Product;
77

8+
use Magento\Catalog\Api\Data\ProductInterface;
89
use Magento\Bundle\Api\ProductOptionRepositoryInterface as OptionRepository;
910
use Magento\Bundle\Api\ProductLinkManagementInterface;
11+
use Magento\Framework\App\ObjectManager;
12+
use Magento\Framework\EntityManager\MetadataPool;
1013
use Magento\Framework\EntityManager\Operation\ExtensionInterface;
1114

1215
/**
@@ -24,16 +27,26 @@ class SaveHandler implements ExtensionInterface
2427
*/
2528
protected $productLinkManagement;
2629

30+
/**
31+
* @var MetadataPool
32+
*/
33+
private $metadataPool;
34+
2735
/**
2836
* @param OptionRepository $optionRepository
2937
* @param ProductLinkManagementInterface $productLinkManagement
38+
* @param MetadataPool|null $metadataPool
3039
*/
3140
public function __construct(
3241
OptionRepository $optionRepository,
33-
ProductLinkManagementInterface $productLinkManagement
42+
ProductLinkManagementInterface $productLinkManagement,
43+
MetadataPool $metadataPool = null
3444
) {
3545
$this->optionRepository = $optionRepository;
3646
$this->productLinkManagement = $productLinkManagement;
47+
48+
$this->metadataPool = $metadataPool
49+
?: ObjectManager::getInstance()->get(MetadataPool::class);
3750
}
3851

3952
/**
@@ -44,31 +57,40 @@ public function __construct(
4457
*/
4558
public function execute($entity, $arguments = [])
4659
{
47-
$bundleProductOptions = $entity->getExtensionAttributes()->getBundleProductOptions();
48-
if ($entity->getTypeId() !== 'bundle' || empty($bundleProductOptions)) {
60+
/** @var \Magento\Bundle\Api\Data\OptionInterface[] $options */
61+
$options = $entity->getExtensionAttributes()->getBundleProductOptions() ?: [];
62+
63+
if ($entity->getTypeId() !== 'bundle' || empty($options)) {
4964
return $entity;
5065
}
5166

5267
if (!$entity->getCopyFromView()) {
53-
/** @var \Magento\Catalog\Api\Data\ProductInterface $entity */
54-
foreach ($this->optionRepository->getList($entity->getSku()) as $option) {
55-
$this->removeOptionLinks($entity->getSku(), $option);
56-
$this->optionRepository->delete($option);
57-
}
68+
$updatedOptions = [];
69+
$oldOptions = $this->optionRepository->getList($entity->getSku());
70+
71+
$metadata = $this->metadataPool->getMetadata(ProductInterface::class);
72+
73+
$productId = $entity->getData($metadata->getLinkField());
5874

59-
$options = $bundleProductOptions ?: [];
6075
foreach ($options as $option) {
61-
$option->setOptionId(null);
62-
$this->optionRepository->save($entity, $option);
76+
$updatedOptions[$option->getOptionId()][$productId] = (bool)$option->getOptionId();
6377
}
64-
} else {
65-
//save only labels and not selections + product links
66-
$options = $bundleProductOptions ?: [];
67-
foreach ($options as $option) {
68-
$this->optionRepository->save($entity, $option);
78+
79+
foreach ($oldOptions as $option) {
80+
if (!isset($updatedOptions[$option->getOptionId()][$productId])) {
81+
$option->setParentId($productId);
82+
$this->removeOptionLinks($entity->getSku(), $option);
83+
$this->optionRepository->delete($option);
84+
}
6985
}
70-
$entity->setCopyFromView(false);
7186
}
87+
88+
foreach ($options as $option) {
89+
$this->optionRepository->save($entity, $option);
90+
}
91+
92+
$entity->setCopyFromView(false);
93+
7294
return $entity;
7395
}
7496

0 commit comments

Comments
 (0)