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

Дадаць каментар