Magento 2: внасяне на продукти от външни източници

Magento е решение за електронна търговия, т.е. повече е ориентирано към продажба на продукти, отколкото на съпътстващ счетоводство за складове, логистика или финанси. За съпътстващите е по-подходящо да се използват други приложения (например, ERP системи). Затова често в практиката на използване на Magento възниква необходимост от интеграция на магазина с тези системи (например, с 1С).

В общи линии интеграцията може да се сведе до репликация на данни по:

  • каталога (продукти, категории);
  • инвентарни данни (остатъци на продукти в складовете и цени);
  • клиенти;
  • поръчки;

Magento предлага отделен клас обекти за манипулация с данни в базата — репозитории. Поради спецификата на Magento добавянето на данни в базата през репозитории е лесно за кодиране, но се случва, да речем, не много бързо. В тази публикация разглеждам основните етапи за софтуерно добавяне на продукт в Magento 2 по "класически" начин — с помощта на репо-класове.

Клиентите и поръчките обикновено се репликират в друга посока — от Magento към външните ERP системи. Затова с тях е по-лесно, от страна на Magento просто се избира съответните данни, а след това — "от наша страна куршумите излязоха«.

Принципи за запис на данни в базата

В момента създаването на обекти, които се запазват в базата, по програмния метод в Magento става чрез Factory:

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

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

а записът в базата — чрез Repository:

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);

и получавам изключение:

Стойността на атрибута "Име на продукта" е празна. Задайте атрибута и опитайте отново.

Добавям името на продукта в заявката и получавам съобщение, че липсва атрибут Цена. След добавяне на цената продуктът се записва в базата данни:

$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). В първия случай е желателно да покажете "продукта открито", докато във втория — е достатъчно да предоставите обща представа за продукта. Въпреки това, прехвърлянето на поне основното изображение на продукта е доста разпространено случай при импортиране на данни.

При добавяне на изображение през админ панела, картинката първо се записва в времевия каталог (./pub/media/tmp/catalog/product) и само при запазване на продукта се премества в медийната директория (./pub/media/catalog/product). Освен това, при добавянето през админ панела, на изображението се задават тагове изображение, 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 при добавяне на запис в медийната галерия на продукта (в противен случай получаваме изключение Неопределен индекс: label в .../module-catalog/Model/Product/Gallery/CreateHandler.php на ред 516).

Категории

Често структурата на категориите в магазина и backend приложението или разположението на продуктите в тях може значително да се различава. Стратегиите за пренос на данни за категориите и продуктите в тях зависят от множество фактори. В този пример се ръководя от следното:

  • категориите на backend и магазина се сопоставят по име;
  • ако се импортира категория, която няма в магазина, тя се създава под коренова категория (Основна категория) и нейното по-нататъшно позициониране в каталога на магазина се предвижда ръчно;
  • привързването на продукта към категорията става само при неговото създаване в магазина (първия импорт);

Основната информация за категорията се намира в таблицата 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

Купете надежден хостинг за сайтове с защита от DDoS, VPS VDS сървъри 🔥 Купете надежден хостинг за сайтове с защита от DDoS, VPS VDS сървъри | ProHoster