|
11 | 11 | use Magento\Catalog\Model\Product\Visibility;
|
12 | 12 | use Magento\Catalog\Model\ResourceModel\Product\Link;
|
13 | 13 | use Magento\CatalogImportExport\Model\Import\Product\ImageTypeProcessor;
|
| 14 | +use Magento\CatalogImportExport\Model\Import\Product\LinkProcessor; |
14 | 15 | use Magento\CatalogImportExport\Model\Import\Product\MediaGalleryProcessor;
|
15 | 16 | use Magento\CatalogImportExport\Model\Import\Product\RowValidatorInterface as ValidatorInterface;
|
16 | 17 | use Magento\CatalogImportExport\Model\Import\Product\StatusProcessor;
|
@@ -224,6 +225,8 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
|
224 | 225 | /**
|
225 | 226 | * Links attribute name-to-link type ID.
|
226 | 227 | *
|
| 228 | + * @deprecated use DI for LinkProcessor class if you want to add additional types |
| 229 | + * |
227 | 230 | * @var array
|
228 | 231 | */
|
229 | 232 | protected $_linkNameToId = [
|
@@ -758,6 +761,11 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
|
758 | 761 | */
|
759 | 762 | private $stockProcessor;
|
760 | 763 |
|
| 764 | + /** |
| 765 | + * @var LinkProcessor |
| 766 | + */ |
| 767 | + private $linkProcessor; |
| 768 | + |
761 | 769 | /**
|
762 | 770 | * @param \Magento\Framework\Json\Helper\Data $jsonHelper
|
763 | 771 | * @param \Magento\ImportExport\Helper\Data $importExportData
|
@@ -805,6 +813,7 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
|
805 | 813 | * @param ProductRepositoryInterface|null $productRepository
|
806 | 814 | * @param StatusProcessor|null $statusProcessor
|
807 | 815 | * @param StockProcessor|null $stockProcessor
|
| 816 | + * @param LinkProcessor|null $linkProcessor |
808 | 817 | * @throws LocalizedException
|
809 | 818 | * @throws \Magento\Framework\Exception\FileSystemException
|
810 | 819 | * @SuppressWarnings(PHPMD.ExcessiveParameterList)
|
@@ -856,7 +865,8 @@ public function __construct(
|
856 | 865 | DateTimeFactory $dateTimeFactory = null,
|
857 | 866 | ProductRepositoryInterface $productRepository = null,
|
858 | 867 | StatusProcessor $statusProcessor = null,
|
859 |
| - StockProcessor $stockProcessor = null |
| 868 | + StockProcessor $stockProcessor = null, |
| 869 | + LinkProcessor $linkProcessor = null |
860 | 870 | ) {
|
861 | 871 | $this->_eventManager = $eventManager;
|
862 | 872 | $this->stockRegistry = $stockRegistry;
|
@@ -896,6 +906,10 @@ public function __construct(
|
896 | 906 | ->get(StatusProcessor::class);
|
897 | 907 | $this->stockProcessor = $stockProcessor ?: ObjectManager::getInstance()
|
898 | 908 | ->get(StockProcessor::class);
|
| 909 | + $this->linkProcessor = $linkProcessor ?? ObjectManager::getInstance() |
| 910 | + ->get(LinkProcessor::class); |
| 911 | + $this->linkProcessor->addNameToIds($this->_linkNameToId); |
| 912 | + |
899 | 913 | parent::__construct(
|
900 | 914 | $jsonHelper,
|
901 | 915 | $importExportData,
|
@@ -1133,14 +1147,15 @@ protected function _replaceProducts()
|
1133 | 1147 | * Save products data.
|
1134 | 1148 | *
|
1135 | 1149 | * @return $this
|
| 1150 | + * @throws LocalizedException |
1136 | 1151 | */
|
1137 | 1152 | protected function _saveProductsData()
|
1138 | 1153 | {
|
1139 | 1154 | $this->_saveProducts();
|
1140 | 1155 | foreach ($this->_productTypeModels as $productTypeModel) {
|
1141 | 1156 | $productTypeModel->saveData();
|
1142 | 1157 | }
|
1143 |
| - $this->_saveLinks(); |
| 1158 | + $this->linkProcessor->saveLinks($this, $this->_dataSourceModel, $this->getProductEntityLinkField()); |
1144 | 1159 | $this->_saveStockItem();
|
1145 | 1160 | if ($this->_replaceFlag) {
|
1146 | 1161 | $this->getOptionEntity()->clearProductsSkuToId();
|
@@ -1274,30 +1289,13 @@ protected function _prepareRowForDb(array $rowData)
|
1274 | 1289 | *
|
1275 | 1290 | * Must be called after ALL products saving done.
|
1276 | 1291 | *
|
| 1292 | + * @deprecated use linkProcessor Directly |
| 1293 | + * |
1277 | 1294 | * @return $this
|
1278 | 1295 | */
|
1279 | 1296 | protected function _saveLinks()
|
1280 | 1297 | {
|
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()); |
1301 | 1299 | return $this;
|
1302 | 1300 | }
|
1303 | 1301 |
|
@@ -1534,14 +1532,19 @@ public function getImagesFromRow(array $rowData)
|
1534 | 1532 | return [$images, $labels];
|
1535 | 1533 | }
|
1536 | 1534 |
|
| 1535 | + // phpcs:disable Generic.Metrics.NestingLevel |
| 1536 | + |
1537 | 1537 | /**
|
1538 | 1538 | * Gather and save information about product entities.
|
1539 | 1539 | *
|
| 1540 | + * FIXME: Reduce nesting level |
| 1541 | + * |
1540 | 1542 | * @return $this
|
1541 | 1543 | * @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
1542 | 1544 | * @SuppressWarnings(PHPMD.NPathComplexity)
|
1543 | 1545 | * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
|
1544 | 1546 | * @SuppressWarnings(PHPMD.UnusedLocalVariable)
|
| 1547 | + * @SuppressWarnings(PHPMD.UnusedLocalVariable) |
1545 | 1548 | * @throws LocalizedException
|
1546 | 1549 | * phpcs:disable Generic.Metrics.NestingLevel.TooHigh
|
1547 | 1550 | */
|
@@ -1929,6 +1932,8 @@ protected function _saveProducts()
|
1929 | 1932 | }
|
1930 | 1933 | //phpcs:enable Generic.Metrics.NestingLevel
|
1931 | 1934 |
|
| 1935 | + // phpcs:enable |
| 1936 | + |
1932 | 1937 | /**
|
1933 | 1938 | * Prepare array with image states (visible or hidden from product page)
|
1934 | 1939 | *
|
@@ -3114,203 +3119,6 @@ private function getValidationErrorLevel($sku): string
|
3114 | 3119 | : ProcessingError::ERROR_LEVEL_NOT_CRITICAL;
|
3115 | 3120 | }
|
3116 | 3121 |
|
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 |
| - |
3314 | 3122 | /**
|
3315 | 3123 | * Get row store ID
|
3316 | 3124 | *
|
|
0 commit comments