Skip to content

Commit 8b87fa9

Browse files
committed
Merge remote-tracking branch 'origin/MAGETWO-90149' into 2.3-develop-pr16
2 parents f658498 + 794900d commit 8b87fa9

File tree

15 files changed

+672
-164
lines changed

15 files changed

+672
-164
lines changed

app/code/Magento/BundleImportExport/Model/Export/RowCustomizer.php

Lines changed: 132 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99
use Magento\CatalogImportExport\Model\Export\RowCustomizerInterface;
1010
use Magento\CatalogImportExport\Model\Import\Product as ImportProductModel;
1111
use Magento\Bundle\Model\ResourceModel\Selection\Collection as SelectionCollection;
12-
use Magento\ImportExport\Controller\Adminhtml\Import;
1312
use Magento\ImportExport\Model\Import as ImportModel;
14-
use \Magento\Catalog\Model\Product\Type\AbstractType;
13+
use Magento\Catalog\Model\Product\Type\AbstractType;
14+
use Magento\Store\Model\StoreManagerInterface;
1515

1616
/**
1717
* Class RowCustomizer
@@ -105,6 +105,34 @@ class RowCustomizer implements RowCustomizerInterface
105105
AbstractType::SHIPMENT_SEPARATELY => 'separately',
106106
];
107107

108+
/**
109+
* @var \Magento\Bundle\Model\ResourceModel\Option\Collection[]
110+
*/
111+
private $optionCollections = [];
112+
113+
/**
114+
* @var array
115+
*/
116+
private $storeIdToCode = [];
117+
118+
/**
119+
* @var string
120+
*/
121+
private $optionCollectionCacheKey = '_cache_instance_options_collection';
122+
123+
/**
124+
* @var StoreManagerInterface
125+
*/
126+
private $storeManager;
127+
128+
/**
129+
* @param StoreManagerInterface $storeManager
130+
*/
131+
public function __construct(StoreManagerInterface $storeManager)
132+
{
133+
$this->storeManager = $storeManager;
134+
}
135+
108136
/**
109137
* Retrieve list of bundle specific columns
110138
* @return array
@@ -205,17 +233,15 @@ protected function populateBundleData($collection)
205233
* @param \Magento\Catalog\Model\Product $product
206234
* @return string
207235
*/
208-
protected function getFormattedBundleOptionValues($product)
236+
protected function getFormattedBundleOptionValues(\Magento\Catalog\Model\Product $product): string
209237
{
210-
/** @var \Magento\Bundle\Model\ResourceModel\Option\Collection $optionsCollection */
211-
$optionsCollection = $product->getTypeInstance()
212-
->getOptionsCollection($product)
213-
->setOrder('position', Collection::SORT_ORDER_ASC);
214-
238+
$optionCollections = $this->getProductOptionCollection($product);
215239
$bundleData = '';
216-
foreach ($optionsCollection as $option) {
240+
$optionTitles = $this->getBundleOptionTitles($product);
241+
foreach ($optionCollections->getItems() as $option) {
242+
$optionValues = $this->getFormattedOptionValues($option, $optionTitles);
217243
$bundleData .= $this->getFormattedBundleSelections(
218-
$this->getFormattedOptionValues($option),
244+
$optionValues,
219245
$product->getTypeInstance()
220246
->getSelectionsCollection([$option->getId()], $product)
221247
->setOrder('position', Collection::SORT_ORDER_ASC)
@@ -267,16 +293,25 @@ function ($value, $key) {
267293
* Retrieve option value of bundle product
268294
*
269295
* @param \Magento\Bundle\Model\Option $option
296+
* @param string[] $optionTitles
270297
* @return string
271298
*/
272-
protected function getFormattedOptionValues($option)
273-
{
274-
return 'name' . ImportProductModel::PAIR_NAME_VALUE_SEPARATOR
275-
. $option->getTitle() . ImportModel::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR
276-
. 'type' . ImportProductModel::PAIR_NAME_VALUE_SEPARATOR
277-
. $option->getType() . ImportModel::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR
278-
. 'required' . ImportProductModel::PAIR_NAME_VALUE_SEPARATOR
279-
. $option->getRequired();
299+
protected function getFormattedOptionValues(
300+
\Magento\Bundle\Model\Option $option,
301+
array $optionTitles = []
302+
): string {
303+
$names = implode(ImportModel::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, array_map(
304+
function ($title, $storeName) {
305+
return $storeName . ImportProductModel::PAIR_NAME_VALUE_SEPARATOR . $title;
306+
},
307+
$optionTitles[$option->getOptionId()],
308+
array_keys($optionTitles[$option->getOptionId()])
309+
));
310+
return $names . ImportModel::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR
311+
. 'type' . ImportProductModel::PAIR_NAME_VALUE_SEPARATOR
312+
. $option->getType() . ImportModel::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR
313+
. 'required' . ImportProductModel::PAIR_NAME_VALUE_SEPARATOR
314+
. $option->getRequired();
280315
}
281316

282317
/**
@@ -381,4 +416,83 @@ private function parseAdditionalAttributes($additionalAttributes)
381416
}
382417
return $preparedAttributes;
383418
}
419+
420+
/**
421+
* Get product options titles.
422+
*
423+
* Values for all store views (default) should be specified with 'name' key.
424+
* If user want to specify value or change existing for non default store views it should be specified with
425+
* 'name_' prefix and needed store view suffix.
426+
*
427+
* For example:
428+
* - 'name=All store views name' for all store views
429+
* - 'name_specific_store=Specific store name' for store view with 'specific_store' store code
430+
*
431+
* @param \Magento\Catalog\Model\Product $product
432+
* @return array
433+
*/
434+
private function getBundleOptionTitles(\Magento\Catalog\Model\Product $product): array
435+
{
436+
$optionCollections = $this->getProductOptionCollection($product);
437+
$optionsTitles = [];
438+
/** @var \Magento\Bundle\Model\Option $option */
439+
foreach ($optionCollections->getItems() as $option) {
440+
$optionsTitles[$option->getId()]['name'] = $option->getTitle();
441+
}
442+
$storeIds = $product->getStoreIds();
443+
if (count($storeIds) > 1) {
444+
foreach ($storeIds as $storeId) {
445+
$optionCollections = $this->getProductOptionCollection($product, (int)$storeId);
446+
/** @var \Magento\Bundle\Model\Option $option */
447+
foreach ($optionCollections->getItems() as $option) {
448+
$optionTitle = $option->getTitle();
449+
if ($optionsTitles[$option->getId()]['name'] != $optionTitle) {
450+
$optionsTitles[$option->getId()]['name_' . $this->getStoreCodeById((int)$storeId)] =
451+
$optionTitle;
452+
}
453+
}
454+
}
455+
}
456+
return $optionsTitles;
457+
}
458+
459+
/**
460+
* Get product options collection by provided product model.
461+
*
462+
* Set given store id to the product if it was defined (default store id will be set if was not).
463+
*
464+
* @param \Magento\Catalog\Model\Product $product $product
465+
* @param int $storeId
466+
* @return \Magento\Bundle\Model\ResourceModel\Option\Collection
467+
*/
468+
private function getProductOptionCollection(
469+
\Magento\Catalog\Model\Product $product,
470+
int $storeId = \Magento\Store\Model\Store::DEFAULT_STORE_ID
471+
): \Magento\Bundle\Model\ResourceModel\Option\Collection {
472+
$productSku = $product->getSku();
473+
if (!isset($this->optionCollections[$productSku][$storeId])) {
474+
$product->unsetData($this->optionCollectionCacheKey);
475+
$product->setStoreId($storeId);
476+
$this->optionCollections[$productSku][$storeId] = $product->getTypeInstance()
477+
->getOptionsCollection($product)
478+
->setOrder('position', Collection::SORT_ORDER_ASC);
479+
}
480+
return $this->optionCollections[$productSku][$storeId];
481+
}
482+
483+
/**
484+
* Retrieve store code by it's ID.
485+
*
486+
* Collect store id in $storeIdToCode[] private variable if it was not initialized earlier.
487+
*
488+
* @param int $storeId
489+
* @return string
490+
*/
491+
private function getStoreCodeById(int $storeId): string
492+
{
493+
if (!isset($this->storeIdToCode[$storeId])) {
494+
$this->storeIdToCode[$storeId] = $this->storeManager->getStore($storeId)->getCode();
495+
}
496+
return $this->storeIdToCode[$storeId];
497+
}
384498
}

app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php

Lines changed: 78 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,15 @@
88
*/
99
namespace Magento\BundleImportExport\Model\Import\Product\Type;
1010

11-
use \Magento\Framework\App\ObjectManager;
12-
use \Magento\Bundle\Model\Product\Price as BundlePrice;
13-
use \Magento\Catalog\Model\Product\Type\AbstractType;
11+
use Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory as AttributeCollectionFactory;
12+
use Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\CollectionFactory as AttributeSetCollectionFactory;
13+
use Magento\Framework\App\ObjectManager;
14+
use Magento\Bundle\Model\Product\Price as BundlePrice;
15+
use Magento\Catalog\Model\Product\Type\AbstractType;
1416
use Magento\CatalogImportExport\Model\Import\Product;
17+
use Magento\Framework\App\ResourceConnection;
18+
use Magento\Framework\EntityManager\MetadataPool;
19+
use Magento\Store\Model\StoreManagerInterface;
1520

1621
/**
1722
* Class Bundle
@@ -137,25 +142,39 @@ class Bundle extends \Magento\CatalogImportExport\Model\Import\Product\Type\Abst
137142
private $relationsDataSaver;
138143

139144
/**
140-
* @param \Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\CollectionFactory $attrSetColFac
141-
* @param \Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory $prodAttrColFac
142-
* @param \Magento\Framework\App\ResourceConnection $resource
145+
* @var StoreManagerInterface
146+
*/
147+
private $storeManager;
148+
149+
/**
150+
* @var array
151+
*/
152+
private $storeCodeToId = [];
153+
154+
/**
155+
* @param AttributeSetCollectionFactory $attrSetColFac
156+
* @param AttributeCollectionFactory $prodAttrColFac
157+
* @param ResourceConnection $resource
143158
* @param array $params
144-
* @param \Magento\Framework\EntityManager\MetadataPool|null $metadataPool
159+
* @param MetadataPool|null $metadataPool
145160
* @param Bundle\RelationsDataSaver|null $relationsDataSaver
161+
* @param StoreManagerInterface|null $storeManager
146162
*/
147163
public function __construct(
148-
\Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\CollectionFactory $attrSetColFac,
149-
\Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory $prodAttrColFac,
150-
\Magento\Framework\App\ResourceConnection $resource,
164+
AttributeSetCollectionFactory $attrSetColFac,
165+
AttributeCollectionFactory $prodAttrColFac,
166+
ResourceConnection $resource,
151167
array $params,
152-
\Magento\Framework\EntityManager\MetadataPool $metadataPool = null,
153-
Bundle\RelationsDataSaver $relationsDataSaver = null
168+
MetadataPool $metadataPool = null,
169+
Bundle\RelationsDataSaver $relationsDataSaver = null,
170+
StoreManagerInterface $storeManager = null
154171
) {
155172
parent::__construct($attrSetColFac, $prodAttrColFac, $resource, $params, $metadataPool);
156173

157174
$this->relationsDataSaver = $relationsDataSaver
158175
?: ObjectManager::getInstance()->get(Bundle\RelationsDataSaver::class);
176+
$this->storeManager = $storeManager
177+
?: ObjectManager::getInstance()->get(StoreManagerInterface::class);
159178
}
160179

161180
/**
@@ -262,20 +281,29 @@ protected function populateOptionTemplate($option, $entityId, $index = null)
262281
* @param array $option
263282
* @param int $optionId
264283
* @param int $storeId
265-
*
266-
* @return array|bool
284+
* @return array
267285
*/
268-
protected function populateOptionValueTemplate($option, $optionId, $storeId = 0)
286+
protected function populateOptionValueTemplate(array $option, int $optionId, int $storeId = 0): array
269287
{
270-
if (!isset($option['name']) || !isset($option['parent_id']) || !$optionId) {
271-
return false;
288+
$optionValues = [];
289+
if (isset($option['name'], $option['parent_id']) && $optionId) {
290+
$pattern = '/^name[_]?(.*)/';
291+
$keys = array_keys($option);
292+
$optionNames = preg_grep($pattern, $keys);
293+
foreach ($optionNames as $optionName) {
294+
preg_match($pattern, $optionName, $storeCodes);
295+
$storeCode = array_pop($storeCodes);
296+
$storeId = $storeCode ? $this->getStoreIdByCode($storeCode) : $storeId;
297+
$optionValues[] = [
298+
'option_id' => $optionId,
299+
'parent_product_id' => $option['parent_id'],
300+
'store_id' => $storeId,
301+
'title' => $option[$optionName],
302+
];
303+
}
272304
}
273-
return [
274-
'option_id' => $optionId,
275-
'parent_product_id' => $option['parent_id'],
276-
'store_id' => $storeId,
277-
'title' => $option['name'],
278-
];
305+
306+
return $optionValues;
279307
}
280308

281309
/**
@@ -285,7 +313,7 @@ protected function populateOptionValueTemplate($option, $optionId, $storeId = 0)
285313
* @param int $optionId
286314
* @param int $parentId
287315
* @param int $index
288-
* @return array
316+
* @return array|bool
289317
* @SuppressWarnings(PHPMD.NPathComplexity)
290318
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
291319
*/
@@ -574,23 +602,27 @@ protected function insertOptions()
574602
* @param array $optionIds
575603
* @return array
576604
*/
577-
protected function populateInsertOptionValues($optionIds)
605+
protected function populateInsertOptionValues(array $optionIds): array
578606
{
579-
$insertValues = [];
607+
$optionValues = [];
580608
foreach ($this->_cachedOptions as $entityId => $options) {
581609
foreach ($options as $key => $option) {
582610
foreach ($optionIds as $optionId => $assoc) {
583611
if ($assoc['position'] == $this->_cachedOptions[$entityId][$key]['index']
584612
&& $assoc['parent_id'] == $entityId) {
585613
$option['parent_id'] = $entityId;
586-
$insertValues[] = $this->populateOptionValueTemplate($option, $optionId);
614+
$optionValues = array_merge(
615+
$optionValues,
616+
$this->populateOptionValueTemplate($option, $optionId)
617+
);
587618
$this->_cachedOptions[$entityId][$key]['option_id'] = $optionId;
588619
break;
589620
}
590621
}
591622
}
592623
}
593-
return $insertValues;
624+
625+
return $optionValues;
594626
}
595627

596628
/**
@@ -711,4 +743,22 @@ protected function clear()
711743
$this->_cachedSkuToProducts = [];
712744
return $this;
713745
}
746+
747+
/**
748+
* Get store id by store code.
749+
*
750+
* @param string $storeCode
751+
* @return int
752+
*/
753+
private function getStoreIdByCode(string $storeCode): int
754+
{
755+
if (!isset($this->storeIdToCode[$storeCode])) {
756+
/** @var $store \Magento\Store\Model\Store */
757+
foreach ($this->storeManager->getStores() as $store) {
758+
$this->storeCodeToId[$store->getCode()] = $store->getId();
759+
}
760+
}
761+
762+
return $this->storeCodeToId[$storeCode];
763+
}
714764
}

0 commit comments

Comments
 (0)