Skip to content

Commit c53b82b

Browse files
authored
ENGCOM-7035: Refactor addlinks to own class take 3 (follows #21658) #23191
2 parents c3b42db + 30c3417 commit c53b82b

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)