Skip to content

Commit fdaa419

Browse files
authored
Merge pull request #2524 from magento-honey-badgers/MAGETWO-90182-Bundle-Child-Index
[Honey-Badgers] Bug Fixes
2 parents 61814ad + b949d3f commit fdaa419

Some content is hidden

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

44 files changed

+1215
-1186
lines changed
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Bundle\Model\Option;
9+
10+
use Magento\Bundle\Api\Data\OptionInterface;
11+
use Magento\Bundle\Model\ResourceModel\Option;
12+
use Magento\Catalog\Api\Data\ProductInterface;
13+
use Magento\Framework\EntityManager\MetadataPool;
14+
use Magento\Framework\Exception\CouldNotSaveException;
15+
use Magento\Bundle\Model\Product\Type;
16+
use Magento\Bundle\Api\ProductLinkManagementInterface;
17+
18+
/**
19+
* Encapsulates logic for saving a bundle option, including coalescing the parent product's data.
20+
*/
21+
class SaveAction
22+
{
23+
/**
24+
* @var Option
25+
*/
26+
private $optionResource;
27+
28+
/**
29+
* @var MetadataPool
30+
*/
31+
private $metadataPool;
32+
33+
/**
34+
* @var Type
35+
*/
36+
private $type;
37+
38+
/**
39+
* @var ProductLinkManagementInterface
40+
*/
41+
private $linkManagement;
42+
43+
/**
44+
* @param Option $optionResource
45+
* @param MetadataPool $metadataPool
46+
* @param Type $type
47+
* @param ProductLinkManagementInterface $linkManagement
48+
*/
49+
public function __construct(
50+
Option $optionResource,
51+
MetadataPool $metadataPool,
52+
Type $type,
53+
ProductLinkManagementInterface $linkManagement
54+
) {
55+
$this->optionResource = $optionResource;
56+
$this->metadataPool = $metadataPool;
57+
$this->type = $type;
58+
$this->linkManagement = $linkManagement;
59+
}
60+
61+
/**
62+
* Manage the logic of saving a bundle option, including the coalescence of its parent product data.
63+
*
64+
* @param ProductInterface $bundleProduct
65+
* @param OptionInterface $option
66+
* @return OptionInterface
67+
* @throws CouldNotSaveException
68+
* @throws \Exception
69+
*/
70+
public function save(ProductInterface $bundleProduct, OptionInterface $option)
71+
{
72+
$metadata = $this->metadataPool->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class);
73+
74+
$option->setStoreId($bundleProduct->getStoreId());
75+
$parentId = $bundleProduct->getData($metadata->getLinkField());
76+
$option->setParentId($parentId);
77+
78+
$optionId = $option->getOptionId();
79+
$linksToAdd = [];
80+
$optionCollection = $this->type->getOptionsCollection($bundleProduct);
81+
$optionCollection->setIdFilter($option->getOptionId());
82+
$optionCollection->setProductLinkFilter($parentId);
83+
84+
/** @var \Magento\Bundle\Model\Option $existingOption */
85+
$existingOption = $optionCollection->getFirstItem();
86+
if (!$optionId || $existingOption->getParentId() != $parentId) {
87+
//If option ID is empty or existing option's parent ID is different
88+
//we'd need a new ID for the option.
89+
$option->setOptionId(null);
90+
$option->setDefaultTitle($option->getTitle());
91+
if (is_array($option->getProductLinks())) {
92+
$linksToAdd = $option->getProductLinks();
93+
}
94+
} else {
95+
if (!$existingOption->getOptionId()) {
96+
throw new NoSuchEntityException(
97+
__("The option that was requested doesn't exist. Verify the entity and try again.")
98+
);
99+
}
100+
101+
$option->setData(array_merge($existingOption->getData(), $option->getData()));
102+
$this->updateOptionSelection($bundleProduct, $option);
103+
}
104+
105+
try {
106+
$this->optionResource->save($option);
107+
} catch (\Exception $e) {
108+
throw new CouldNotSaveException(__("The option couldn't be saved."), $e);
109+
}
110+
111+
/** @var \Magento\Bundle\Api\Data\LinkInterface $linkedProduct */
112+
foreach ($linksToAdd as $linkedProduct) {
113+
$this->linkManagement->addChild($bundleProduct, $option->getOptionId(), $linkedProduct);
114+
}
115+
116+
$bundleProduct->setIsRelationsChanged(true);
117+
118+
return $option;
119+
}
120+
121+
/**
122+
* Update option selections
123+
*
124+
* @param \Magento\Catalog\Api\Data\ProductInterface $product
125+
* @param \Magento\Bundle\Api\Data\OptionInterface $option
126+
* @return void
127+
*/
128+
private function updateOptionSelection(ProductInterface $product, OptionInterface $option)
129+
{
130+
$optionId = $option->getOptionId();
131+
$existingLinks = $this->linkManagement->getChildren($product->getSku(), $optionId);
132+
$linksToAdd = [];
133+
$linksToUpdate = [];
134+
$linksToDelete = [];
135+
if (is_array($option->getProductLinks())) {
136+
$productLinks = $option->getProductLinks();
137+
foreach ($productLinks as $productLink) {
138+
if (!$productLink->getId() && !$productLink->getSelectionId()) {
139+
$linksToAdd[] = $productLink;
140+
} else {
141+
$linksToUpdate[] = $productLink;
142+
}
143+
}
144+
/** @var \Magento\Bundle\Api\Data\LinkInterface[] $linksToDelete */
145+
$linksToDelete = $this->compareLinks($existingLinks, $linksToUpdate);
146+
}
147+
foreach ($linksToUpdate as $linkedProduct) {
148+
$this->linkManagement->saveChild($product->getSku(), $linkedProduct);
149+
}
150+
foreach ($linksToDelete as $linkedProduct) {
151+
$this->linkManagement->removeChild(
152+
$product->getSku(),
153+
$option->getOptionId(),
154+
$linkedProduct->getSku()
155+
);
156+
}
157+
foreach ($linksToAdd as $linkedProduct) {
158+
$this->linkManagement->addChild($product, $option->getOptionId(), $linkedProduct);
159+
}
160+
}
161+
162+
/**
163+
* Compute the difference between given arrays.
164+
*
165+
* @param \Magento\Bundle\Api\Data\LinkInterface[] $firstArray
166+
* @param \Magento\Bundle\Api\Data\LinkInterface[] $secondArray
167+
*
168+
* @return array
169+
*/
170+
private function compareLinks(array $firstArray, array $secondArray)
171+
{
172+
$result = [];
173+
174+
$firstArrayIds = [];
175+
$firstArrayMap = [];
176+
177+
$secondArrayIds = [];
178+
179+
foreach ($firstArray as $item) {
180+
$firstArrayIds[] = $item->getId();
181+
182+
$firstArrayMap[$item->getId()] = $item;
183+
}
184+
185+
foreach ($secondArray as $item) {
186+
$secondArrayIds[] = $item->getId();
187+
}
188+
189+
foreach (array_diff($firstArrayIds, $secondArrayIds) as $id) {
190+
$result[] = $firstArrayMap[$id];
191+
}
192+
193+
return $result;
194+
}
195+
}

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

Lines changed: 17 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@
77

88
namespace Magento\Bundle\Model;
99

10+
use Magento\Bundle\Model\Option\SaveAction;
1011
use Magento\Catalog\Api\Data\ProductInterface;
11-
use Magento\Framework\App\ObjectManager;
12-
use Magento\Framework\EntityManager\MetadataPool;
13-
use Magento\Framework\Exception\CouldNotSaveException;
1412
use Magento\Framework\Exception\InputException;
1513
use Magento\Framework\Exception\NoSuchEntityException;
1614

1715
/**
16+
* Repository for performing CRUD operations for a bundle product's options.
17+
*
1818
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
1919
*/
2020
class OptionRepository implements \Magento\Bundle\Api\ProductOptionRepositoryInterface
@@ -39,11 +39,6 @@ class OptionRepository implements \Magento\Bundle\Api\ProductOptionRepositoryInt
3939
*/
4040
protected $optionResource;
4141

42-
/**
43-
* @var \Magento\Store\Model\StoreManager
44-
*/
45-
protected $storeManager;
46-
4742
/**
4843
* @var \Magento\Bundle\Api\ProductLinkManagementInterface
4944
*/
@@ -54,53 +49,44 @@ class OptionRepository implements \Magento\Bundle\Api\ProductOptionRepositoryInt
5449
*/
5550
protected $productOptionList;
5651

57-
/**
58-
* @var Product\LinksList
59-
*/
60-
protected $linkList;
61-
6252
/**
6353
* @var \Magento\Framework\Api\DataObjectHelper
6454
*/
6555
protected $dataObjectHelper;
6656

6757
/**
68-
* @var \Magento\Framework\EntityManager\MetadataPool
58+
* @var SaveAction
6959
*/
70-
private $metadataPool;
60+
private $optionSave;
7161

7262
/**
7363
* @param \Magento\Catalog\Api\ProductRepositoryInterface $productRepository
7464
* @param Product\Type $type
7565
* @param \Magento\Bundle\Api\Data\OptionInterfaceFactory $optionFactory
7666
* @param \Magento\Bundle\Model\ResourceModel\Option $optionResource
77-
* @param \Magento\Store\Model\StoreManagerInterface $storeManager
7867
* @param \Magento\Bundle\Api\ProductLinkManagementInterface $linkManagement
7968
* @param Product\OptionList $productOptionList
80-
* @param Product\LinksList $linkList
8169
* @param \Magento\Framework\Api\DataObjectHelper $dataObjectHelper
82-
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
70+
* @param SaveAction $optionSave
8371
*/
8472
public function __construct(
8573
\Magento\Catalog\Api\ProductRepositoryInterface $productRepository,
8674
\Magento\Bundle\Model\Product\Type $type,
8775
\Magento\Bundle\Api\Data\OptionInterfaceFactory $optionFactory,
8876
\Magento\Bundle\Model\ResourceModel\Option $optionResource,
89-
\Magento\Store\Model\StoreManagerInterface $storeManager,
9077
\Magento\Bundle\Api\ProductLinkManagementInterface $linkManagement,
9178
\Magento\Bundle\Model\Product\OptionList $productOptionList,
92-
\Magento\Bundle\Model\Product\LinksList $linkList,
93-
\Magento\Framework\Api\DataObjectHelper $dataObjectHelper
79+
\Magento\Framework\Api\DataObjectHelper $dataObjectHelper,
80+
SaveAction $optionSave
9481
) {
9582
$this->productRepository = $productRepository;
9683
$this->type = $type;
9784
$this->optionFactory = $optionFactory;
9885
$this->optionResource = $optionResource;
99-
$this->storeManager = $storeManager;
10086
$this->linkManagement = $linkManagement;
10187
$this->productOptionList = $productOptionList;
102-
$this->linkList = $linkList;
10388
$this->dataObjectHelper = $dataObjectHelper;
89+
$this->optionSave = $optionSave;
10490
}
10591

10692
/**
@@ -118,7 +104,7 @@ public function get($sku, $optionId)
118104
);
119105
}
120106

121-
$productLinks = $this->linkList->getItems($product, $optionId);
107+
$productLinks = $this->linkManagement->getChildren($product->getSku(), $optionId);
122108

123109
/** @var \Magento\Bundle\Api\Data\OptionInterface $option */
124110
$optionDataObject = $this->optionFactory->create();
@@ -177,7 +163,9 @@ public function deleteById($sku, $optionId)
177163
$product = $this->getProduct($sku);
178164
$optionCollection = $this->type->getOptionsCollection($product);
179165
$optionCollection->setIdFilter($optionId);
180-
return $this->delete($optionCollection->getFirstItem());
166+
$hasBeenDeleted = $this->delete($optionCollection->getFirstItem());
167+
168+
return $hasBeenDeleted;
181169
}
182170

183171
/**
@@ -187,51 +175,12 @@ public function save(
187175
\Magento\Catalog\Api\Data\ProductInterface $product,
188176
\Magento\Bundle\Api\Data\OptionInterface $option
189177
) {
190-
$metadata = $this->getMetadataPool()->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class);
191-
192-
$option->setStoreId($product->getStoreId());
193-
$parentId = $product->getData($metadata->getLinkField());
194-
$option->setParentId($parentId);
195-
196-
$optionId = $option->getOptionId();
197-
$linksToAdd = [];
198-
$optionCollection = $this->type->getOptionsCollection($product);
199-
$optionCollection->setIdFilter($option->getOptionId());
200-
$optionCollection->setProductLinkFilter($parentId);
201-
202-
/** @var \Magento\Bundle\Model\Option $existingOption */
203-
$existingOption = $optionCollection->getFirstItem();
204-
if (!$optionId || $existingOption->getParentId() != $parentId) {
205-
//If option ID is empty or existing option's parent ID is different
206-
//we'd need a new ID for the option.
207-
$option->setOptionId(null);
208-
$option->setDefaultTitle($option->getTitle());
209-
if (is_array($option->getProductLinks())) {
210-
$linksToAdd = $option->getProductLinks();
211-
}
212-
} else {
213-
if (!$existingOption->getOptionId()) {
214-
throw new NoSuchEntityException(
215-
__("The option that was requested doesn't exist. Verify the entity and try again.")
216-
);
217-
}
178+
$savedOption = $this->optionSave->save($product, $option);
218179

219-
$option->setData(array_merge($existingOption->getData(), $option->getData()));
220-
$this->updateOptionSelection($product, $option);
221-
}
180+
$productToSave = $this->productRepository->get($product->getSku());
181+
$this->productRepository->save($productToSave);
222182

223-
try {
224-
$this->optionResource->save($option);
225-
} catch (\Exception $e) {
226-
throw new CouldNotSaveException(__("The option couldn't be saved."), $e);
227-
}
228-
229-
/** @var \Magento\Bundle\Api\Data\LinkInterface $linkedProduct */
230-
foreach ($linksToAdd as $linkedProduct) {
231-
$this->linkManagement->addChild($product, $option->getOptionId(), $linkedProduct);
232-
}
233-
$product->setIsRelationsChanged(true);
234-
return $option->getOptionId();
183+
return $savedOption->getOptionId();
235184
}
236185

237186
/**
@@ -325,16 +274,4 @@ private function compareLinks(array $firstArray, array $secondArray)
325274

326275
return $result;
327276
}
328-
329-
/**
330-
* Get MetadataPool instance
331-
* @return MetadataPool
332-
*/
333-
private function getMetadataPool()
334-
{
335-
if (!$this->metadataPool) {
336-
$this->metadataPool = ObjectManager::getInstance()->get(MetadataPool::class);
337-
}
338-
return $this->metadataPool;
339-
}
340277
}

0 commit comments

Comments
 (0)