Magento 2: impurtà i prudutti direttamente in a basa di dati

В articulu precedente Aghju descrittu u prucessu di impurtà prudutti in Magento 2 in u modu di solitu - attraversu mudelli è repositori. U metudu di solitu hà una vitezza di trasfurmazioni di dati assai bassu. U mo laptop pruducia circa un pruduttu per seconda. In questa continuazione, aghju cunsideratu un modu alternativu per impurtà un pruduttu - per l'ingressu direttu in a basa di dati, sguassendu i miccanismi standard Magento 2 (mudelli, fabbriche, repository). A sequenza di passi per impurtà i prudutti pò esse adattatu à qualsiasi lingua di prugrammazione chì pò travaglià cù MySQL.

Légales: Magento hà funziunalità ready-made per importazione di dati è, assai prubabile, serà abbastanza per voi. Tuttavia, s'è vo avete bisognu di cuntrollu più cumpleta nantu à u prucessu di impurtazioni, micca limitatu à a preparazione di un schedariu CSV per ciò chì vo avete, benvenuti à cat.

Magento 2: impurtà i prudutti direttamente in a basa di dati

U codice risultatu da a scrittura di i dui articuli pò esse vistu in u modulu Magento "flancer32/mage2_ext_demo_import". Eccu alcune restrizioni chì aghju seguitu per simplificà u codice di u modulu demo:

  • I prudutti sò solu creati, micca aghjurnati.
  • Un magazzinu
  • Solu i nomi di categurie sò impurtati, senza a so struttura
  • Strutture di dati cumplessi cù a versione 2.3

JSON per impurtà un solu pruduttu:

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

Panoramica di i principali tappe di impurtazione

  • registrazione di u pruduttu stessu
  • cunnessione trà u produttu è u situ web
  • attributi basi di u produttu (EAV)
  • dati d'inventariu (quantità di pruduttu in stock)
  • media (imaghjini)
  • cunnessione cù categurie di catalogu

Registrazione di u produttu

L'infurmazione basica di u produttu pò esse truvata in 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`)
)

L'infurmazione minima necessaria per creà una entrata in u registru di u produttu hè:

  • attribute_set_id
  • sku

supplementu:

  • type_id - se ùn avemu micca specifichi, allora "semplice" serà utilizatu

Per scrive direttamente à a basa di dati, aghju utilizatu l'adattatore DB di Magento stessu:

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

Dopu avè registratu u pruduttu cù catalog_product_entity diventa visibile in u pannellu di amministrazione, in a griglia di u produttu (Catalogu / Prudutti).

Magento 2: impurtà i prudutti direttamente in a basa di dati

Relazione trà u produttu è u situ web

L'associu di u pruduttu cù u situ determina in quale magazzini è mostra u pruduttu serà dispunibule à u fronte.

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: impurtà i prudutti direttamente in a basa di dati

Attributi basi di u produttu

U pruduttu novu registratu ùn hà ancu un nome o descrizzione. Tuttu chistu hè fattu attraversu attributi EAV. Eccu un elencu di l'attributi basi di u produttu chì sò necessarii per chì u pruduttu sia affissatu currettamente in fronte:

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

Un attributu separatu hè aghjuntu à un pruduttu cum'è questu (i dettagli di ottene l'identificatore è u tipu di l'attributu da u so codice sò omessi):

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

Utilizendu u codice attributu, determinamu u so id è u tipu di dati (datetime, decimal, int, text, varchar), dopu scrivite i dati per a finestra amministrativa in a tavola adatta (store_id = 0).

Dopu avè aghjustatu l'attributi sopra à u pruduttu, uttene sta stampa in u pannellu di amministrazione:

Magento 2: impurtà i prudutti direttamente in a basa di dati

Dati di l'inventariu

Partendu da a versione 2.3 in Magento, ci sò dui gruppi paralleli di tavule chì furnisce u almacenamentu di l'infurmazioni d'inventariu (quantità di produttu):

  • cataloginventory_*: struttura antica;
  • inventory_*: nova struttura (MSI - Multi Source Inventory);

Avete bisognu di aghjunghje dati d'inventariu à e duie strutture, perchè a nova struttura ùn hè ancu cumplettamente indipendente di l'antica (hè assai prubabile chì per default magazzinu in a nova struttura una tavola hè implicata cataloginventory_stock_status cum'è inventory_stock_1).

catalogu inventariu_

Quandu implementate Magneto 2.3 avemu inizialmente 2 entrate in store_website, chì currisponde à dui siti - amministrativu è cliente principale:

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

Table cataloginventory_stock avemu solu una entrata:

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

Vale à dì, in a nostra vechja struttura ci hè solu un "magazzinu" (stock) è hè ligatu à u situ amministrativu. Aghjunghjendu novi attraversu u pannellu admin sources/stocks in MSI (nova struttura) ùn hà micca risultatu in novi entrate in cataloginventory_stock.

I dati di l'inventariu nantu à i prudutti in l'antica struttura sò inizialmente registrati in tavule:

  • cataloginventory_stock_item
  • cataloginventory_stock_status

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

inventariu_

Inizialmente, a nova struttura per almacenà dati d'inventariu cuntene 1 "fonti«(inventory_source):

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

è unu "magazzinu«(inventory_stock):

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

«Source» rapprisenta l'almacenamiento fisicu per i prudutti (u registru cuntene coordenate fisiche è indirizzu postale). "Magazzinu"hè una unione logica di parechje "fonti" (inventory_source_stock_link)

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

à u livellu à quale a cunnessione à u canali di vendita si trova (inventory_stock_sales_channel)

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

A ghjudicà da a struttura di dati, diversi tipi di canali di vendita sò assunti, ma per difettu solu a cunnessione "scorta"-"situ"(u ligame à u situ web seguita u codice di u situ web - base).

Unu "magazzinu"pò esse ligatu à parechji"fonti"è unu"fonti"- à parechji "magazzini"(relazioni assai à parechji). L'eccezzioni sò predeterminati "fonti"E"magazzinu". Ùn sò micca riligati à altre entità (limitazione à u nivellu di codice - l'errore "Ùn pò micca salvà u ligame ligatu à a Fonte Predeterminata o a Stock Predefinitu"). Più dettagli nantu à a struttura MSI in Magento 2 ponu esse truvati in l'articulu "Sistema di gestione di magazzini cù CQRS è Event Sourcing. Disegnu".

Aduprà a cunfigurazione predeterminata è aghjunghje tutte l'infurmazioni d'inventariu à a fonte default, chì hè implicatu in u canali di vendita assuciatu cù u situ web cù u codice base (currisponde à u front end di a tenda - vede 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);
}

Dopu avè aghjustatu dati d'inventariu à u pruduttu in u pannellu di amministrazione, uttene sta stampa:

Magento 2: impurtà i prudutti direttamente in a basa di dati

Media

Quandu "manualmente" aghjunghje una maghjina à un pruduttu attraversu u pannellu di amministrazione, l'infurmazioni pertinenti sò scritte in e seguenti tavule:

  • catalog_product_entity_media_gallery: registru media (imaghjini è fugliali video);
  • catalog_product_entity_media_gallery_value: ligà i media à i prudutti è i vetri (localizazione);
  • catalog_product_entity_media_gallery_value_to_entity: ligà i media à i prudutti solu (presumibilmente u cuntenutu media predeterminatu per u pruduttu);
  • catalog_product_entity_varchar: I roli in quale l'imaghjini hè utilizatu sò guardati quì;

è l'imaghjini stessi sò salvati in u cartulare ./pub/media/catalog/product/x/y/induve x и y - a prima è a seconda lettera di u nome di l'imaghjini. Per esempiu, u schedariu image.png deve esse salvatu cum'è ./pub/media/catalog/product/i/m/image.png, cusì chì a piattaforma pò usà cum'è una maghjina quandu descrive i prudutti di u catalogu.

Registrate publicatu in ./pub/media/catalog/product/ media file (u prucessu di mette u schedariu stessu ùn hè micca discututu in questu articulu):

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

Quandu hè registratu, un novu schedariu media hè assignatu un identificatore.

Associemu u schedariu media registratu cù u pruduttu currispundente per a vetrina predeterminata:

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

Associemu u schedariu media registratu cù u pruduttu currispundente senza esse ligatu à alcuna vetrina. Ùn hè micca chjaru induve esattamente sta dati hè utilizatu è perchè hè impussibile di accede à i dati da a tavula precedente, ma sta tabella esiste è e dati sò scritti à questu quandu una stampa hè aghjuntu à u pruduttu. Allora hè questu.

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

Un schedariu media pò esse usatu cù diversi roles (u codice d'attributu currispundente hè indicatu in parentesi):

  • basa (image)
  • Piccola immagine (small_image)
  • miniatura (thumbnail)
  • Swatch Image (swatch_image)

Linking roles à un schedariu media hè esattamente ciò chì succede in catalog_product_entity_varchar. U codice di ubligatoriu hè simile à u codice in u "Attributi basi di u produttu".

Dopu avè aghjustatu una maghjina à u pruduttu in u pannellu admin, pare cusì:

Magento 2: impurtà i prudutti direttamente in a basa di dati

Categurie

Tavule principali chì cuntenenu dati per categuria:

  • catalog_category_entity: registru di categurie;
  • catalog_category_product: cunnessione trà prudutti è categurie;
  • catalog_category_entity_*: valori d'attributi EAV;

Inizialmente, in una applicazione Magento viota, u registru di categurie cuntene 2 categurie (aghju accurtatu i nomi di colonna: 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|

A categuria cù id = 1 hè a radica di tuttu u catalogu Magento è ùn hè micca dispunibule nè in u panel admin nè in a prima pagina. Categoria cù id=2 (Categoria predefinita) hè a categuria radicali per a tenda principale di u situ principale (Negoziu di u situ web principale) creata durante l'implementazione di l'applicazione (vede. Admin / Stores / All Stores). Inoltre, a categuria radicali di a tenda stessa ùn hè micca dispunibule ancu in fronte, solu i so subcategories.

Siccomu u tema di questu articulu hè sempre impurtatu dati nantu à i prudutti, ùn aghju micca aduprà l'entrata diretta in a basa di dati quandu creanu categurie, ma aduprà e classi furnite da Magento stessu (mudelli è repositori). L'entrata diretta in a basa di dati hè aduprata solu per associà u pruduttu impurtatu cù una categuria (a categuria hè assuciata cù u so nome, è l'identificatore di categuria hè recuperatu durante l'abbinamentu):

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

Dopu avè aghjustatu un ligame di u produttu à e categurie "Categoria 1" è "Categoria 2", i dettagli di u produttu in u pannellu di amministrazione sò cusì cusì:

Magento 2: impurtà i prudutti direttamente in a basa di dati

Azzioni addiziunali

Una volta l'importazione di dati hè cumpleta, avete bisognu di compie i seguenti passi supplementari:

  • indexazione di dati: chjamate in a cunsola ./bin/magento indexer:reindex;
  • rigenerazione di URL per i prudutti / categurie: pudete aduprà l'estensione "elgentos/regenerate-catalog-urls«

I prudutti in u pannellu di amministrazione dopu avè realizatu azzioni supplementari:

Magento 2: impurtà i prudutti direttamente in a basa di dati

è in fronte:

Magento 2: impurtà i prudutti direttamente in a basa di dati

Resumen

U listessu settore di prudutti (10 pezzi) cum'è in l'articulu precedente hè impurtatu almenu un ordine di grandezza più veloce (1 second versus 10). Per stima più precisamente a vitezza, avete bisognu di un numeru più grande di prudutti - parechji centu, o megliu ancu millaie. In ogni casu, ancu cù una dimensione cusì chjuca di dati di input, pudemu cuncludi chì l'usu di l'arnesi furniti da Magento (mudelli è repositori) hè significativu (senza - assai!) accelerà u sviluppu di e funziunalità dumandata, ma à u stessu tempu significativamente (sottu - assai!) riduce a velocità à quale i dati entranu in a basa di dati.

In u risultatu, l'acqua hè stata umida è questu ùn hè micca una revelazione. Tuttavia, avà aghju u codice per ghjucà è forse ghjunghje à qualchi cunclusioni più interessanti.

Source: www.habr.com