In Ho descritto il processo di importazione dei prodotti in Magento 2 in modo tradizionale, utilizzando modelli e repository. Il metodo tradizionale presenta una velocità di elaborazione dei dati piuttosto bassa. Sul mio laptop, si otteneva circa un prodotto al secondo. In questo seguito, prendo in considerazione un metodo alternativo di importazione del prodotto, ovvero la scrittura diretta nel database, bypassando i meccanismi standard di Magento 2 (modelli, fabbriche, repository). La sequenza di passaggi necessari per l'importazione dei prodotti può essere adattata a qualsiasi linguaggio di programmazione in grado di interagire con MySQL.
Avviso: In Magento è disponibile una funzionalità già pronta per e, probabilmente, sarà sufficiente per le vostre esigenze. Tuttavia, se avete bisogno di un controllo più completo sul processo di importazione, che vada oltre la preparazione di un file CSV per quello che è, siete i benvenuti sotto il tag.

Il codice risultante dalla scrittura di entrambi gli articoli può essere visualizzato nel modulo Magento "". Ecco alcune limitazioni che ho seguito per semplificare il codice del modulo demo:
- I prodotti vengono solo creati, non aggiornati.
- Un magazzino
- Vengono importati solo i nomi delle categorie, senza la loro struttura
- Le strutture dei dati corrispondono alla versione 2.3
JSON per l'importazione di un singolo prodotto:
{
"sku": "MVA20D-UBV-3",
"name": "Tappo per la sigillatura VA47-29 IEK",
"desc": "Fornire accesso ai dispositivi ...",
"desc_short": "Il tappo per la sigillatura VA47-29 IEK è progettato per ...",
"price": 5.00,
"qty": 25,
"categories": ["Categoria 1", "Categoria 2"],
"image_path": "mva20d_ubv_3.png"
}Panoramica delle fasi principali dell'importazione
- registrazione del prodotto stesso
- collegamento del prodotto al sito web
- attributi base del prodotto (EAV)
- dati di inventario (quantità del prodotto in magazzino)
- media (immagini)
- collegamento alle categorie del catalogo
Registrazione del prodotto
Le informazioni di base sul prodotto si trovano in catalog_product_entity:
CREATE TABLE `catalog_product_entity` (
`entity_id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID Entità',
`attribute_set_id` smallint(5) unsigned NOT NULL DEFAULT '0' COMMENT 'ID Set Attributi',
`type_id` varchar(32) NOT NULL DEFAULT 'simple' COMMENT 'ID Tipo',
`sku` varchar(64) DEFAULT NULL COMMENT 'SKU',
`has_options` smallint(6) NOT NULL DEFAULT '0' COMMENT 'Ha Opzioni',
`required_options` smallint(5) unsigned NOT NULL DEFAULT '0' COMMENT 'Opzioni Richieste',
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Data di Creazione',
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'Data di Aggiornamento',
PRIMARY KEY (`entity_id`),
KEY `CATALOG_PRODUCT_ENTITY_ATTRIBUTE_SET_ID` (`attribute_set_id`),
KEY `CATALOG_PRODUCT_ENTITY_SKU` (`sku`)
)Informazioni minime necessarie per creare una registrazione nel registro dei prodotti:
attribute_set_idsku
aggiuntivo:
type_id— se non specificato, verrà utilizzato 'simple'
Per la scrittura diretta nel database utilizzo l'adattatore DB di Magento stesso:
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;
}Dopo la registrazione del prodotto in catalog_product_entity diventa visibile nell'admin, nella griglia dei prodotti (Catalogo / Prodotti).

Collegamento del prodotto e sito web
Il collegamento del prodotto al sito determina in quali negozi e su quali vetrine il prodotto sarà disponibile sul 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);
}
Attributi base del prodotto
Un prodotto appena registrato non ha né nome né descrizione. Tutto ciò avviene tramite . Ecco l'elenco degli attributi base del prodotto necessari affinché il prodotto venga visualizzato correttamente sul fronte:
namepricedescriptionshort_descriptionstatotax_class_idurl_keyvisibility
Un attributo separato viene aggiunto al prodotto in questo modo (omesse le dettagli per ottenere l'identificativo e il tipo di attributo tramite il suo codice):
public function create($prodId, $attrCode, $attrValue)
{
$attrId = \/** ottieni l'ID dell'attributo tramite il codice dell'attributo *\/
$attrType = \/** ottieni il tipo di attributo [datetime|decimal|int|text|varchar] tramite il codice dell'attributo *\/
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,
\/** inserisci tutti gli attributi nella vista del negozio predefinita con id=0 (admin) *\/
'store_id' => 0,
'value' => $attrValue
];
$conn->insert($table, $bind);
}
}Dalla codifica dell'attributo determiniamo il suo id e tipo di dato (datetime, decimale, int, text, varchar), quindi nella tabella corrispondente scriviamo i dati per la vista amministrativa (store_id = 0).
Dopo aver aggiunto gli attributi sopra citati al prodotto, si ottiene questa visualizzazione nell'admin:

Dati dell'inventario
A partire dalla versione 2.3 in Magento esistono parallelamente due insiemi di tabelle che garantiscono la memorizzazione delle informazioni di inventario (quantità del prodotto):
cataloginventory_*: vecchia struttura;inventory_*: nuova struttura (MSI — Multi Source Inventory);
È necessario aggiungere i dati di inventario in entrambe le strutture, poiché la nuova struttura non è ancora completamente indipendente dalla vecchia (sembra molto che per default il magazzino nella nuova struttura venga utilizzata la tabella cataloginventory_stock_status come inventory_stock_1).
cataloginventory_
Alla distribuzione di Magneto 2.3 abbiamo inizialmente 2 record in store_website, che corrisponde a due siti - amministrativo e principale per i clienti:
website_id|code |name |sort_order|default_group_id|is_default|
----------|-----|------------|----------|----------------|----------|
0|admin|Admin | 0| 0| 0|
1|base |Sito principale| 0| 1| 1|Nella tabella cataloginventory_stock abbiamo solo un record:
stock_id|website_id|stock_name|
--------|----------|----------|
1| 0|Predefinito |Cioè, nella vecchia struttura abbiamo solo un "magazzino" (stock) ed è collegato al sito web amministrativo. L'aggiunta tramite il pannello di amministrazione di nuovi sources/stocks in MSI (la nuova struttura) non porta alla creazione di nuovi record in cataloginventory_stock.
I dati di inventario sui prodotti nella vecchia struttura sono inizialmente registrati nelle tabelle:
cataloginventory_stock_itemcataloginventory_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,
/* utilizziamo solo un magazzino nella struttura 'cataloginventory' per impostazione predefinita */
'stock_id' => 1,
'qty' => $qty,
'is_qty_decimal' => $isQtyDecimal,
'is_in_stock' => $isInStock,
/* il magazzino predefinito è collegato al sito web amministrativo (vedi `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,
/* utilizziamo solo un magazzino nella struttura 'cataloginventory' per impostazione predefinita */
'stock_id' => 1,
'qty' => $qty,
'stock_status' => MagentoCatalogInventoryApiDataStockStatusInterface::STATUS_IN_STOCK,
/* il magazzino predefinito è collegato al sito web amministrativo (vedi `cataloginventory_stock`) */
'website_id' => 0
];
$conn->insert($table, $bind);
}inventory_
Inizialmente, la nuova struttura per la memorizzazione dei dati di inventario contiene 1 "fonte» (inventory_source):
source_code|name |enabled|description |latitude|longitude|country_id|...|
-----------|--------------|-------|--------------|--------|---------|----------|...|
default |Fonte predefinita| 1|Fonte predefinita|0.000000| 0.000000|US |...|e un "magazzino» (inventory_stock):
stock_id|name |
--------|-------------|
1|Magazzino predefinito|«Fonte» è un deposito fisico per i prodotti (la registrazione contiene coordinate fisiche e indirizzo postale). «Magazzino» è una fusione logica di più «fonti» (inventory_source_stock_link)
link_id|stock_id|source_code|priority|
-------|--------|-----------|--------|
1| 1|default | 1|a livello del quale avviene il collegamento al canale di vendita (inventory_stock_sales_channel)
type |code|stock_id|
-------|----|--------|
website|base| 1|Dalla struttura dei dati si presuppongono diversi tipi di canali di vendita, ma per impostazione predefinita viene utilizzato solo il collegamento «stock«-«website» (il collegamento al sito web avviene tramite il codice del sito web — base).
Una «magazzino» può essere collegata a più «fonti«, e una «fonte» può essere collegata a più «magazzini» (relazione «molti-a-molti»). Le eccezioni sono le «fonte" e "magazzinodefault» che non vengono ricollegate ad altre entità (restrizione a livello di codice — si genera un errore «Cannot save link related to Default Source or Default Stock«). Maggiori dettagli sulla struttura MSI in Magento 2 possono essere letti nell'articolo ««.
Utilizzerò la configurazione predefinita e aggiungerò tutte le informazioni di inventario alla fonte default, che è coinvolta nel canale di vendita collegato al sito web con codice base (corrisponde alla parte cliente del negozio — vedi. store_website):
function createNewItem($sku, $qty)
{
\/** @var MagentoFrameworkAppResourceConnection $this->resource *\/\n \/** @var MagentoFrameworkDBAdapterPdoMysql $conn *\/\n $conn = $this->resource->getConnection();\n $table = $this->resource->getTableName('inventory_source_item');\n $bind = [\n 'source_code' => 'default',\n 'sku' => $sku,\n 'quantity' => $qty,\n 'status' => MagentoInventoryApiApiDataSourceItemInterface::STATUS_IN_STOCK\n ];\n $conn->insert($table, $bind);\n}Dopo aver aggiunto i dati di inventario al prodotto nell'amministrazione, si ottiene questa immagine:

Media
Quando si aggiungono manualmente immagini al prodotto tramite l'amministrazione, le informazioni corrispondenti vengono registrate nelle seguenti tabelle:
catalog_product_entity_media_gallery: registro dei media (immagini e video);catalog_product_entity_media_gallery_value: associazione dei media ai prodotti e alle vetrine (localizzazione);catalog_product_entity_media_gallery_value_to_entity: associazione dei media solo ai prodotti (presumibilmente, contenuto media predefinito per il prodotto);catalog_product_entity_varchar: qui vengono salvati i ruoli in cui viene utilizzata l'immagine;
e le immagini stesse vengono salvate nella cartella .\/pub\/media\/catalog\/product\/x\/y\/, dove x e y — la prima e la seconda lettera del nome del file dell'immagine. Ad esempio, il file image.png deve essere salvato come ./pub/media/catalog/product/i/m/image.png, affinché la piattaforma possa utilizzarlo come immagine nella descrizione dei prodotti del catalogo.
catalog_product_entity_media_gallery
Registriamo il media caricato in .\/pub\/media\/catalog\/product\/ file multimediale (il processo di caricamento del file in questo articolo non viene considerato):
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;
}Quando registriamo un nuovo file multimediale, viene assegnato un identificatore.
catalog_product_entity_media_gallery_value
Colleghiamo il file multimediale registrato al corrispondente prodotto per la vetrina predefinita:
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,
\/ * abbiamo solo un'immagine *\/
'position' => 1,
'disabled' => false
];
$conn->insert($table, $bind);
}catalog_product_entity_media_gallery_value_to_entity
Colleghiamo il file multimediale registrato al corrispondente prodotto senza legarlo a nessuna vetrina. Non è chiaro dove vengano utilizzati esattamente questi dati e perché non si possano utilizzare i dati della tabella precedente, ma questa tabella esiste e i dati vengono scritti in essa quando si aggiunge un'immagine al prodotto. Pertanto, è così.
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
Il file multimediale può essere utilizzato con diversi ruoli (il codice corrispondente all'attributo è indicato tra parentesi):
- Base (
image) - Immagine Piccola (
small_image) - Miniatura (
thumbnail) - Immagine Campione (
swatch_image)
L'assegnazione dei ruoli al file multimediale avviene proprio in catalog_product_entity_varchar. Il codice di assegnazione è analogo al codice nella sezione «Attributi base del prodotto«.
Dopo aver aggiunto un'immagine al prodotto nell’amministrazione, appare in questo modo:

Categorie
Le principali tabelle che contengono i dati sulle categorie:
catalog_category_entity: registro delle categorie;catalog_category_product: relazione tra prodotti e categorie;catalog_category_entity_*: valori degli attributi EAV;
Inizialmente, in un'app Magento vuota, il registro delle categorie contiene 2 categorie (ho abbreviato i nomi delle colonne: crt — created_at, aggiornato — 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 categoria con id=1 è la radice di tutto il catalogo Magento e non è accessibile né dall'amministratore né dal frontend. La categoria con id=2 (Categoria predefinita) è la categoria radice per il negozio principale del sito principale (Negozio del sito principale), creata durante il deployment dell'applicazione (vedi Admin / Stores / All Stores). Inoltre, la categoria radice del negozio non è accessibile nemmeno nel frontend, solo le sue sottocategorie.
Poiché l'argomento di questo articolo è l'importazione dei dati sui prodotti, non utilizzerò la scrittura diretta nel database per creare le categorie, ma utilizzerò le classi fornite dalla stessa Magento (modelli e repository). La scrittura diretta nel database è utilizzata solo per collegare il prodotto importato alla categoria (il mapping della categoria avviene in base al suo nome, recuperando l'id della categoria):
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);
}Dopo aver aggiunto il collegamento del prodotto con le categorie «Categoria 1» e «Categoria 2», i dettagli del prodotto nell'amministratore appaiono circa così:

Azioni aggiuntive
Al termine dell'importazione dei dati, è necessario eseguire le seguenti azioni aggiuntive:
- indicizzazione dei dati: chiamata da console
./bin/magento indexer:reindex; - rigenerazione degli URL per prodotti/categorie: è possibile utilizzare l'estensione ««
I prodotti nell'amministratore dopo aver eseguito le azioni aggiuntive:

e nel frontend:

Riepilogo
Lo stesso set di prodotti (10 pezzi), come nell'articolo precedente, viene importato, almeno, in modo significativamente più veloce (1 secondo contro 10). Per una valutazione più accurata della velocità sono necessari maggiori quantitativi di prodotti — alcune centinaia, se non migliaia. Tuttavia, anche con una dimensione così ridotta dei dati in ingresso si può concludere che l'uso degli strumenti forniti da Magento (modelli e repository) è notevolmente (sottolineo — significativamente!) accelerano lo sviluppo delle funzionalità richieste, ma riducono in modo significativo (sottolineo — significativamente!) la velocità di ingresso dei dati nel database.
Alla fine, l'acqua si è rivelata bagnata e non è una rivelazione. Tuttavia, ora ho il codice per continuare a sperimentare e, forse, trarre conclusioni più interessanti.
Fonte: habr.com
