Skip to content

Commit 30c3417

Browse files
ENGCOM-7035: Refactor addlinks to own class take 3 (follows #21658) #23191
- Merge Pull Request #23191 from iMi-digital/magento2:refactor-addlinks-to-own-class-take-3 - Merged commits: 1. a269519 2. 095c434 3. 84ccd7f 4. 3b2a4fe 5. f06e4d7 6. 7dbdfd5 7. 39542fd 8. 4fbbcf1 9. dd124e8 10. e8e164f 11. 37ad6a2 12. 11c3525 13. 502d66e 14. e05bb10 15. d437cec 16. 5ca49ae
2 parents 5a9fcab + 5ca49ae commit 30c3417

File tree

4 files changed

+569
-219
lines changed

4 files changed

+569
-219
lines changed

app/code/Magento/CatalogImportExport/Model/Import/Product.php

Lines changed: 27 additions & 219 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use Magento\Catalog\Model\Product\Visibility;
1212
use Magento\Catalog\Model\ResourceModel\Product\Link;
1313
use Magento\CatalogImportExport\Model\Import\Product\ImageTypeProcessor;
14+
use Magento\CatalogImportExport\Model\Import\Product\LinkProcessor;
1415
use Magento\CatalogImportExport\Model\Import\Product\MediaGalleryProcessor;
1516
use Magento\CatalogImportExport\Model\Import\Product\RowValidatorInterface as ValidatorInterface;
1617
use Magento\CatalogImportExport\Model\Import\Product\StatusProcessor;
@@ -224,6 +225,8 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
224225
/**
225226
* Links attribute name-to-link type ID.
226227
*
228+
* @deprecated use DI for LinkProcessor class if you want to add additional types
229+
*
227230
* @var array
228231
*/
229232
protected $_linkNameToId = [
@@ -758,6 +761,11 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
758761
*/
759762
private $stockProcessor;
760763

764+
/**
765+
* @var LinkProcessor
766+
*/
767+
private $linkProcessor;
768+
761769
/**
762770
* @param \Magento\Framework\Json\Helper\Data $jsonHelper
763771
* @param \Magento\ImportExport\Helper\Data $importExportData
@@ -805,6 +813,7 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
805813
* @param ProductRepositoryInterface|null $productRepository
806814
* @param StatusProcessor|null $statusProcessor
807815
* @param StockProcessor|null $stockProcessor
816+
* @param LinkProcessor|null $linkProcessor
808817
* @throws LocalizedException
809818
* @throws \Magento\Framework\Exception\FileSystemException
810819
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
@@ -856,7 +865,8 @@ public function __construct(
856865
DateTimeFactory $dateTimeFactory = null,
857866
ProductRepositoryInterface $productRepository = null,
858867
StatusProcessor $statusProcessor = null,
859-
StockProcessor $stockProcessor = null
868+
StockProcessor $stockProcessor = null,
869+
LinkProcessor $linkProcessor = null
860870
) {
861871
$this->_eventManager = $eventManager;
862872
$this->stockRegistry = $stockRegistry;
@@ -896,6 +906,10 @@ public function __construct(
896906
->get(StatusProcessor::class);
897907
$this->stockProcessor = $stockProcessor ?: ObjectManager::getInstance()
898908
->get(StockProcessor::class);
909+
$this->linkProcessor = $linkProcessor ?? ObjectManager::getInstance()
910+
->get(LinkProcessor::class);
911+
$this->linkProcessor->addNameToIds($this->_linkNameToId);
912+
899913
parent::__construct(
900914
$jsonHelper,
901915
$importExportData,
@@ -1133,14 +1147,15 @@ protected function _replaceProducts()
11331147
* Save products data.
11341148
*
11351149
* @return $this
1150+
* @throws LocalizedException
11361151
*/
11371152
protected function _saveProductsData()
11381153
{
11391154
$this->_saveProducts();
11401155
foreach ($this->_productTypeModels as $productTypeModel) {
11411156
$productTypeModel->saveData();
11421157
}
1143-
$this->_saveLinks();
1158+
$this->linkProcessor->saveLinks($this, $this->_dataSourceModel, $this->getProductEntityLinkField());
11441159
$this->_saveStockItem();
11451160
if ($this->_replaceFlag) {
11461161
$this->getOptionEntity()->clearProductsSkuToId();
@@ -1274,30 +1289,13 @@ protected function _prepareRowForDb(array $rowData)
12741289
*
12751290
* Must be called after ALL products saving done.
12761291
*
1292+
* @deprecated use linkProcessor Directly
1293+
*
12771294
* @return $this
12781295
*/
12791296
protected function _saveLinks()
12801297
{
1281-
/** @var Link $resource */
1282-
$resource = $this->_linkFactory->create();
1283-
$mainTable = $resource->getMainTable();
1284-
$positionAttrId = [];
1285-
$nextLinkId = $this->_resourceHelper->getNextAutoincrement($mainTable);
1286-
1287-
// pre-load 'position' attributes ID for each link type once
1288-
foreach ($this->_linkNameToId as $linkId) {
1289-
$select = $this->_connection->select()->from(
1290-
$resource->getTable('catalog_product_link_attribute'),
1291-
['id' => 'product_link_attribute_id']
1292-
)->where(
1293-
'link_type_id = :link_id AND product_link_attribute_code = :position'
1294-
);
1295-
$bind = [':link_id' => $linkId, ':position' => 'position'];
1296-
$positionAttrId[$linkId] = $this->_connection->fetchOne($select, $bind);
1297-
}
1298-
while ($bunch = $this->_dataSourceModel->getNextBunch()) {
1299-
$this->processLinkBunches($bunch, $resource, $nextLinkId, $positionAttrId);
1300-
}
1298+
$this->linkProcessor->saveLinks($this, $this->_dataSourceModel, $this->getProductEntityLinkField());
13011299
return $this;
13021300
}
13031301

@@ -1534,14 +1532,19 @@ public function getImagesFromRow(array $rowData)
15341532
return [$images, $labels];
15351533
}
15361534

1535+
// phpcs:disable Generic.Metrics.NestingLevel
1536+
15371537
/**
15381538
* Gather and save information about product entities.
15391539
*
1540+
* FIXME: Reduce nesting level
1541+
*
15401542
* @return $this
15411543
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
15421544
* @SuppressWarnings(PHPMD.NPathComplexity)
15431545
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
15441546
* @SuppressWarnings(PHPMD.UnusedLocalVariable)
1547+
* @SuppressWarnings(PHPMD.UnusedLocalVariable)
15451548
* @throws LocalizedException
15461549
* phpcs:disable Generic.Metrics.NestingLevel.TooHigh
15471550
*/
@@ -1929,6 +1932,8 @@ protected function _saveProducts()
19291932
}
19301933
//phpcs:enable Generic.Metrics.NestingLevel
19311934

1935+
// phpcs:enable
1936+
19321937
/**
19331938
* Prepare array with image states (visible or hidden from product page)
19341939
*
@@ -3114,203 +3119,6 @@ private function getValidationErrorLevel($sku): string
31143119
: ProcessingError::ERROR_LEVEL_NOT_CRITICAL;
31153120
}
31163121

3117-
/**
3118-
* Processes link bunches
3119-
*
3120-
* @param array $bunch
3121-
* @param Link $resource
3122-
* @param int $nextLinkId
3123-
* @param array $positionAttrId
3124-
* @return void
3125-
* @throws LocalizedException
3126-
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
3127-
*/
3128-
private function processLinkBunches(
3129-
array $bunch,
3130-
Link $resource,
3131-
int $nextLinkId,
3132-
array $positionAttrId
3133-
): void {
3134-
$productIds = [];
3135-
$linkRows = [];
3136-
$positionRows = [];
3137-
$linksToDelete = [];
3138-
3139-
$bunch = array_filter($bunch, [$this, 'isRowAllowedToImport'], ARRAY_FILTER_USE_BOTH);
3140-
foreach ($bunch as $rowData) {
3141-
$sku = $rowData[self::COL_SKU];
3142-
$productId = $this->skuProcessor->getNewSku($sku)[$this->getProductEntityLinkField()];
3143-
$productIds[] = $productId;
3144-
$productLinkKeys = $this->fetchProductLinks($resource, $productId);
3145-
$linkNameToId = array_filter(
3146-
$this->_linkNameToId,
3147-
function ($linkName) use ($rowData) {
3148-
return isset($rowData[$linkName . 'sku']);
3149-
},
3150-
ARRAY_FILTER_USE_KEY
3151-
);
3152-
foreach ($linkNameToId as $linkName => $linkId) {
3153-
$linkSkus = explode($this->getMultipleValueSeparator(), $rowData[$linkName . 'sku']);
3154-
//process empty value
3155-
if (!empty($linkSkus[0]) && $linkSkus[0] === $this->getEmptyAttributeValueConstant()) {
3156-
$linksToDelete[$linkId][] = $productId;
3157-
continue;
3158-
}
3159-
3160-
$linkPositions = !empty($rowData[$linkName . 'position'])
3161-
? explode($this->getMultipleValueSeparator(), $rowData[$linkName . 'position'])
3162-
: [];
3163-
$linkSkus = array_filter(
3164-
$linkSkus,
3165-
function ($linkedSku) use ($sku) {
3166-
$linkedSku = trim($linkedSku);
3167-
return ($this->skuProcessor->getNewSku($linkedSku) !== null || $this->isSkuExist($linkedSku))
3168-
&& strcasecmp($linkedSku, $sku) !== 0;
3169-
}
3170-
);
3171-
3172-
foreach ($linkSkus as $linkedKey => $linkedSku) {
3173-
$linkedId = $this->getProductLinkedId($linkedSku);
3174-
if ($linkedId == null) {
3175-
// Import file links to a SKU which is skipped for some reason, which leads to a "NULL"
3176-
// link causing fatal errors.
3177-
$formatStr = 'WARNING: Orphaned link skipped: From SKU %s (ID %d) to SKU %s, Link type id: %d';
3178-
$exception = new \Exception(sprintf($formatStr, $sku, $productId, $linkedSku, $linkId));
3179-
$this->_logger->critical($exception);
3180-
continue;
3181-
}
3182-
$linkKey = $this->composeLinkKey($productId, $linkedId, $linkId);
3183-
$productLinkKeys[$linkKey] = $productLinkKeys[$linkKey] ?? $nextLinkId;
3184-
3185-
$linkRows[$linkKey] = $linkRows[$linkKey] ?? [
3186-
'link_id' => $productLinkKeys[$linkKey],
3187-
'product_id' => $productId,
3188-
'linked_product_id' => $linkedId,
3189-
'link_type_id' => $linkId,
3190-
];
3191-
3192-
if (!empty($linkPositions[$linkedKey])) {
3193-
$positionRows[] = [
3194-
'link_id' => $productLinkKeys[$linkKey],
3195-
'product_link_attribute_id' => $positionAttrId[$linkId],
3196-
'value' => $linkPositions[$linkedKey],
3197-
];
3198-
}
3199-
$nextLinkId++;
3200-
}
3201-
}
3202-
}
3203-
$this->deleteProductsLinks($resource, $linksToDelete);
3204-
$this->saveLinksData($resource, $productIds, $linkRows, $positionRows);
3205-
}
3206-
3207-
/**
3208-
* Delete links
3209-
*
3210-
* @param Link $resource
3211-
* @param array $linksToDelete
3212-
* @return void
3213-
* @throws LocalizedException
3214-
*/
3215-
private function deleteProductsLinks(Link $resource, array $linksToDelete)
3216-
{
3217-
if (!empty($linksToDelete) && Import::BEHAVIOR_APPEND === $this->getBehavior()) {
3218-
foreach ($linksToDelete as $linkTypeId => $productIds) {
3219-
if (!empty($productIds)) {
3220-
$whereLinkId = $this->_connection->quoteInto('link_type_id', $linkTypeId);
3221-
$whereProductId = $this->_connection->quoteInto('product_id IN (?)', array_unique($productIds));
3222-
$this->_connection->delete(
3223-
$resource->getMainTable(),
3224-
$whereLinkId . ' AND ' . $whereProductId
3225-
);
3226-
}
3227-
}
3228-
}
3229-
}
3230-
3231-
/**
3232-
* Fetches Product Links
3233-
*
3234-
* @param Link $resource
3235-
* @param int $productId
3236-
* @return array
3237-
*/
3238-
private function fetchProductLinks(Link $resource, int $productId) : array
3239-
{
3240-
$productLinkKeys = [];
3241-
$select = $this->_connection->select()->from(
3242-
$resource->getTable('catalog_product_link'),
3243-
['id' => 'link_id', 'linked_id' => 'linked_product_id', 'link_type_id' => 'link_type_id']
3244-
)->where(
3245-
'product_id = :product_id'
3246-
);
3247-
$bind = [':product_id' => $productId];
3248-
foreach ($this->_connection->fetchAll($select, $bind) as $linkData) {
3249-
$linkKey = $this->composeLinkKey($productId, $linkData['linked_id'], $linkData['link_type_id']);
3250-
$productLinkKeys[$linkKey] = $linkData['id'];
3251-
}
3252-
3253-
return $productLinkKeys;
3254-
}
3255-
3256-
/**
3257-
* Gets the Id of the Sku
3258-
*
3259-
* @param string $linkedSku
3260-
* @return int|null
3261-
*/
3262-
private function getProductLinkedId(string $linkedSku) : ?int
3263-
{
3264-
$linkedSku = trim($linkedSku);
3265-
$newSku = $this->skuProcessor->getNewSku($linkedSku);
3266-
$linkedId = !empty($newSku) ? $newSku['entity_id'] : $this->getExistingSku($linkedSku)['entity_id'];
3267-
return $linkedId;
3268-
}
3269-
3270-
/**
3271-
* Saves information about product links
3272-
*
3273-
* @param Link $resource
3274-
* @param array $productIds
3275-
* @param array $linkRows
3276-
* @param array $positionRows
3277-
* @throws LocalizedException
3278-
*/
3279-
private function saveLinksData(Link $resource, array $productIds, array $linkRows, array $positionRows)
3280-
{
3281-
$mainTable = $resource->getMainTable();
3282-
if (Import::BEHAVIOR_APPEND != $this->getBehavior() && $productIds) {
3283-
$this->_connection->delete(
3284-
$mainTable,
3285-
$this->_connection->quoteInto('product_id IN (?)', array_unique($productIds))
3286-
);
3287-
}
3288-
if ($linkRows) {
3289-
$this->_connection->insertOnDuplicate($mainTable, $linkRows, ['link_id']);
3290-
}
3291-
if ($positionRows) {
3292-
// process linked product positions
3293-
$this->_connection->insertOnDuplicate(
3294-
$resource->getAttributeTypeTable('int'),
3295-
$positionRows,
3296-
['value']
3297-
);
3298-
}
3299-
}
3300-
3301-
/**
3302-
* Composes the link key
3303-
*
3304-
* @param int $productId
3305-
* @param int $linkedId
3306-
* @param int $linkTypeId
3307-
* @return string
3308-
*/
3309-
private function composeLinkKey(int $productId, int $linkedId, int $linkTypeId) : string
3310-
{
3311-
return "{$productId}-{$linkedId}-{$linkTypeId}";
3312-
}
3313-
33143122
/**
33153123
* Get row store ID
33163124
*

0 commit comments

Comments
 (0)