Magento 2: імпорт продуктів із зовнішніх джерел

Magento є e-commerce рішенням, тобто. більше націлено на продаж продуктів, ніж на супутній продаж складський, логістичний або фінансовий облік. Для супутнього краще підходять інші програми (наприклад, ERP-системи). Тому досить часто в практиці використання Magento виникає завдання інтеграції магазину з іншими системами (наприклад, з 1С).

За великим рахунком інтеграцію можна звести до реплікації даних:

  • каталогу (продукти, категорії);
  • інвентарним даним (залишки товарів на складах та ціни);
  • клієнтам;
  • замовлень;

Magento для маніпуляції з даними в базі пропонує окремий клас об'єктів. репозиторії. У силу специфіки Magento додавання даних до бази через репозиторії легко кодується, але відбувається, скажімо так, нешвидко. У даній публікації я розглядаю основні етапи програмного додавання до Magento 2 продукту «класичним» способом – з використанням репо-класів.

Клієнти та замовлення реплікуються, як правило, в інший бік — з Magento у зовнішні ERP-системи. Тому з ними простіше, на боці Magento потрібно просто вибрати відповідні дані, а далі -з нашого боку кулі вилетіли".

Принципи запису даних у базу

На даний момент створення об'єктів, що зберігаються в базі, програмним способом в Magento виробляється через завод:

function __construct (MagentoCmsModelBlockFactory $blockFactory) {
    $this->blockFactory = $blockFactory;
}

/** @var MagentoCmsModelBlock $block */
$block = $this->blockFactory->create();

а запис до бази - через Сховище:

function __construct (MagentoCmsApiBlockRepositoryInterface $blockRepo) {
    $this->blockRepo = $blockRepo;
}

$this->blockRepo->save($block);

Підхід із використанням «Factory» та «Repository» можна використовувати для всіх основних моделей у предметній області Magento 2.

Базова інформація про продукт

Я розглядаю структуру даних, яка відповідає версії Magento 2.3. Основна інформація про продукт знаходиться в таблиці catalog_product_entity (реєстр продуктів):

entity_id
attribute_set_id
type_id
sku
has_options
required_options
created_at
updated_at

Обмежуюсь одним типом продукту (type_id='simple'), набором атрибутів за замовчуванням (attribute_set_id=4) та ігнорую атрибути has_options и required_options. Оскільки атрибути entity_id, created_at и updated_at генеруються автоматично, то, по суті, нам для додавання нового продукту достатньо задати sku. Роблю так:

/** @var MagentoCatalogApiDataProductInterfaceFactory $factProd */
/** @var MagentoCatalogApiProductRepositoryInterface $repoProd */
/** @var MagentoCatalogApiDataProductInterface $prod */
$prod = $factProd->create();
$prod->setAttributeSetId(4);
$prod->setTypeId('simple');
$prod->setSku($sku);
$repoProd->save($prod);

і отримую виняток:

The "Product Name" attribute value is empty. Set the attribute and try again.

Додаю в запит ім'я продукту та отримую повідомлення, що не вистачає атрибуту Price. Після додавання ціни товар лягає в основу:

$prod = $factProd->create();
$prod->setAttributeSetId(4);
$prod->setTypeId('simple');
$prod->setSku($sku);
$prod->setName($name);
$prod->setPrice($price);
$repoProd->save($prod);

Назва продукту зберігається у таблиці varchar-атрибутів продукту (catalog_product_entity_varchar), ціна - у таблиці catalog_product_entity_decimal. Перед додаванням продукту бажано уявно вказати, що ми використовуємо адміністративну вітрину для імпорту даних:

/** @var MagentoStoreModelStoreManagerInterface $manStore */
$manStore->setCurrentStore(0);

Додаткові атрибути

Обробка додаткових атрибутів продуктів засобами Magento – одне задоволення. EAV-модель даних для основних сутностей (див. таблицю eav_entity_type) - Одна з ключових особливостей цієї платформи. Просто додаємо відповідні атрибути до моделі продукту:

$prodEntity->setData('description', $desc);
$prodEntity->setData('short_description', $desc_short);
// или
$prodEntity->setDescription($desc);
$prodEntity->setShortDescription($desc_short);

і при збереженні моделі через репо-об'єкт:

$repoProd->save($prod);

додаткові атрибути також будуть збережені у відповідних таблицях БД.

Інвентарні дані

Просто — кількість продукту на складі. У Magento 2.3 структури БД, що описують формат зберігання інвентарних даних, значно відрізняються від того, що було раніше. Тим не менш, додавання кількості продукту на складі через модель продукту не набагато складніше, ніж додавання інших атрибутів:

/** @var MagentoCatalogModelProduct $prodEntity */
/** @var MagentoCatalogApiProductRepositoryInterface $repoProd */
$inventory = [
    'is_in_stock' => true,
    'qty' => 1234
];
$prodEntity->setData('quantity_and_stock_status', $inventory);
$repoProd->save($prodEntity);

Медіа

Як правило, медіа-супровід продукту для клієнта в магазині (e-commerce) відрізняється від медіа-супроводу цього ж продукту для співробітника у внутрішній системі обліку (ERP). У першому випадку бажано показати "товар обличчям", у другому - достатньо дати загальне уявлення про продукт. Тим не менш, перенесення хоча б первинного зображення продукту досить поширене case під час імпорту даних.

При додаванні зображення через адмінку зображення спочатку зберігається в тимчасовому каталозі (./pub/media/tmp/catalog/product) і лише при збереженні продукту переміщається в медіа-каталог (./pub/media/catalog/product). Також при додаванні через адмінку зображенню виставляються теги image, small_image, thumbnail, swatch_image.

/** @var MagentoCatalogApiProductRepositoryInterface $repoProd */
/** @var MagentoCatalogModelProductGalleryCreateHandler $hndlGalleryCreate */
/* $imagePath = '/path/to/file.png';  $imagePathRelative = '/f/i/file.png' */
$imagePathRelative = $this->imagePlaceToTmpMedia($imagePath);
/* reload product with gallery data */
$product = $repoProd->get($sku);
/* add image to product's gallery */
$gallery['images'][] = [
    'file' => $imagePathRelative,
    'media_type' => 'image'
    'label' => ''
];
$product->setData('media_gallery', $gallery);
/* set usage areas */
$product->setData('image', $imagePathRelative);
$product->setData('small_image', $imagePathRelative);
$product->setData('thumbnail', $imagePathRelative);
$product->setData('swatch_image', $imagePathRelative);
/* create product's gallery */
$hndlGalleryCreate->execute($product);

Чомусь медіа підв'язується лише після попереднього збереження продукту та отримання його з репозиторію заново. І потрібно вказувати атрибут label при додаванні запису до медіа-галереї продукту (інакше отримуємо виняток Undefined index: label in .../module-catalog/Model/Product/Gallery/CreateHandler.php on line 516).

Категорії

Найчастіше структура категорій магазину та backend-програми або розміщення в них продуктів може значно відрізнятися. Стратегії перенесення даних про категорії та продукти в них залежать від багатьох факторів. У цьому прикладі я дотримуюсь наступного:

  • категорії backend'а та магазину зіставляються за назвою;
  • якщо імпортується категорія, якої немає у магазині, вона створюється під кореневою категорією (Default Category) та її подальше позиціонування в каталозі магазину передбачається вручну;
  • прив'язка продукту до категорії відбувається лише за його створення у магазині (першому імпорті);

Основна інформація про категорію знаходиться в таблиці catalog_category_entity (Каталог категорій). Створення категорії в Magento:

/** @var MagentoCatalogApiDataCategoryInterfaceFactory $factCat */
/** @var MagentoCatalogApiCategoryRepositoryInterface $repoCat */
$cat = $factCat->create();
$cat->setName($name);
$cat->setIsActive(true);
$repoCat->save($cat);

Прив'язка продукту до категорії здійснюється за ID категорії та SKU продукту:

/** @var MagentoCatalogModelCategoryProductLinkFactory $factCatProdLink */
/** @var MagentoCatalogApiCategoryLinkRepositoryInterface $repoCatLink */
$link = $factCatProdLink->create();
$link->setCategoryId($catMageId);
$link->setSku($prodSku);
$repoCatLink->save($link);

Разом

Написати код для додавання до Magento 2 продукту програмним шляхом дуже нескладно. Все викладене вище я звів у демо-модульflancer32/mage2_ext_demo_import«. У модулі лише одна консольна команда fl32:import:prod, яка імпортує продукти, описані в JSON-файлі «./etc/data/products.json":

[
  {
    "sku": "...",
    "name": "...",
    "desc": "...",
    "desc_short": "...",
    "price": ...,
    "qty": ...,
    "categories": ["..."],
    "image_path": "..."
  }
]

Картинки для імпорту знаходяться у каталозі ./etc/data/img.

Час імпорту 10 продуктів у такий спосіб становить близько 10 секунд на моєму ноутбуці. Якщо розвивати цю думку далі, то неважко дійти висновку, що за годину можна імпортувати близько 3600 товарів, але в імпорт 100К товарів може піти близько 30 годин. Заміна ноутбука на сервер дозволяє дещо згладити ситуацію. Можливо навіть у рази. Але не на порядок. Можливо ця швидкість повільність певною мірою є однією з причин появи проекту magento/async-import.

Кардинальним рішенням для збільшення швидкості імпорту може стати прямий запис в базу, але в цьому випадку губляться всі «плюшки», що стосуються розширюваності Magento – доведеться все «розширене» робити самому. Тим не менш, воно того варте. Якщо вийде, то розгляну підхід із прямим записом у БД у наступній статті.

Джерело: habr.com

Додати коментар або відгук