Magento 2: importu produktojn rekte en la datumbazon

В antaŭa artikolo Mi priskribis la procezon de importado de produktoj en Magento 2 laŭ la kutima maniero - per modeloj kaj deponejoj. La kutima metodo havas tre malaltan datumtraktadrapidecon. Mia tekkomputilo produktis proksimume unu produkton je sekundo. En ĉi tiu daŭrigo, mi konsideras alternativan manieron importi produkton - per rekta eniro en la datumbazon, preterirante la normajn mekanismojn Magento 2 (modeloj, fabrikoj, deponejoj). La sekvenco de paŝoj por importi produktojn povas esti adaptita al iu ajn programlingvo kiu povas funkcii kun MySQL.

malgarantio: Magento havas pretajn funkciojn por importado de datumoj kaj, plej verŝajne, ĝi sufiĉos por vi. Tamen, se vi bezonas pli kompletan kontrolon pri la importprocezo, ne limigita al preparado de CSV-dosiero por tio, kion vi havas, bonvenon al kato.

Magento 2: importu produktojn rekte en la datumbazon

La kodo rezultanta de skribado de ambaŭ artikoloj videblas en la Magento-modulo "flancer32/mage2_ext_demo_import". Jen kelkaj limigoj, kiujn mi sekvis por simpligi la demomodulkodon:

  • Produktoj estas nur kreitaj, ne ĝisdatigitaj.
  • Unu magazeno
  • Nur kategorinomoj estas importitaj, sen ilia strukturo
  • Datumaj strukturoj konformas al versio 2.3

JSON por importi ununuran produkton:

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

Superrigardo de la ĉefaj etapoj de importado

  • registrado de la produkto mem
  • konekto inter produkto kaj retejo
  • Bazaj produktaj atributoj (EAV)
  • inventardatumoj (kvanto de produkto en stoko)
  • amaskomunikilaro (bildoj)
  • rilato kun katalogaj kategorioj

Produkta Registrado

Bazaj produktaj informoj troveblas en 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`)
)

La minimuma informo necesa por krei eniron en la produkta registro estas:

  • attribute_set_id
  • sku

pliaj:

  • type_id — se ni ne specifas ĝin, tiam 'simple' estos uzata

Por skribi rekte al la datumbazo, mi uzas la DB-adaptilon de Magento mem:

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

Post registri la produkton kun catalog_product_entity ĝi fariĝas videbla en la administra panelo, en la produkta krado (Katalogo/Produktoj).

Magento 2: importu produktojn rekte en la datumbazon

Rilato inter produkto kaj retejo

La asocio de la produkto kun la retejo determinas en kiuj vendejoj kaj ekranoj la produkto estos disponebla ĉe la fronto.

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: importu produktojn rekte en la datumbazon

Bazaj produktaj atributoj

La lastatempe registrita produkto ankoraŭ ne havas nomon aŭ priskribon. Ĉio ĉi estas farita tra EAV-atributoj. Jen listo de bazaj produktaj atributoj, kiuj necesas por ke la produkto estu ĝuste montrita ĉe la fronto:

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

Aparta atributo estas aldonita al produkto kiel ĉi tiu (la detaloj pri akiro de la identigilo kaj speco de la atributo de ĝia kodo estas preterlasitaj):

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

Uzante la atributan kodon, ni determinas ĝian id kaj datumtipo (datetime, decimal, int, text, varchar), tiam skribu la datumojn por la administra fenestro en la taŭgan tabelon (store_id = 0).

Post aldoni la suprajn atributojn al la produkto, vi ricevas ĉi tiun bildon en la administra panelo:

Magento 2: importu produktojn rekte en la datumbazon

Invenraj datumoj

Komencante de versio 2.3 en Magento, ekzistas du paralelaj aroj de tabeloj, kiuj provizas stokajn informojn pri stokregistro (produkta kvanto):

  • cataloginventory_*: malnova strukturo;
  • inventory_*: nova strukturo (MSI - Multi Source Inventory);

Vi devas aldoni inventarajn datumojn al ambaŭ strukturoj, ĉar la nova strukturo ankoraŭ ne estas tute sendependa de la malnova (tre verŝajne ke por default magazeno en la nova strukturo tablo estas implikita cataloginventory_stock_status kiel inventory_stock_1).

katalogoinventario_

Dum deplojado de Magneto 2.3 ni komence havas 2 enirojn store_website, kiu respondas al du retejoj - administra kaj ĉefa kliento:

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

Tablo cataloginventory_stock ni havas nur unu enskribon:

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

Tio estas, en nia malnova strukturo estas nur unu "stokejo" (stock) kaj ĝi estas ligita al la administra retejo. Aldonante novajn tra la administra panelo sources/stocks en MSI (nova strukturo) ne rezultigas novajn enskribojn cataloginventory_stock.

Stokregistraj datumoj pri produktoj en la malnova strukturo estas komence registritaj en tabeloj:

  • cataloginventory_stock_item
  • cataloginventory_stock_status

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

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

inventaro_

Komence, la nova strukturo por stoki inventardatumojn enhavas 1 "fonto"(inventory_source):

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

kaj unu"magazeno"(inventory_stock):

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

«Fonto» reprezentas la fizikan stokadon por produktoj (la registro enhavas fizikajn koordinatojn kaj poŝtadreson). "Stokejo"estas logika kuniĝo de pluraj "fontoj" (inventory_source_stock_link)

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

je la nivelo ĉe kiu okazas la ligo al la venda kanalo (inventory_stock_sales_channel)

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

Juĝante laŭ la datumstrukturo, diversaj specoj de vendaj kanaloj estas supozitaj, sed defaŭlte nur la konekto "provizo"-"retejo"(la ligilo al la retejo sekvas la retejo-kodon - base).

Unu"magazeno"povas esti ligita al pluraj"al fontoj"kaj unu"fonto"- al pluraj"magazenoj"(multaj-al-multaj rilato). La esceptoj estas defaŭltaj "fonto"Kaj"magazeno". Ili ne estas religitaj al aliaj estaĵoj (limigo ĉe la kodnivelo - la eraro "Ne povas konservi ligon rilatan al Defaŭlta Fonto aŭ Defaŭlta Stoko"). Pli da detaloj pri la MSI-strukturo en Magento 2 troveblas en la artikolo "Mastruma sistemo de magazeno uzante CQRS kaj Event Sourcing. Dezajno".

Mi uzos la defaŭltan agordon kaj aldonos ĉiujn inventarajn informojn al la fonto default, kiu estas implikita en la venda kanalo asociita kun la retejo kun la kodo base (korespondas al la antaŭa fino de la vendejo - vidu 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);
}

Post aldoni stokajn datumojn al la produkto en la administra panelo, vi ricevas ĉi tiun bildon:

Magento 2: importu produktojn rekte en la datumbazon

Amaskomunikilaro

Kiam "mane" aldonas bildon al produkto per la administra panelo, la koncernaj informoj estas notitaj en la sekvaj tabeloj:

  • catalog_product_entity_media_gallery: amaskomunikila registro (bildoj kaj videodosieroj);
  • catalog_product_entity_media_gallery_value: ligi amaskomunikilaron al produktoj kaj montrofenestroj (lokigo);
  • catalog_product_entity_media_gallery_value_to_entity: ligi amaskomunikilaron al produktoj nur (supozeble defaŭlta amaskomunikila enhavo por la produkto);
  • catalog_product_entity_varchar: La roloj en kiuj la bildo estas uzata estas konservitaj ĉi tie;

kaj la bildoj mem estas konservitaj al la dosierujo ./pub/media/catalog/product/x/y/kie x и y — la unua kaj dua literoj de la bildodosiernomo. Ekzemple, dosiero image.png devus esti konservita kiel ./pub/media/catalog/product/i/m/image.png, por ke la platformo povu uzi ĝin kiel bildon priskribante produktojn de la katalogo.

Registriĝi afiŝita ./pub/media/catalog/product/ amaskomunikila dosiero (la procezo de metado de la dosiero mem ne estas diskutita en ĉi tiu artikolo):

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

Kiam registrite, nova amaskomunikila dosiero ricevas identigilon.

Ni asocias la registritan amaskomunikilaron kun la responda produkto por la defaŭlta vendejo:

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

Ni asocias la registritan amaskomunikilan dosieron kun la responda produkto sen esti ligitaj al iu ajn vendejo. Ne estas klare, kie ĝuste ĉi tiuj datumoj estas uzataj kaj kial estas neeble aliri la datumojn de la antaŭa tabelo, sed ĉi tiu tabelo ekzistas kaj la datumoj estas skribitaj al ĝi kiam bildo estas aldonita al la produkto. Do jen.

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

Multimedia dosiero povas esti uzata kun malsamaj roloj (la responda atributkodo estas indikita inter krampoj):

  • Bazo(image)
  • Malgranda Bildo (small_image)
  • Bildeto (thumbnail)
  • Swatch Bildo (swatch_image)

Ligi rolojn al amaskomunikila dosiero estas ĝuste tio, kio okazas catalog_product_entity_varchar. La deviga kodo estas simila al la kodo en la "Bazaj produktaj atributoj".

Post aldoni bildon al la produkto en la administra panelo, ĝi aspektas jene:

Magento 2: importu produktojn rekte en la datumbazon

Kategorioj

Ĉefaj tabeloj enhavantaj datumojn laŭ kategorio:

  • catalog_category_entity: registro de kategorioj;
  • catalog_category_product: rilato inter produktoj kaj kategorioj;
  • catalog_category_entity_*: EAV-atributaj valoroj;

Komence, en malplena Magento-aplikaĵo, la kategorio-registro enhavas 2 kategoriojn (mi mallongigis la kolonnomojn: 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|

La kategorio kun id=1 estas la radiko de la tuta Magento-katalogo kaj ne haveblas nek en la administra panelo nek sur la frontpaĝo. Kategorio kun id=2 (Defaŭlta Kategorio) estas la radika kategorio por la ĉefa butiko de la ĉefa retejo (Ĉefa Reteja Vendejo) kreita dum deplojado de la aplikaĵo (vidu. Administranto / Vendejoj / Ĉiuj Vendejoj). Krome, la radika kategorio de la vendejo mem ankaŭ ne haveblas ĉe la fronto, nur ĝiaj subkategorioj.

Ĉar la temo de ĉi tiu artikolo ankoraŭ importas datumojn pri produktoj, mi ne uzos rektan eniron en la datumbazon dum kreado de kategorioj, sed uzos la klasojn provizitajn de Magento mem (modeloj kaj deponejoj). Rekta eniro en la datumbazon estas uzata nur por asocii la importitan produkton kun kategorio (la kategorio kongruas per sia nomo, kaj la kategoriidentigilo estas prenita dum kongruo):

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

Post aldoni produktan ligon al la kategorioj "Kategorio 1" kaj "Kategorio 2", la produktaj detaloj en la administra panelo aspektas kiel ĉi tio:

Magento 2: importu produktojn rekte en la datumbazon

Pliaj agoj

Post kiam la importo de datumoj finiĝas, vi devas plenumi la sekvajn pliajn paŝojn:

  • indeksado de datumoj: voku en la konzolo ./bin/magento indexer:reindex;
  • regenerante URL-ojn por produktoj/kategorioj: vi povas uzi la etendon "elgentos/regenerate-catalog-urls«

Produktoj en la administra panelo post plenumi pliajn agojn:

Magento 2: importu produktojn rekte en la datumbazon

kaj ĉe la fronto:

Magento 2: importu produktojn rekte en la datumbazon

Resumo

La sama aro da produktoj (10 pecoj) kiel en la antaŭa artikolo estas importita almenaŭ grandordo pli rapide (1 sekundo kontraŭ 10). Por pli precize taksi la rapidecon, vi bezonas pli grandan nombron da produktoj - kelkcent, aŭ pli bone ankoraŭ milojn. Tamen, eĉ kun tiom malgranda grandeco de eniga datumoj, ni povas konkludi, ke la uzo de la iloj provizitaj de Magento (modeloj kaj deponejoj) estas grava (mi emfazas - multe!) akceli la disvolviĝon de la bezonata funkcieco, sed samtempe signife (mi emfazas - multe!) reduktu la rapidecon je kiu datumoj eniras la datumbazon.

Kiel rezulto, la akvo montriĝis malseka kaj ĉi tio ne estas revelacio. Tamen nun mi havas la kodon por ludi kaj eble elveni al iuj pli interesaj konkludoj.

fonto: www.habr.com