In Ich habe den Importprozess von Produkten in Magento 2 auf die herkömmliche Weise beschrieben – über Modelle und Repositories. Der herkömmliche Ansatz bietet eine relativ geringe Datenverarbeitungsgeschwindigkeit. Auf meinem Laptop ergab es etwa ein Produkt pro Sekunde. In diesem Teil betrachte ich einen alternativen Ansatz für den Produktimport – die direkte Eingabe in die Datenbank, um die Standardmechanismen von Magento 2 (Modelle, Fabriken, Repositories) zu umgehen. Die Abfolge der Schritte, die den Import von Produkten gewährleisten, kann an jede Programmiersprache angepasst werden, die mit MySQL arbeiten kann.
Haftungsausschluss: Magento bietet eine integrierte Funktionalität für und wahrscheinlich werden Sie damit ausreichend bedient. Wenn Sie jedoch eine umfassendere Kontrolle über den Importprozess wünschen, die über die Vorbereitung der CSV-Datei hinausgeht, sind Sie herzlich eingeladen, weiterzulesen.

Der Code, der aus dem Schreiben beider Artikel resultiert, ist im Magento-Modul „“ einzusehen. Hier sind einige Einschränkungen, an die ich mich gehalten habe, um den Code des Demomoduls zu vereinfachen:
- Produkte werden nur erstellt, nicht aktualisiert.
- Ein Lager
- Es werden nur die Kategorienamen ohne deren Struktur importiert
- Die Datenstrukturen entsprechen der Version 2.3
JSON für den Import eines einzelnen Produkts:
{
"sku": "MVA20D-UBV-3",
"name": "Dichtung für die Versiegelung VA47-29 IEK",
"desc": "Zugang zu Geräten gewährleisten ...",
"desc_short": "Die Dichtung für die Versiegelung VA47-29 IEK dient dazu ...",
"price": 5.00,
"qty": 25,
"categories": ["Kategorie 1", "Kategorie 2"],
"image_path": "mva20d_ubv_3.png"
}Überblick über die wichtigsten Schritte des Imports
- Registrierung des Produkts selbst
- Verknüpfung des Produkts mit der Webseite
- Basisattribute des Produkts (EAV)
- Bestandsdaten (Produktmenge auf Lager)
- Medien (Bilder)
- Verknüpfung mit Produktkategorien
Produktregistrierung
Die Basisinformationen über das Produkt befinden sich in catalog_product_entity:
ERSTELLEN SIE DIE TABELLE `catalog_product_entity` (
`entity_id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Entitäts-ID',
`attribute_set_id` smallint(5) unsigned NOT NULL DEFAULT '0' COMMENT 'Attributsatz-ID',
`type_id` varchar(32) NOT NULL DEFAULT 'simple' COMMENT 'Typ-ID',
`sku` varchar(64) DEFAULT NULL COMMENT 'SKU',
`has_options` smallint(6) NOT NULL DEFAULT '0' COMMENT 'Hat Optionen',
`required_options` smallint(5) unsigned NOT NULL DEFAULT '0' COMMENT 'Erforderliche Optionen',
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Erstellungszeit',
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'Aktualisierungszeit',
PRIMARY KEY (`entity_id`),
KEY `CATALOG_PRODUCT_ENTITY_ATTRIBUTE_SET_ID` (`attribute_set_id`),
KEY `CATALOG_PRODUCT_ENTITY_SKU` (`sku`)
)Die minimal erforderlichen Informationen zur Erstellung eines Produkteintrags im Katalog:
attribute_set_idsku
zusätzlich:
type_id— falls nicht angegeben, wird ‘simple’ verwendet
Für den direkten Zugriff auf die Datenbank verwende ich den DB-Adapter von Magento:
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;
}Nach der Registrierung des Produkts in catalog_product_entity wird es im Admin-Bereich sichtbar, im Produkt-Grid (Katalog / Produkte).

Verknüpfung des Produkts mit der Website
Die Verbindung des Produkts mit der Website bestimmt, in welchen Geschäften und auf welchen Regalen das Produkt auf der Frontseite verfügbar sein wird.
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);
}
Grundlegende Produktattribute
Ein frisch registriertes Produkt hat noch keinen Namen und keine Beschreibung. All dies erfolgt über . Hier ist die Liste der grundlegenden Produktattribute, die benötigt werden, damit das Produkt ausreichend korrekt auf der Frontseite angezeigt wird:
namePreisBeschreibungkurze Beschreibungstatussteuerklass_idurl_keySichtbarkeit
Ein einzelnes Attribut wird so zum Produkt hinzugefügt (Details zum Abrufen der ID und Art des Attributs anhand seines Codes wurden weggelassen):
public function create($prodId, $attrCode, $attrValue)
{
$attrId = /* Attribut-ID anhand des Attributcodes erhalten */
$attrType = /* Attributtyp [datetime|decimal|int|text|varchar] anhand des Attributcodes erhalten */
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,
/* Alle Attribute zur Standard-Store-Ansicht mit id=0 (Admin) hinzufügen */
'store_id' => 0,
'value' => $attrValue
];
$conn->insert($table, $bind);
}
}Wir bestimmen die ID und den Datentyp anhand des Attributcodes (datetime, decimal, int, text, varchar), und schreiben dann die Daten in die entsprechende Tabelle für die Verwaltungsansicht (store_id = 0).
Nachdem die oben genannten Attribute zum Produkt hinzugefügt wurden, sieht die Ansicht im Admin-Bereich folgendermaßen aus:

Bestandsdaten
Seit Version 2.3 existieren in Magento parallel zwei Satz von Tabellen, die die Speicherung von Inventarinformationen (Produktmenge) ermöglichen:
cataloginventory_*: alte Struktur;inventory_*: neue Struktur (MSI — Multi Source Inventory);
Inventardaten müssen in beide Strukturen eingefügt werden, da die neue Struktur bisher noch nicht vollständig unabhängig von der alten ist (es sieht ganz danach aus, dass für default Im neuen Strukturmodell wird eine Tabelle für Lager verwendet. cataloginventory_stock_status als inventory_stock_1).
cataloginventory_
Beim Deployment von Magento 2.3 haben wir zunächst 2 Einträge in store_website, was zwei Websites entspricht — einer Verwaltungs- und einer Hauptkundenwebsite:
website_id|code |name |sort_order|default_group_id|is_default|
----------|-----|------------|----------|----------------|----------|
0|admin|Admin | 0| 0| 0|
1|base |Hauptwebsite| 0| 1| 1|In der Tabelle cataloginventory_stock gibt es nur einen Eintrag:
stock_id|website_id|stock_name|
--------|----------|----------|
1| 0|Standard |Das heißt, in der alten Struktur gibt es nur ein ‚Lager‘ (stock) und es ist mit der Verwaltungswebsite verbunden. Das Hinzufügen neuer transforms/stocks in MSI (neue Struktur) führt nicht zur Erstellung neuer Einträge in cataloginventory_stock.
Die Produktdaten in der alten Struktur werden zunächst in den Tabellen festgelegt:
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,
/* wir verwenden standardmäßig nur einen Lagerbestand in der 'cataloginventory'-Struktur */
'stock_id' => 1,
'qty' => $qty,
'is_qty_decimal' => $isQtyDecimal,
'is_in_stock' => $isInStock,
/* Der Standardbestand ist an die Admin-Website gebunden (siehe `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,
/* wir verwenden standardmäßig nur einen Lagerbestand in der 'cataloginventory'-Struktur */
'stock_id' => 1,
'qty' => $qty,
'stock_status' => MagentoCatalogInventoryApiDataStockStatusInterface::STATUS_IN_STOCK,
/* Der Standardbestand ist an die Admin-Website gebunden (siehe `cataloginventory_stock`) */
'website_id' => 0
];
$conn->insert($table, $bind);
}lager_
Ursprünglich enthält die neue Struktur zur Speicherung von Inventardaten 1 „Quelle» (lagerquelle):
source_code|name |enabled|description |latitude|longitude|country_id|...|
-----------|--------------|-------|--------------|--------|---------|----------|...|
default |Standardquelle | 1|Standardquelle |0.000000| 0.000000|DE |...|und einer «Lager» (inventar_bestand):
stock_id|name |
--------|-------------|
1|Standard Lager|«Quelle» stellt ein physisches Lager für Produkte dar (der Datensatz enthält geografische Koordinaten und die postalische Adresse). «Lager» stellt eine logische Verbindung mehrerer „Quellen“ dar (inventar_quelle_lager_verknüpfung)
link_id|stock_id|source_code|priority|
-------|--------|-----------|--------|
1| 1|default | 1|auf der Ebene, auf der die Zuordnung zum Verkaufs Kanal erfolgt (inventar_lager_verkaufs_kana)
type |code|stock_id|
-------|----|--------|
website|basis| 1|Aufgrund der Datenstruktur werden verschiedene Arten von Verkaufskanälen angenommen, aber standardmäßig wird nur die Verbindung „stock«-«website» (der Link zur Webseite erfolgt über den Code der Webseite — basis).
Eine „Lager» kann mehreren „Quellen« zugeordnet werden, während eine „Quelle» mit mehreren „Lager» verbunden werden kann (Beziehung „viele-zu-vielen“). Ausnahmen bilden die standardmäßigen „Quelle» und «Lager«. Diese werden nicht mit anderen Entitäten neu verknüpft (Beschränkung auf der Code-Ebene — es tritt ein Fehler auf „Kann keinen Link speichern, der mit Standardquelle oder Standardlager verbunden istMehr über die MSI-Struktur in Magento 2 erfahren Sie in dem Artikel „«.
Ich werde die Standardkonfiguration verwenden und alle Bestandsinformationen in die Quelle hinzufügen, die defaultim Verkaufskanal involviert ist, der mit der Website mit dem Code basis übereinstimmt (entspricht der Clientseite des Shops – siehe 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);
}Nach dem Hinzufügen der Bestandsdaten zum Produkt im Admin-Bereich sieht das Ergebnis wie folgt aus:

Medien
Beim „manuellen“ Hinzufügen von Bildern zum Produkt über das Admin-Panel werden die entsprechenden Informationen in den folgenden Tabellen erfasst:
catalog_product_entity_media_gallery: Medienregister (Bilder und Videos);catalog_product_entity_media_gallery_value: Verknüpfung von Medien zu Produkten und Schaufenstern (Lokalisierung);catalog_product_entity_media_gallery_value_to_entity: Medien nur an Produkte binden (voraussichtlich, Standard-Medieninhalte für das Produkt);catalog_product_entity_varchar: Hier werden die Rollen gespeichert, in denen das Bild verwendet wird;
und die Bilder selbst werden im Katalog gespeichert ./pub/media/catalog/product/x/y/, wobei x und y — die ersten beiden Buchstaben des Dateinamens des Bildes. Zum Beispiel sollte die Datei image.png als ./pub/media/catalog/product/i/m/image.pnggespeichert werden, damit die Plattform sie als Bild zur Beschreibung von Produkten aus dem Katalog verwenden kann.
catalog_product_entity_media_gallery
Wir registrieren die platzierte Datei in ./pub/media/catalog/product/ Mediadatei (der Prozess der Dateiablage wird in diesem Artikel nicht behandelt):
function createMediaGallery($imgPathPrefixed)
{
$attrId = /* Attribut-ID durch Attributcode 'media_gallery' abrufen */
/** @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' oder 'video' */
'media_type' => 'image',
'disabled' => false
];
$conn->insert($table, $bind);
$result = $conn->lastInsertId($table);
return $result;
}Beim Registrieren wird dem neuen Mediendatei eine Identifikationsnummer zugewiesen.
catalog_product_entity_media_gallery_value
Wir verknüpfen die registrierte Mediendatei mit dem entsprechenden Produkt für das Standard-Frontend:
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,
/* Standardmäßig wird die Administrationsansicht verwendet */
'store_id' => 0,
'entity_id' => $prodId,
'label' => null,
/* Wir haben nur ein Bild */
'position' => 1,
'disabled' => false
];
$conn->insert($table, $bind);
}catalog_product_entity_media_gallery_value_to_entity
Wir verknüpfen die registrierte Mediendatei mit dem entsprechenden Produkt, ohne an ein bestimmtes Store-Frontend gebunden zu sein. Es ist unklar, wo genau diese Daten verwendet werden und warum auf die Daten der vorherigen Tabelle nicht zugegriffen werden kann, aber diese Tabelle existiert und die Daten werden beim Hinzufügen eines Bildes zum Produkt hier gespeichert. Daher ist das so.
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
Mediendateien können mit verschiedenen Rollen verwendet werden (der entsprechende Attributcode ist in Klammern angegeben):
- Basis (
image) - Kleinbild (
kleines_Bild) - Miniaturansicht (
Miniaturansicht) - Stichprobe Bild (
Farbvarianten_Bild)
Die Bindung von Rollen an Mediendateien erfolgt genau hier in catalog_product_entity_varchar. Der Bindungscode ähnelt dem Code im Abschnitt „Grundlegende Produktattribute«.
Nach dem Hinzufügen eines Bildes zum Produkt in der Admin-Oberfläche sieht es so aus:

Kategorien
Die Haupttabellen, in denen die Daten zu den Kategorien gespeichert sind:
catalog_category_entity: Katalog der Kategorien;catalog_category_product: Verbindung zwischen Produkten und Kategorien;catalog_category_entity_*: Werte der EAV-Attribute;
Ursprünglich enthält das leere Magento-Anwendung im Katalogregister 2 Kategorien (ich habe die Spaltennamen gekürzt: 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|Die Kategorie mit der ID=1 ist die Wurzel des gesamten Magento-Katalogs und ist weder im Backend noch im Frontend verfügbar. Die Kategorie mit der ID=2 (Standardkategorie) ist die Wurzelkategorie für den Hauptstore der Hauptwebsite (Hauptwebsite Store), die bei der Bereitstellung der Anwendung erstellt wird (siehe Admin / Stores / Alle Stores). Zudem ist die Wurzelkategorie des Stores im Frontend ebenfalls nicht verfügbar, nur deren Unterkategorien.
Da das Thema dieses Artikels der Import von Produktdaten ist, werde ich bei der Erstellung von Kategorien keine direkte Datenbankeinfügung verwenden, sondern die von Magento bereitgestellten Klassen (Modelle und Repositories). Die direkte Datenbankeinfügung wird nur verwendet, um das importierte Produkt mit der Kategorie zu verknüpfen (die Zuordnung der Kategorie erfolgt anhand ihres Namens, wobei die ID der Kategorie extrahiert wird):
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);
}Nach der Verknüpfung des Produkts mit den Kategorien „Kategorie 1“ und „Kategorie 2“ sieht die Produktdetailseite im Admin-Bereich etwa so aus:

Zusätzliche Schritte
Nach Abschluss des Datenimports sind folgende zusätzliche Schritte erforderlich:
- Datenindizierung: Aufruf in der Konsole
./bin/magento indexer:reindex; - Generierung von URLs für Produkte/Kategorien: Sie können die Erweiterung „«
Produkte im Admin-Bereich nach Durchführung der zusätzlichen Maßnahmen:

und an der Front:

Zusammenfassung
Das gleiche Produktsortiment (10 Stück), das auch im letzten Artikel behandelt wurde, wird mindestens um ein Vielfaches schneller importiert (1 Sekunde statt 10). Um die Geschwindigkeit genauer zu bewerten, wären mehrere Hundert, besser noch Tausende von Produkten notwendig. Dennoch lässt sich selbst bei solch einer kleinen Datenmenge feststellen, dass die Nutzung der von Magento bereitgestellten Werkzeuge (Modelle und Repositories) die erforderliche Funktionalität erheblich (ich betone — erheblich!) beschleunigt, jedoch gleichzeitig die Geschwindigkeiten des Dateneingangs in die Datenbank signifikant (ich betone — erheblich!) reduziert.
Am Ende stellte sich heraus, dass Wasser nass ist, was keine Überraschung ist. Dennoch habe ich jetzt Code, um weiter zu experimentieren und möglicherweise interessantere Ergebnisse zu erzielen.
Quelle: habr.com
