Comments 6
/** @var \Magento\Catalog\Model\Product $prodEntity */
/** @var \Magento\Catalog\Api\ProductRepositoryInterface $repoProd */
$inventory = [
'is_in_stock' => true,
'qty' => 1234
];
$prodEntity->setData('quantity_and_stock_status', $inventory);
$repoProd->save($prodEntity);
Является устаревшим в Magento 2.3.
C появлением нового механизма инвентори (Magento MSI) данные инвентори отвязаны от сущности продукта и сохранять их предлагается через отдельные API эндпоинты:
https://github.com/magento-engcom/msi/tree/2.3-develop/app/code/Magento/InventoryApi/Api
Здесь можно почитать подробней о новом механизме инвентори — https://devdocs.magento.com/guides/v2.3/inventory/
Спасибо за дополнение. Ваш комментарий как раз и демонстрирует гибкость Magento. Старый подход позволяет просто указать кол-во продукта без необходимости разбираться в sources, stocks & sales channels. А сохранение данных происходит уже в MSI-структуру с использованием отдельных API endpoints (конкретно — \Magento\InventoryApi\Api\SourceItemsSaveInterface
/ \Magento\Inventory\Model\SourceItem\Command\SourceItemsSaveWithoutLegacySynchronization
). Привязка "нового" подхода к "старому" происходит в \Magento\CatalogInventory\Observer\SaveInventoryDataObserver
(метод execute). Вернее, в этом месте инвентарные данные отвязываются от продукта (они отвязывались и до версии 2.3), а непосредственно новые API вызовы подключаются в \Magento\InventoryCatalog\Plugin\CatalogInventory\UpdateSourceItemAtLegacyStockItemSavePlugin.
Здесь использованы сразу два основных механизма обеспечения гибкости в Magento — обсерверы и плагины.
У использования абстракции для импорта данных есть один минус — скорость, часто этот минус больше плюса по простоте реализации.
Время импорта 10 продуктов подобным способом составляет порядка 10 секунд на моём ноутбуке.
Импорт 100k+, 150k+ товаров превращается в кошмар и ужас при каждом обновлении.
И еще если кеш очистится из-за какого-то чудо-кода модуля и начинает тормозить фронт…
Самый надежный способ на практике:
1. Загрузка данных в транзитную transit_flat табличку тупеньким скриптиком.
2. Создаем товар создав строчки в «catalog_product_entity», «catalog_product_entity_int» для атрибутов visibility, status, tax_class.
3. catalog_category_product для привязки к категории, catalog_product_website для привязки к магазину.
4. И для каждого нужного атрибута:
Insert into catalog_product_entity{_int|_varchar|_text} from… transit.field_x as value select transit_flat transit inner join catalog_product_entity as cpe ON… on duplicate update…
5. Для склада все просто: все всегда лежит в «cataloginventory_stock_item», можно «cataloginventory_stock_status» обновить сразу и не ждать индексатора.
Описанный выше метод работает быстро так как все операции происходят в памяти базы данных mysql: search using index — O(log(N)), full scan — O(N), insert — O(1).
10-20 минут для каталога, который некоторые считают огромным, хватает.
Прошу прощения, что так сжато.
Надеюсь найдется время договориться по NDA, подготовить «консерву» быстрого импорта.
1) Сохраняет по одному продукту, при этом все будет в транзакциях. Фронт при импорте просто упадет.
2) Оперирует большим колличестовом моделейя, используется $product = $repoProd->get($sku); //загрузит все eav для одной записи
3) У Вас при сохранении начнет отрабатывать индексатор Eav, stock итд. Первые продукты импортируются быстро, а потом все подвиснит.
Для быстрого импорта, можно воспользоваться механизмом импорта из csv.
$file - название csv
$path - директория, где лежит csv.
/**
* @var $import \Magento\ImportExport\Model\Import
*/
$import = $this->importFactory->create();
$import->setData(
[
'entity' => 'catalog_product',
'behavior' => 'append',
'validation_strategy' => \Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregatorInterface::VALIDATION_STRATEGY_SKIP_ERRORS,
'allowed_error_count' => 100
]
);
$read_file = $this->readFactory->create($path);
$csvSource = $this->csvSourceFactory->create(
[
'file' => $file,
'directory' => $read_file,
]
);
$validate = $import->validateSource($csvSource);
if ($validate) {
try {
$result = $import->importSource();
} catch (\Throwable $e) {
$result = false;
}
....
Минусы тоже есть: по умолчанию, если поле пустое, то оно не «очищается», а пропускается. Но все лечится напильником.
Magento 2: импорт продуктов из внешних источников