Magento 2: uvozite izdelke neposredno v bazo podatkov

В prejšnji članek Postopek uvoza izdelkov v Magento 2 sem opisal na običajen način – preko modelov in repozitorijev. Običajna metoda ima zelo nizko hitrost obdelave podatkov. Moj prenosnik je proizvajal približno en izdelek na sekundo. V tem nadaljevanju razmišljam o alternativnem načinu uvoza izdelka - z neposrednim vnosom v bazo podatkov, mimo standardnih mehanizmov Magento 2 (modeli, tovarne, repozitoriji). Zaporedje korakov za uvoz izdelkov je mogoče prilagoditi kateremu koli programskemu jeziku, ki lahko deluje z MySQL.

Zavrnitev odgovornosti: Magento ima že pripravljeno funkcionalnost za uvoz podatkov in najverjetneje vam bo dovolj. Če pa potrebujete popolnejši nadzor nad postopkom uvoza, ki ni omejen na pripravo datoteke CSV za to, kar imate, dobrodošli v cat.

Magento 2: uvozite izdelke neposredno v bazo podatkov

Kodo, ki je nastala pri pisanju obeh člankov, si lahko ogledate v modulu Magento "flancer32/mage2_ext_demo_import". Tukaj je nekaj omejitev, ki sem jih upošteval za poenostavitev kode demo modula:

  • Izdelki se samo ustvarjajo, ne posodabljajo.
  • Eno skladišče
  • Uvožena so samo imena kategorij, brez njihove strukture
  • Podatkovne strukture so skladne z različico 2.3

JSON za uvoz enega izdelka:

{
  "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"
}

Pregled glavnih faz uvoza

  • registracija samega izdelka
  • povezava med izdelkom in spletno stranjo
  • osnovne lastnosti izdelka (EAV)
  • podatki o zalogi (količina izdelka na zalogi)
  • mediji (slike)
  • povezava s kataloškimi kategorijami

Registracija izdelka

Osnovne informacije o izdelku najdete 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`)
)

Minimalne informacije, potrebne za ustvarjanje vnosa v register izdelkov, so:

  • attribute_set_id
  • sku

dodatno:

  • type_id — če tega ne navedemo, bo uporabljen 'preprost'

Za neposredno pisanje v bazo podatkov uporabljam adapter DB samega 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 registraciji izdelka pri catalog_product_entity postane viden na skrbniški plošči, v mreži izdelkov (Katalog/Izdelki).

Magento 2: uvozite izdelke neposredno v bazo podatkov

Odnos med izdelkom in spletnim mestom

Povezava izdelka s spletnim mestom določa, v katerih trgovinah in razstavah bo izdelek na voljo na sprednji strani.

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: uvozite izdelke neposredno v bazo podatkov

Osnovne lastnosti izdelka

Na novo registriran izdelek še nima imena ali opisa. Vse to se izvaja skozi EAV atributi. Tukaj je seznam osnovnih atributov izdelka, ki so potrebni, da je izdelek pravilno prikazan na sprednji strani:

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

Izdelku, kot je ta, je dodan ločen atribut (podrobnosti o pridobivanju identifikatorja in vrste atributa iz njegove kode so izpuščene):

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

S kodo atributa določimo njegov ID in tip podatkov (datetime, decimal, int, text, varchar), nato zapišite podatke za skrbniško okno v ustrezno tabelo (store_id = 0).

Ko izdelku dodate zgornje atribute, dobite to sliko na skrbniški plošči:

Magento 2: uvozite izdelke neposredno v bazo podatkov

Podatki o zalogah

Od različice 2.3 dalje v Magentu obstajata dva vzporedna niza tabel, ki zagotavljata shranjevanje informacij o inventarju (količina izdelkov):

  • cataloginventory_*: stara zgradba;
  • inventory_*: nova struktura (MSI - Multi Source Inventory);

Obema strukturama morate dodati podatke o inventarju, ker nova struktura še ni povsem neodvisna od stare (zelo verjetno je za default skladišče v novi strukturi je vključena miza cataloginventory_stock_status kot inventory_stock_1).

kataloški inventar_

Pri uvajanju Magneta 2.3 imamo na začetku 2 vnosa store_website, ki ustreza dvema mestoma - administrativni in glavni stranki:

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

Tabela cataloginventory_stock imamo samo en vnos:

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

To pomeni, da je v naši stari strukturi samo eno "skladišče" (stock) in je povezan z administrativno spletno stranjo. Dodajanje novih prek skrbniške plošče sources/stocks v MSI (nova struktura) ne povzroči novih vnosov v cataloginventory_stock.

Inventurni podatki o izdelkih v stari strukturi so na začetku zapisani v tabelah:

  • cataloginventory_stock_item
  • cataloginventory_stock_status

catalogueinventory_stock_item

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

kataloginventory_stock_status

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

inventar_

Na začetku nova struktura za shranjevanje podatkov o inventarju vsebuje 1 "Vir"(inventory_source):

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

in ena"skladišče"(inventory_stock):

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

«Vir» predstavlja fizično skladišče za izdelke (zapis vsebuje fizične koordinate in poštni naslov). "Skladišče"je logična zveza več" virov "(inventory_source_stock_link)

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

na ravni, na kateri pride do povezave s prodajnim kanalom (inventory_stock_sales_channel)

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

Po strukturi podatkov sodeč se predvidevajo različne vrste prodajnih kanalov, privzeto pa le povezava »zaloge"-"spletna stran"(povezava do spletnega mesta sledi kodi spletnega mesta - base).

ena"skladišče"lahko se poveže z več"viri"in ena"Vir" - na več "skladišča"(razmerje veliko proti mnogo). Izjeme so privzete "Vir"In"skladišče". Niso ponovno povezani z drugimi subjekti (omejitev na ravni kode - napaka “Povezave, povezane s privzetim virom ali privzeto zalogo, ni mogoče shraniti"). Več podrobnosti o strukturi MSI v Magento 2 najdete v članku “Sistem vodenja skladišč z uporabo CQRS in Event Sourcing. Oblikovanje".

Uporabil bom privzeto konfiguracijo in viru dodal vse informacije o inventarju default, ki je vključen v prodajni kanal, povezan s spletno stranjo s kodo base (ustreza sprednjemu delu trgovine - glejte 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);
}

Ko izdelku na skrbniški plošči dodate podatke o inventarju, dobite to sliko:

Magento 2: uvozite izdelke neposredno v bazo podatkov

Mediji

Pri “ročnem” dodajanju slike izdelku preko skrbniške plošče so ustrezni podatki zapisani v naslednjih tabelah:

  • catalog_product_entity_media_gallery: register medijev (slike in video datoteke);
  • catalog_product_entity_media_gallery_value: povezovanje medijev z izdelki in izložbami (lokalizacija);
  • catalog_product_entity_media_gallery_value_to_entity: samo povezovanje medijev z izdelki (verjetno privzeta medijska vsebina za izdelek);
  • catalog_product_entity_varchar: Tukaj so shranjene vloge, v katerih se uporablja slika;

in slike same se shranijo v imenik ./pub/media/catalog/product/x/y/Če x и y — prva in druga črka imena slikovne datoteke. Na primer datoteka image.png je treba shraniti kot ./pub/media/catalog/product/i/m/image.png, tako da jo lahko platforma uporabi kot sliko pri opisovanju izdelkov iz kataloga.

Register objavljen v ./pub/media/catalog/product/ predstavnostna datoteka (sam postopek namestitve datoteke v tem članku ni obravnavan):

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

Ob registraciji se novi medijski datoteki dodeli identifikator.

Registrirano medijsko datoteko povežemo z ustreznim izdelkom za privzeto trgovino:

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

Registrirano predstavnostno datoteko povežemo z ustreznim izdelkom, ne da bi bili vezani na katero koli trgovino. Ni jasno, kje točno se ti podatki uporabljajo in zakaj je nemogoče dostopati do podatkov iz prejšnje tabele, vendar ta tabela obstaja in se podatki vanjo zapišejo, ko se izdelku doda slika. Torej to je to.

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

Medijska datoteka se lahko uporablja z različnimi vlogami (ustrezna koda atributa je navedena v oklepaju):

  • Osnova (image)
  • Majhna slika (small_image)
  • Sličica (thumbnail)
  • Vzorčna slika (swatch_image)

Povezovanje vlog z medijsko datoteko je točno to, kar se zgodi v catalog_product_entity_varchar. Vezna koda je podobna kodi v "Osnovne lastnosti izdelka".

Po dodajanju slike izdelku na skrbniški plošči je videti takole:

Magento 2: uvozite izdelke neposredno v bazo podatkov

Категории

Glavne tabele s podatki po kategorijah:

  • catalog_category_entity: register kategorij;
  • catalog_category_product: povezava med izdelki in kategorijami;
  • catalog_category_entity_*: vrednosti atributa EAV;

Na začetku v prazni aplikaciji Magento register kategorij vsebuje 2 kategoriji (imena stolpcev sem skrajšal: 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|

Kategorija z id=1 je koren celotnega kataloga Magento in ni na voljo niti v skrbniški plošči niti na prvi strani. Kategorija z id=2 (Privzeta kategorija) je korenska kategorija za glavno trgovino glavnega mesta (Glavna spletna trgovina), ustvarjeno ob uvedbi aplikacije (glejte. Admin / Trgovine / Vse trgovine). Poleg tega korenska kategorija same trgovine prav tako ni na voljo na sprednji strani, le njene podkategorije.

Ker je tema tega članka še vedno uvoz podatkov o izdelkih, pri ustvarjanju kategorij ne bom uporabljal neposrednega vnosa v bazo, ampak bom uporabil razrede, ki jih ponuja sam Magento (modeli in repozitoriji). Neposredni vnos v zbirko podatkov se uporablja samo za povezovanje uvoženega izdelka s kategorijo (kategorija se ujema z imenom, ID kategorije pa se pridobi med ujemanjem):

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 dodajanju povezave do izdelka v kategoriji »Kategorija 1« in »Kategorija 2« so podrobnosti o izdelku na skrbniški plošči videti nekako takole:

Magento 2: uvozite izdelke neposredno v bazo podatkov

Dodatni ukrepi

Ko je uvoz podatkov končan, morate opraviti naslednje dodatne korake:

  • indeksiranje podatkov: pokličite v konzoli ./bin/magento indexer:reindex;
  • ponovno generiranje URL-jev za izdelke/kategorije: uporabite lahko razširitev “elgentos/regeneriraj-urade-katalogov«

Izdelki v skrbniški plošči po izvedbi dodatnih dejanj:

Magento 2: uvozite izdelke neposredno v bazo podatkov

in spredaj:

Magento 2: uvozite izdelke neposredno v bazo podatkov

Povzetek

Isti nabor izdelkov (10 kosov) kot v prejšnjem članku se uvozi vsaj za red velikosti hitreje (1 sekunda proti 10). Za natančnejšo oceno hitrosti potrebujete večje število izdelkov - nekaj sto ali še bolje na tisoče. Vendar pa lahko tudi pri tako majhnem obsegu vhodnih podatkov sklepamo, da je uporaba orodij, ki jih ponuja Magento (modeli in repozitoriji), pomembna (poudarjam - veliko!) pospešiti razvoj zahtevane funkcionalnosti, a hkrati bistveno (poudarjam - veliko!) zmanjšajte hitrost, s katero podatki pridejo v bazo podatkov.

Posledično se je voda izkazala za mokro in to ni razodetje. Zdaj pa imam kodo, s katero se lahko igram in morda pridem do zanimivejših zaključkov.

Vir: www.habr.com