Description
The function upsertCategories may return a category id that is no longer present in the DB as upsertCategory on line 138 of Magento\CatalogImportExport\Model\Import\Product\CategoryProcessor fetches the category id from the local class cache ($this->categories) of the available categories.
This causes the import to fail if the category was inserted, added to the cache and then removed from the DB due to either a transaction being rolled back or the category being automatically deleted.
Transactions in this instance are probably being rolled back due to url_key conflicts.
Preconditions
- Magento 2.2.5
- PHP 7.1.20
- MySQL 5.7.23
Steps to reproduce
My best guess is that transactions are being rolled back due to url_key conflicts.
Refer to the issue #17586 for clarification on how to set up import data that will have unexpected URL rewrite conflicts.
It would take quite a bit of time to set up a full procedure, but let me know if it's absolutely required.
It should be obvious enough for anyone reviewing the code and referring to the previous bug report #17586
I have implemented a work-around for now, but this needs to be addressed and fixed in the core.
<?php
namespace {my_namespace}\Plugin\Magento\CatalogImportExport\Model\Import\Product;
use Magento\CatalogImportExport\Model\Import\Product\CategoryProcessor;
class CategoryProcessorPlugin
{
/**
* // NOTE: Must cache the failed categories here as Magento caches them even if they have failed and as
* a result won't add them to the failed categories list
* @var array
*/
protected static $failedCategoriesCache = [];
/**
* [afterUpsertCategories description]
* @param CategoryProcessor $categoryProcessor [description]
* @param [type] $categoryIds [description]
* @return [type] [description]
*/
public function afterUpsertCategories(CategoryProcessor $categoryProcessor, $categoryIds)
{
// NOTE: Work-around for a Magento bug where it's still adding failed categories to the list
if ($failedCategories = $categoryProcessor->getFailedCategories()) {
foreach ($failedCategories as $failedCategory) {
self::$failedCategoriesCache[] = $failedCategory['category']->getId();
}
}
$categoryIds = array_diff($categoryIds, self::$failedCategoriesCache);
return $categoryIds;
}
}
Expected result
The function upsertCategories should only ever return categories that actually exist.
Actual result
The function upsertCategories returns categories from the local class cache that have since been removed from the DB during the same import run.