Magento 2: import produktov priamo do databázy

В predchádzajúci článok Proces importu produktov do Magento 2 som opísal bežným spôsobom – cez modely a úložiská. Bežná metóda má veľmi nízku rýchlosť spracovania údajov. Môj laptop produkoval asi jeden produkt za sekundu. V tomto pokračovaní uvažujem o alternatívnom spôsobe importu produktu – priamym vstupom do databázy, obídením štandardných mechanizmov Magento 2 (modely, továrne, úložiská). Postupnosť krokov na import produktov je možné prispôsobiť akémukoľvek programovaciemu jazyku, ktorý dokáže pracovať s MySQL.

Vylúčenie zodpovednosti: Magento má hotové funkcie pre import údajov a s najväčšou pravdepodobnosťou vám to bude stačiť. Ak však potrebujete úplnejšiu kontrolu nad procesom importu, ktorý sa neobmedzuje len na prípravu súboru CSV pre to, čo máte, vitajte na stránke cat.

Magento 2: import produktov priamo do databázy

Kód vyplývajúci z písania oboch článkov si môžete pozrieť v module Magento "flancer32/mage2_ext_demo_import". Tu sú niektoré obmedzenia, ktoré som dodržiaval, aby som zjednodušil kód demo modulu:

  • Produkty sú len vytvorené, nie aktualizované.
  • Jeden sklad
  • Importujú sa iba názvy kategórií bez ich štruktúry
  • Dátové štruktúry zodpovedajú verzii 2.3

JSON na import jedného produktu:

{
  "sku": "MVA20D-UBV-3",
  "name": "Заглушка для пломбировки ВА47-29 IEK",
  "desc": "Обеспечение доступа к устройствам ...",
  "desc_short": "Заглушка для пломбировки ВА47-29 IEK предназначена для ...",
  "price": 5.00,
  "qty": 25,
  "categories": ["Категория 1", "Категория 2"],
  "image_path": "mva20d_ubv_3.png"
}

Prehľad hlavných fáz importu

  • registrácia samotného produktu
  • prepojenie medzi produktom a webovou stránkou
  • základné atribúty produktu (EAV)
  • skladové údaje (množstvo produktu na sklade)
  • médiá (obrázky)
  • prepojenie s katalógovými kategóriami

Registrácia produktu

Základné informácie o produkte nájdete v catalog_product_entity:

CREATE TABLE `catalog_product_entity` (
  `entity_id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Entity Id',
  `attribute_set_id` smallint(5) unsigned NOT NULL DEFAULT '0' COMMENT 'Attribute Set ID',
  `type_id` varchar(32) NOT NULL DEFAULT 'simple' COMMENT 'Type ID',
  `sku` varchar(64) DEFAULT NULL COMMENT 'SKU',
  `has_options` smallint(6) NOT NULL DEFAULT '0' COMMENT 'Has Options',
  `required_options` smallint(5) unsigned NOT NULL DEFAULT '0' COMMENT 'Required Options',
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Creation Time',
  `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'Update Time',
  PRIMARY KEY (`entity_id`),
  KEY `CATALOG_PRODUCT_ENTITY_ATTRIBUTE_SET_ID` (`attribute_set_id`),
  KEY `CATALOG_PRODUCT_ENTITY_SKU` (`sku`)
)

Minimálne informácie potrebné na vytvorenie záznamu v registri produktov sú:

  • attribute_set_id
  • sku

dodatočné:

  • type_id — ak to nešpecifikujeme, použije sa slovo „jednoduché“.

Na priamy zápis do databázy používam DB adaptér samotného Magenta:

function create($sku, $typeId, $attrSetId)
{
    /** @var MagentoFrameworkAppResourceConnection $this->resource */
    /** @var MagentoFrameworkDBAdapterPdoMysql $conn */
    $conn = $this->resource->getConnection();
    $table = $this->resource->getTableName('catalog_product_entity');
    $bind = [
        'sku' => $sku,
        'type_id' => $typeId,
        'attribute_set_id' => $attrSetId
    ];
    $conn->insert($table, $bind);
    $result = $conn->lastInsertId($table);
    return $result;
}

Po registrácii produktu s catalog_product_entity zobrazí sa na paneli správcu, v mriežke produktov (Katalóg/Produkty).

Magento 2: import produktov priamo do databázy

Vzťah medzi produktom a webovou stránkou

Priradenie produktu k stránke určuje, v ktorých predajniach a výstavách bude produkt dostupný na prednej strane.

function linkToWebsite($prodId, $websiteId)
{
    /** @var MagentoFrameworkAppResourceConnection $this->resource */
    /** @var MagentoFrameworkDBAdapterPdoMysql $conn */
    $conn = $this->resource->getConnection();
    $table = $this->resource->getTableName('catalog_product_website');
    $bind = [
        'product_id' => $prodId,
        'website_id' => $websiteId
    ];
    $conn->insert($table, $bind);
}

Magento 2: import produktov priamo do databázy

Základné vlastnosti produktu

Novo zaregistrovaný produkt ešte nemá názov ani popis. Toto všetko sa robí cez EAV atribúty. Tu je zoznam základných atribútov produktu, ktoré sú potrebné na to, aby sa produkt správne zobrazoval na prednej strane:

  • name
  • price
  • description
  • short_description
  • status
  • tax_class_id
  • url_key
  • visibility

K takémuto produktu sa pridá samostatný atribút (podrobnosti o získaní identifikátora a typu atribútu z jeho kódu sú vynechané):

public function create($prodId, $attrCode, $attrValue)
{
    $attrId = /* get attribute ID by attribute code */
    $attrType = /* get attribute type [datetime|decimal|int|text|varchar]) by attribute code */
    if ($attrId) {
        /** @var MagentoFrameworkAppResourceConnection $this->resource */
        /** @var MagentoFrameworkDBAdapterPdoMysql $conn */
        $conn = $this->resource->getConnection();
        $tblName = 'catalog_product_entity_' . $attrType;
        $table = $this->resource->getTableName($tblName);
        $bind = [
            'attribute_id' => $attrId,
            'entity_id' => $prodId,
            /* put all attributes to default store view with id=0 (admin) */
            'store_id' => 0,
            'value' => $attrValue
        ];
        $conn->insert($table, $bind);
    }
}

Pomocou kódu atribútu určíme jeho ID a typ údajov (datetime, decimal, int, text, varchar), potom zapíšte údaje pre administratívne okno do príslušnej tabuľky (store_id = 0).

Po pridaní vyššie uvedených atribútov k produktu získate tento obrázok v administračnom paneli:

Magento 2: import produktov priamo do databázy

Údaje o zásobách

Od verzie 2.3 v Magento existujú dve paralelné sady tabuliek, ktoré poskytujú ukladanie informácií o zásobách (množstvo produktu):

  • cataloginventory_*: stará štruktúra;
  • inventory_*: nová štruktúra (MSI - Multi Source Inventory);

K obom štruktúram musíte pridať údaje o zásobách, pretože nová štruktúra ešte nie je úplne nezávislá od starej (je veľmi pravdepodobné, že pre default sklad v novej štruktúre je zahrnutý stôl cataloginventory_stock_status ako inventory_stock_1).

katalógový inventár_

Pri nasadzovaní Magneto 2.3 máme na začiatku 2 položky store_website, čo zodpovedá dvom stránkam - administratívnemu a hlavnému klientovi:

website_id|code |name        |sort_order|default_group_id|is_default|
----------|-----|------------|----------|----------------|----------|
         0|admin|Admin       |         0|               0|         0|
         1|base |Main Website|         0|               1|         1|

Tabuľka cataloginventory_stock máme len jeden záznam:

stock_id|website_id|stock_name|
--------|----------|----------|
       1|         0|Default   |

To znamená, že v našej starej štruktúre je len jeden „sklad“ (stock) a je prepojený s administratívnou webovou stránkou. Pridávanie nových cez administračný panel sources/stocks v MSI (nová štruktúra) nevedie k novým záznamom v cataloginventory_stock.

Údaje o zásobách o výrobkoch v starej štruktúre sa spočiatku zaznamenávajú v tabuľkách:

  • cataloginventory_stock_item
  • cataloginventory_stock_status

katalógový_zásob_položky

function createOldItem($prodId, $qty)
{
    $isQtyDecimal = (((int)$qty) != $qty);
    $isInStock = ($qty > 0);
    /** @var MagentoFrameworkAppResourceConnection $this->resource */
    /** @var MagentoFrameworkDBAdapterPdoMysql $conn */
    $conn = $this->resource->getConnection();
    $table = $this->resource->getTableName('cataloginventory_stock_item');
    $bind = [
        'product_id' => $prodId,
        /* we use one only stock in 'cataloginventory' structure by default */
        'stock_id' => 1,
        'qty' => $qty,
        'is_qty_decimal' => $isQtyDecimal,
        'is_in_stock' => $isInStock,
        /* default stock is bound to admin website (see `cataloginventory_stock`) */
        'website_id' => 0
    ];
    $conn->insert($table, $bind);
}

katalógový_stav_skladov

function createOldStatus($prodId, $qty)
{
    $isInStock = ($qty > 0);
    /** @var MagentoFrameworkAppResourceConnection $this->resource */
    /** @var MagentoFrameworkDBAdapterPdoMysql $conn */
    $conn = $this->resource->getConnection();
    $table = $this->resource->getTableName('cataloginventory_stock_status');
    $bind = [
        'product_id' => $prodId,
        /* we use one only stock in 'cataloginventory' structure by default */
        'stock_id' => 1, 
        'qty' => $qty,
        'stock_status' => MagentoCatalogInventoryApiDataStockStatusInterface::STATUS_IN_STOCK,
        /* default stock is bound to admin website (see `cataloginventory_stock`) */
        'website_id' => 0 
    ];
    $conn->insert($table, $bind);
}

inventár_

Na začiatku nová štruktúra na ukladanie údajov o zásobách obsahuje 1 "zdroj"(inventory_source):

source_code|name          |enabled|description   |latitude|longitude|country_id|...|
-----------|--------------|-------|--------------|--------|---------|----------|...|
default    |Default Source|      1|Default Source|0.000000| 0.000000|US        |...|

a jeden "sklad"(inventory_stock):

stock_id|name         |
--------|-------------|
       1|Default Stock|

«Zdroj» predstavuje fyzické úložisko pre produkty (záznam obsahuje fyzické súradnice a poštovú adresu). "Sklad"je logickým spojením niekoľkých "zdrojov" (inventory_source_stock_link)

link_id|stock_id|source_code|priority|
-------|--------|-----------|--------|
      1|       1|default    |       1|

na úrovni, na ktorej dochádza k spojeniu s predajným kanálom (inventory_stock_sales_channel)

type   |code|stock_id|
-------|----|--------|
website|base|       1|

Podľa štruktúry údajov sa predpokladajú rôzne typy predajných kanálov, ale štandardne iba spojenie „sklad„-“webové stránky"(odkaz na webovú stránku nasleduje za kódom webovej stránky - base).

jeden"sklad"možno prepojiť s viacerými"na zdroje"a jeden "zdroj"- viacerým"sklady"(vzťah veľa k mnohým). Výnimky sú predvolené "zdroj"A"sklad". Nie sú opätovne prepojené s inými entitami (obmedzenie na úrovni kódu – chyba “Nie je možné uložiť odkaz súvisiaci s predvoleným zdrojom alebo predvoleným skladom"). Viac podrobností o štruktúre MSI v Magento 2 nájdete v článku “Systém riadenia skladu využívajúci CQRS a Event Sourcing. Dizajn".

Použijem predvolenú konfiguráciu a do zdroja pridám všetky informácie o inventári default, ktorý je zapojený do predajného kanála spojeného s webovou stránkou s kódom base (zodpovedá prednej časti predajne - viď store_website):

function createNewItem($sku, $qty)
{
    /** @var MagentoFrameworkAppResourceConnection $this->resource */
    /** @var MagentoFrameworkDBAdapterPdoMysql $conn */
    $conn = $this->resource->getConnection();
    $table = $this->resource->getTableName('inventory_source_item');
    $bind = [
        'source_code' => 'default',
        'sku' => $sku,
        'quantity' => $qty,
        'status' => MagentoInventoryApiApiDataSourceItemInterface::STATUS_IN_STOCK
    ];
    $conn->insert($table, $bind);
}

Po pridaní údajov o sklade k produktu na paneli správcu získate tento obrázok:

Magento 2: import produktov priamo do databázy

Médiá

Pri „ručnom“ pridávaní obrázka k produktu cez administračný panel sa príslušné informácie zapíšu do nasledujúcich tabuliek:

  • catalog_product_entity_media_gallery: register médií (obrázky a video súbory);
  • catalog_product_entity_media_gallery_value: prepojenie médií s produktmi a vitrínami (lokalizácia);
  • catalog_product_entity_media_gallery_value_to_entity: prepojenie médií iba s produktmi (pravdepodobne predvolený mediálny obsah pre produkt);
  • catalog_product_entity_varchar: Tu sú uložené roly, v ktorých sa obrázok používa;

a samotné obrázky sa uložia do adresára ./pub/media/catalog/product/x/y/Kde x и y — prvé a druhé písmeno názvu súboru obrázka. Napríklad súbor image.png by mal byť uložený ako ./pub/media/catalog/product/i/m/image.png, aby ho platforma mohla použiť ako obrázok pri popise produktov z katalógu.

Registrácia zverejnená v ./pub/media/catalog/product/ mediálny súbor (proces umiestňovania samotného súboru nie je v tomto článku diskutovaný):

function createMediaGallery($imgPathPrefixed)
{
    $attrId = /* get attribute ID by attribute code 'media_gallery' */
    /** @var MagentoFrameworkAppResourceConnection $this->resource */
    /** @var MagentoFrameworkDBAdapterPdoMysql $conn */
    $conn = $this->resource->getConnection();
    $table = $this->resource->getTableName('catalog_product_entity_media_gallery');
    $bind = [
        'attribute_id' => $attrId,
        'value' => $imgPathPrefixed,
        /* 'image' or 'video' */
        'media_type' => 'image',
        'disabled' => false
    ];
    $conn->insert($table, $bind);
    $result = $conn->lastInsertId($table);
    return $result;
}

Po registrácii sa novému mediálnemu súboru pridelí identifikátor.

Registrovaný mediálny súbor spájame s príslušným produktom pre predvolený výklad:

function createGalleryValue($mediaId, $prodId)
{
    /** @var MagentoFrameworkAppResourceConnection $this->resource */
    /** @var MagentoFrameworkDBAdapterPdoMysql $conn */
    $conn = $this->resource->getConnection();
    $table = $this->resource->getTableName('catalog_product_entity_media_gallery_value');
    $bind = [
        'value_id' => $mediaId,
        /* use admin store view by default */
        'store_id' => 0,
        'entity_id' => $prodId,
        'label' => null,
        /* we have one only image */
        'position' => 1,
        'disabled' => false
    ];
    $conn->insert($table, $bind);
}

Registrovaný mediálny súbor spájame s príslušným produktom bez toho, aby bol viazaný na akýkoľvek výklad. Nie je jasné, kde presne sa tieto údaje používajú a prečo nie je možné získať prístup k údajom z predchádzajúcej tabuľky, ale táto tabuľka existuje a údaje sa do nej zapíšu, keď sa k produktu pridá obrázok. Takže to je všetko.

function createGalleryValueToEntity($mediaId, $prodId)
{
    /** @var MagentoFrameworkAppResourceConnection $this->resource */
    /** @var MagentoFrameworkDBAdapterPdoMysql $conn */
    $conn = $this->resource->getConnection();
    $table = $this->resource->getTableName('catalog_product_entity_media_gallery_value_to_entity');
    $bind = [
        'value_id' => $mediaId,
        'entity_id' => $prodId
    ];
    $conn->insert($table, $bind);
}

Catalog_product_entity_varchar

Mediálny súbor možno použiť s rôznymi rolami (zodpovedajúci kód atribútu je uvedený v zátvorkách):

  • základňa(image)
  • Malý obrázok (small_image)
  • miniatúra (thumbnail)
  • Obrázok vzorky (swatch_image)

Prepojenie rolí s mediálnym súborom je presne to, čo sa deje v catalog_product_entity_varchar. Kód väzby je podobný kódu v "Základné vlastnosti produktu".

Po pridaní obrázka k produktu na paneli administrátora to vyzerá takto:

Magento 2: import produktov priamo do databázy

Категории

Hlavné tabuľky obsahujúce údaje podľa kategórie:

  • catalog_category_entity: register kategórií;
  • catalog_category_product: prepojenie medzi produktmi a kategóriami;
  • catalog_category_entity_*: hodnoty atribútov EAV;

Spočiatku v prázdnej aplikácii Magento obsahuje register kategórií 2 kategórie (skrátil som názvy stĺpcov: crt - created_at, upd - updated_at):

entity_id|attribute_set_id|parent_id|crt|upd|path|position|level|children_count|
---------|----------------|---------|---|---|----|--------|-----|--------------|
        1|               3|        0|...|...|1   |       0|    0|             1|
        2|               3|        1|...|...|1/2 |       1|    1|             0|

Kategória s id=1 je koreňom celého katalógu Magento a nie je dostupná ani v admin paneli, ani na titulnej stránke. Kategória s id=2 (predvolené kategórie) je koreňová kategória pre hlavný obchod hlavnej stránky (Hlavná stránka obchodu) vytvorený pri nasadení aplikácie (pozri. Správca / Obchody / Všetky obchody). Navyše, koreňová kategória samotného obchodu tiež nie je k dispozícii na prednej strane, iba jej podkategórie.

Keďže témou tohto článku je stále import údajov o produktoch, nebudem pri vytváraní kategórií využívať priamy vstup do databázy, ale využijem triedy, ktoré poskytuje samotné Magento (modely a úložiská). Priamy vstup do databázy sa používa iba na priradenie importovaného produktu ku kategórii (kategória sa zhoduje s jej názvom a počas priraďovania sa získava ID kategórie):

function create($prodId, $catId)
{
    /** @var MagentoFrameworkAppResourceConnection $this->resource */
    /** @var MagentoFrameworkDBAdapterPdoMysql $conn */
    $conn = $this->resource->getConnection();
    $table = $this->resource->getTableName('catalog_category_product');
    $bind = [
        'category_id' => $catId,
        'product_id' => $prodId,
    ];
    $conn->insert($table, $bind);
}

Po pridaní odkazu na produkt do kategórií „Kategória 1“ a „Kategória 2“ vyzerajú podrobnosti o produkte v administračnom paneli približne takto:

Magento 2: import produktov priamo do databázy

Ďalšie akcie

Po dokončení importu údajov musíte vykonať nasledujúce dodatočné kroky:

  • indexovanie údajov: volanie v konzole ./bin/magento indexer:reindex;
  • regenerácia URL pre produkty/kategórie: môžete použiť rozšírenie “elgentos/regenerate-catalog-urls«

Produkty na paneli správcu po vykonaní ďalších akcií:

Magento 2: import produktov priamo do databázy

a vpredu:

Magento 2: import produktov priamo do databázy

Zhrnutie

Rovnaká sada produktov (10 kusov) ako v predchádzajúcom článku sa dováža minimálne o rádovo rýchlejšie (1 sekunda oproti 10). Na presnejšie odhadnutie rýchlosti potrebujete väčší počet produktov – niekoľko stoviek, alebo ešte lepšie tisícky. Aj pri tak malej veľkosti vstupných údajov však môžeme konštatovať, že využitie nástrojov poskytovaných Magentom (modely a úložiská) je významné (zdôrazňujem - veľa!) urýchliť vývoj požadovanej funkcionality, no zároveň výrazne (zdôrazňujem - veľa!) znížiť rýchlosť, akou sa údaje dostávajú do databázy.

V dôsledku toho sa voda ukázala ako mokrá a to nie je zjavenie. Teraz však mám kód, s ktorým sa môžem pohrať a možno dospieť k zaujímavejším záverom.

Zdroj: hab.com