Magento є e-commerce рішенням, тобто. більше націлено на продаж продуктів, ніж на супутній продаж складський, логістичний або фінансовий облік. Для супутнього краще підходять інші програми (наприклад, ERP-системи). Тому досить часто в практиці використання Magento виникає завдання інтеграції магазину з іншими системами (наприклад, з 1С).
За великим рахунком інтеграцію можна звести до реплікації даних:
- каталогу (продукти, категорії);
- інвентарним даним (залишки товарів на складах та ціни);
- клієнтам;
- замовлень;
Magento для маніпуляції з даними в базі пропонує окремий клас об'єктів.
Клієнти та замовлення реплікуються, як правило, в інший бік — з 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 продукту програмним шляхом дуже нескладно. Все викладене вище я звів у демо-модульfl32:import:prod
, яка імпортує продукти, описані в JSON-файлі «
[
{
"sku": "...",
"name": "...",
"desc": "...",
"desc_short": "...",
"price": ...,
"qty": ...,
"categories": ["..."],
"image_path": "..."
}
]
Картинки для імпорту знаходяться у каталозі ./etc/data/img
.
Час імпорту 10 продуктів у такий спосіб становить близько 10 секунд на моєму ноутбуці. Якщо розвивати цю думку далі, то неважко дійти висновку, що за годину можна імпортувати близько 3600 товарів, але в імпорт 100К товарів може піти близько 30 годин. Заміна ноутбука на сервер дозволяє дещо згладити ситуацію. Можливо навіть у рази. Але не на порядок. Можливо ця швидкість повільність певною мірою є однією з причин появи проекту
Кардинальним рішенням для збільшення швидкості імпорту може стати прямий запис в базу, але в цьому випадку губляться всі «плюшки», що стосуються розширюваності Magento – доведеться все «розширене» робити самому. Тим не менш, воно того варте. Якщо вийде, то розгляну підхід із прямим записом у БД у наступній статті.
Джерело: habr.com