I Jeg har beskrevet processen for at importere produkter til Magento 2 på den traditionelle måde – via modeller og repository. Den almindelige metode er kendetegnet ved en ret lav dataforarbejdningshastighed. På min bærbare computer fik jeg omtrent ét produkt pr. sekund. I denne fortsættelse undersøger jeg en alternativ metode til produktimport – direkte skrivning til databasen, uden om de standardmæssige mekanismer i Magento 2 (modeller, fabrikker, repository). Sekvensen af trin, der sikrer import af produkter, kan tilpasses ethvert programmeringssprog, der kan arbejde med MySQL.
Ansvarsfraskrivelse: Magento har indbygget funktionalitet til og det vil højst sandsynligt være tilstrækkeligt for dig. Men hvis du har brug for mere kontrol over importprocessen, som ikke kun begrænser sig til at forberede en CSV-fil til det, der er – så er du velkommen til at læse videre.

Koden, der er resultatet af at skrive begge artikler, kan ses i Magento-modulet ««. Her er nogle begrænsninger, som jeg har holdt mig til for at forenkle koden i demo-modulet:
- Produkter oprettes kun, de opdateres ikke.
- Én lagerplads
- Kun kategorinavne importeres, uden deres struktur
- Datastrukturen svarer til version 2.3
JSON til import af et enkelt produkt:
{
"sku": "MVA20D-UBV-3",
"name": "Tætslutningsprop til plombisering VA47-29 IEK",
"desc": "Tilvejebringelse af adgang til enheder ...",
"desc_short": "Tætslutningsproppen til plombisering VA47-29 IEK er designet til ...",
"price": 5.00,
"qty": 25,
"categories": ["Kategori 1", "Kategori 2"],
"image_path": "mva20d_ubv_3.png"
}Oversigt over de vigtigste trin i importen
- registrering af selve produktet
- forbindelse mellem produktet og webstedet
- grundlæggende produktattributter (EAV)
- lagerdata (antal produkter på lager)
- medier (billeder)
- forbindelse til katalogkategorier
Produktregistrering
Grundlæggende produktinformation findes i 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 'Attributsæt 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 'Har muligheder',
`required_options` smallint(5) unsigned NOT NULL DEFAULT '0' COMMENT 'Obligatoriske muligheder',
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Oprettelsestid',
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'Opdateringstid',
PRIMARY KEY (`entity_id`),
KEY `CATALOG_PRODUCT_ENTITY_ATTRIBUTE_SET_ID` (`attribute_set_id`),
KEY `CATALOG_PRODUCT_ENTITY_SKU` (`sku`)
)Minimum information required to create a record in the product registry:
attribute_set_idsku
additional:
type_id— if not specified, ‘simple’ will be used
For direct entry into the database, I use Magento's own DB adapter:
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;
}After registering the product in catalog_product_entity it becomes visible in the admin area, in the product grid (Catalog / Products).

Product and website linkage
The product's linkage to the website determines in which stores and on which displays the product will be available on the front end.
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);
}
Basic product attributes
A newly registered product does not have a name or description yet. All this is done through . Here’s a list of basic product attributes needed for the product to be displayed correctly on the front end:
namepricedescriptionshort_descriptionstatustax_class_idurl_keyvisibility
An individual attribute is added to the product like this (omitting details on obtaining the identifier and attribute type by its code):
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);
}
}By the attribute code, we determine its id and data type (datetime, decimal, int, text, varchar), then we write the data to the corresponding table for the admin view (store_id = 0).
After adding the above attributes to the product, the admin view looks as follows:

Inventory data
Fra version 2.3 i Magento findes der parallelt to sæt tabeller, der sikrer opbevaring af lagerinformation (produktmængde):
cataloginventory_*: den gamle struktur;inventory_*: den nye struktur (MSI — Multi Source Inventory);
Lagerdata skal tilføjes i begge strukturer, da den nye struktur stadig ikke er helt uafhængig af den gamle (det lader til, at for default lageret i den nye struktur anvendes tabellen cataloginventory_stock_status som inventory_stock_1).
cataloginventory_
Ved udrulningen af Magneto 2.3 har vi oprindeligt 2 poster i store_website, hvilket svarer til to websites — det administrative og det primære kundesite:
website_id|code |name |sort_order|default_group_id|is_default|
----------|-----|------------|----------|----------------|----------|
0|admin|Admin | 0| 0| 0|
1|base |Hovedwebsite| 0| 1| 1|I tabellen cataloginventory_stock har vi kun én post:
stock_id|website_id|stock_name|
--------|----------|----------|
1| 0|Standard |Det vil sige, at vi i den gamle struktur kun har ét "lager" (stock) og det er bundet til det administrative website. Tilføjelse af nye sources/stocks til MSI (den nye struktur) medfører ikke nye poster i. cataloginventory_stock.
Lagerdata om produkter i den gamle struktur skrives oprindeligt i tabellerne:
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,
/* vi bruger kun ét lager i 'cataloginventory' strukturen som standard */
'stock_id' => 1,
'qty' => $qty,
'is_qty_decimal' => $isQtyDecimal,
'is_in_stock' => $isInStock,
/* standardlageret er bundet til admin website (se `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,
/* vi bruger kun ét lager i 'cataloginventory' strukturen som standard */
'stock_id' => 1,
'qty' => $qty,
'stock_status' => MagentoCatalogInventoryApiDataStockStatusInterface::STATUS_IN_STOCK,
/* standardlageret er bundet til admin website (se `cataloginventory_stock`) */
'website_id' => 0
];
$conn->insert($table, $bind);
}inventory_
Oprindeligt indeholder den nye struktur til opbevaring af lagerdata 1 "kilde» (inventory_source):
source_code|name |enabled|description |latitude|longitude|country_id|...|
-----------|--------------|-------|--------------|--------|---------|----------|...|
default |Default Source| 1|Default Source|0.000000| 0.000000|US |...|og et én «lager» (inventory_stock):
stock_id|name |
--------|-------------|
1|Default Stock|«Kilde» repræsenterer et fysisk lager for produkter (optegnelsen indeholder fysiske koordinater og en postadresse). «Lager» repræsenterer en logisk sammenkobling af flere «kilder» (inventory_source_stock_link)
link_id|stock_id|source_code|priority|
-------|--------|-----------|--------|
1| 1|default | 1|på hvilket niveau der er binding til salgskanalen (inventory_stock_sales_channel)
type |code|stock_id|
-------|----|--------|
website|base| 1|Ifølge datakonstruktionen antages forskellige typer salgskanaler, men som standard bruges kun forbindelsen «stock«-«website» (linken til webstedet går via webstedets kode — base).
En «lager» kan være knyttet til flere «kilder«, og en «kilde» kan være knyttet til flere «lagre» (forholdet «mange-til-mange»). Undtagelser udgør de standard «kilde" og "lager«. De kan ikke blive ombindet til andre enheder (begrænsning på kode niveau — fejlen «Can not save link related to Default Source or Default Stock«). Mere detaljeret om strukturen MSI i Magento 2 kan læses i artiklen ««.
Jeg vil bruge standardkonfigurationen og tilføje alle lageroplysninger til kilden default, som er involveret i salgskanalen tilknyttet webstedet med koden base (som svarer til frontend af butikken — se. 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);
}Efter at have tilføjet lagerdata til produktet i adminpanelet ser det sådan ud:

Media
Ved «manuelt» at tilføje billeder til produktet via adminpanelet registreres de relevante oplysninger i følgende tabeller:
catalog_product_entity_media_gallery: medieregister (billeder og videofiler);catalog_product_entity_media_gallery_value: binding af media til produkter og udstillingsvinduer (lokalisering);catalog_product_entity_media_gallery_value_to_entity: binding af media kun til produkter (formodentlig standard medieindhold for produktet);katalog_product_entity_varchar: her gemmes rollerne, hvor billedet anvendes;
og selve billederne gemmes i kataloget .\/pub\/media\/catalog\/product\/x\/y\/, hvor x og y — de første to bogstaver i filnavnet for billedet. For eksempel, filen image.png skal gemmes som ./pub/media/catalog/product/i/m/image.png, så platformen kan bruge det som billede i produktbeskrivelser fra kataloget.
catalog_product_entity_media_gallery
Vi registrerer den placerede i .\/pub\/media\/catalog\/product\/ mediefil (selve processen med at placere filen i denne artikel er ikke behandlet):
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;
}Når en ny mediefil registreres, tildeles den en identifikator.
catalog_product_entity_media_gallery_value
Vi forbinder den registrerede mediefil med det tilsvarende produkt til default-visningen:
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);
}catalog_product_entity_media_gallery_value_to_entity
Vi forbinder den registrerede mediefil med det tilsvarende produkt uden binding til nogen specifik vitrine. Det er uklart, hvor præcist disse data anvendes, og hvorfor man ikke kan referere til dataene i den forrige tabel, men denne tabel eksisterer, og dataene gemmes i den, når der tilføjes et billede til produktet. Sådan er det.
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);
}katalog_product_entity_varchar
Mediefil kan anvendes med forskellige roller (den tilsvarende attributkode angivet i parentes):
- Base (
billede) - Small Image (
small_image) - Thumbnail (
thumbnail) - Swatch Image (
swatch_image)
Binding af roller til mediefil sker netop i katalog_product_entity_varchar. Koden til binding svarer til koden i afsnittet «Basic product attributes«.
Når der tilføjes et billede til produktet i administrationspanelet, ser det sådan ud:

Kategorier
De primære tabeller, der indeholder data om kategorier:
catalog_category_entity: register over kategorier;catalog_category_product: forbindelse mellem produkter og kategorier;catalog_category_entity_*: værdier for EAV-attributter;
Oprindeligt, i en tom Magento-applikation, indeholder registeret over kategorier 2 kategorier (jeg har forkortet kolonneoverskrifterne: 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|Kategorien med id=1 er roden af hele Magento-kataloget og er ikke tilgængelig hverken i administrationspanelet eller på frontend. Kategorien med id=2 (Default Category) er rodkategorien for hovedbutikken på hovedsiden (Main Website Store), der oprettes ved deployment af applikationen (se. Admin \/ Stores \/ All Stores). Desuden er selve rodkategorien for butikken på frontend heller ikke tilgængelig, kun dens underkategorier.
Eftersom emnet for denne artikel alligevel er import af data vedrørende produkter, vil jeg ikke bruge direkte skrivning til databasen, når jeg opretter kategorier, men benytte de klasser, der tilbydes af Magento selv (modeller og repositories). Direkte skrivning til databasen anvendes kun til at forbinde det importerede produkt med kategorien (kategorierne matches ud fra deres navn, og når der matches, hentes id for kategorien):
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);
}Når forbindelsen mellem produktet og kategorierne "Kategori 1" og "Kategori 2" er tilføjet, ser produktdetaljerne i administrationspanelet cirka sådan ud:

Yderligere handlinger
Efter afslutningen af dataimporten skal følgende yderligere handlinger udføres:
- indeksering af data: kald i konsollen
.\/bin\/magento indexer:reindex; - regenerering af URL’er for produkter\/kategorier: man kan bruge udvidelsen "«
Produkter i administrationspanelet efter udførelse af de yderligere handlinger:

og på frontend:

CV
Det samme sæt af produkter (10 stykker), som i den tidligere artikel, importeres mindst en orden hurtigere (1 sekund mod 10). For en mere præcis vurdering af hastigheden er der brug for flere produkter — flere hundrede, eller helst tusinder. Ikke desto mindre kan man selv med så lille en datamængde konkludere, at brugen af værktøjer leveret af Magento (modeller og repositories) væsentligt (jeg understreger — væsentligt!) fremskynder udviklingen af den nødvendige funktionalitet, men samtidig væsentligt (jeg understreger — væsentligt!) sænker hastigheden hvormed data kommer ind i databasen.
I sidste ende viste det sig, at vandet er vådt, og det er ikke nogen åbenbaring. Ikke desto mindre har jeg nu koden til at lege videre med, og måske gøre mere interessante konklusioner.
Kilde: habr.com
