Im Ich habe den Prozess des Produktimports in Magento 2 auf herkömmliche Weise beschrieben – über Modelle und Repositories. Die übliche Methode weist eine sehr niedrige Datenverarbeitungsgeschwindigkeit auf. Auf meinem Laptop war es etwa ein Produkt pro Sekunde. In diesem Fortsetzung betrachte ich eine alternative Methode des Produktimports – die direkte Eintragung in die Datenbank unter Umgehung der Standardmechanismen von Magento 2 (Modelle, Fabriken, Repositories). Die Reihenfolge der Schritte, die den Import von Produkten sicherstellt, kann für jede Programmiersprache angepasst werden, die mit MySQL arbeiten kann.
Haftungsausschluss: In Magento gibt es eine fertige Funktionalität für und wahrscheinlich werden Sie damit auskommen. Wenn Sie jedoch mehr Kontrolle über den Importprozess benötigen, der nicht nur die Vorbereitung einer CSV-Datei umfasst, kommen Sie bitte unter die Haube.

Den aus dem Schreiben beider Artikel resultierenden Code können Sie im Magento-Modul „„ einsehen. Hier sind einige Einschränkungen, die ich beachtet habe, um den Demo-Modulkode zu vereinfachen:
- Produkte werden nur erstellt, nicht aktualisiert.
- Ein Lager
- Es werden nur die Namen der Kategorien importiert, ohne deren Struktur
- Die Datenstrukturen entsprechen der Version 2.3
JSON zum Import eines einzelnen Produkts:
{
"sku": "MVA20D-UBV-3",
"name": "Verschlussschutz für die Versiegelung VA47-29 IEK",
"desc": "Zugriff auf Geräte sicherstellen ...",
"desc_short": "Der Verschlussschutz für die Versiegelung VA47-29 IEK ist vorgesehen für ...",
"price": 5.00,
"qty": 25,
"categories": ["Kategorie 1", "Kategorie 2"],
"image_path": "mva20d_ubv_3.png"
}Übersicht der Hauptphasen des Imports
- Registrierung des Produkts selbst
- Verknüpfung des Produkts mit der Website
- Grundattribute des Produkts (EAV)
- Bestandsdaten (Menge des Produkts im Lager)
- Medien (Bilder)
- Verknüpfung mit den Kategorien im Katalog
Produktregistrierung
Die grundlegenden Informationen über das Produkt finden sich 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 '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 'Änderungszeit',
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 Eintrags im Produktverzeichnis:
attribut_set_idsku
zusätzlich:
typ_id— wenn wir nichts angeben, wird 'simple' verwendet
Für die direkte Datenbankaufzeichnung verwende ich den DB-Adapter von Magento selbst:
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 Produktgitter (Katalog / Produkte).

Verbindung zwischen Produkt und Website
Die Verbindung des Produkts mit der Website bestimmt, in welchen Geschäften und auf welchen Schaufenstern das Produkt im Frontend 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);
}
Basisattribute des Produkts
Ein frisch registriertes Produkt hat zunächst keinen Namen und keine Beschreibung. All das geschieht über . Hier ist die Liste der Basisattribute des Produkts, die benötigt werden, damit das Produkt im Frontend ausreichend korrekt angezeigt wird:
namePreisdescriptionkurze_beschreibungstatussteuer_klasse_idurl_keysichtbarkeit
Ein einzelnes Attribut wird so zum Produkt hinzugefügt (Details zur Abfrage der ID und des Typs des Attributs anhand seines Codes sind ausgelassen):
public function create($prodId, $attrCode, $attrValue)
{
$attrId = \/\* Attribut-ID anhand des Attributcodes abrufen *\/
$attrType = \/\* Attributtyp [datetime|decimal|int|text|varchar] anhand des Attributcodes abrufen *\/
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 im Standard-Store-View mit id=0 (admin) speichern *\/
'store_id' => 0,
'value' => $attrValue
];
$conn->insert($table, $bind);
}
}Durch den Attributcode bestimmen wir seine ID und Datentyp (datetime, decimal, int, text, varchar), dann schreiben wir die Daten in die entsprechende Tabelle für die administrative Ansicht (store_id = 0).
Nach dem Hinzufügen der oben genannten Attribute zum Produkt sieht das im Admin-Bereich wie folgt aus:

Inventardaten
Seit Version 2.3 von Magento gibt es parallel zwei Satz von Tabellen, die die Speicherung von Bestandsinformationen (Produktmenge) gewährleisten:
cataloginventory_*: alte Struktur;inventory_*: neue Struktur (MSI – Multi Source Inventory);
Inventardaten müssen in beide Strukturen hinzugefügt werden, da die neue Struktur derzeit noch nicht vollständig unabhängig von der alten ist (es scheint sehr wahrscheinlich, dass für default das Lager in der neuen Struktur die Tabelle cataloginventory_stock_status als inventory_stock_1).
cataloginventory_
Bei der Bereitstellung von Magneto 2.3 haben wir zunächst 2 Einträge in store_website, was zwei Websites entspricht – einer administrativen 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 haben wir nur einen Eintrag:
stock_id|website_id|stock_name|
--------|----------|----------|
1| 0|Standard |Das heißt, in der alten Struktur haben wir nur ein 'Lager' (stock) und es ist an die administrative Website gebunden. Das Hinzufügen neuer sources/stocks in MSI (der neuen Struktur) führt nicht dazu, dass neue Einträge in cataloginventory_stock.
Die Inventardaten zu den Produkten in der alten Struktur werden ursprünglich in den Tabellen festgehalten:
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 nur ein Lager in der 'cataloginventory' Struktur standardmäßig */
'stock_id' => 1,
'qty' => $qty,
'is_qty_decimal' => $isQtyDecimal,
'is_in_stock' => $isInStock,
/* standardlager ist an die Verwaltungswebsite 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 nur ein Lager in der 'cataloginventory' Struktur standardmäßig */
'stock_id' => 1,
'qty' => $qty,
'stock_status' => MagentoCatalogInventoryApiDataStockStatusInterface::STATUS_IN_STOCK,
/* standardlager ist an die Verwaltungswebsite gebunden (siehe `cataloginventory_stock`) */
'website_id' => 0
];
$conn->insert($table, $bind);
}inventory_
Ursprünglich enthält die neue Struktur zur Speicherung von Inventardaten 1 'Quelle» (inventory_source):
source_code|name |enabled|description |latitude|longitude|country_id|...|
-----------|--------------|-------|--------------|--------|---------|----------|...|
default |Standardquelle | 1|Standardquelle |0.000000| 0.000000|US |...|und ein 'lager» (inventory_stock):
stock_id|name |
--------|-------------|
1|Standardlager |«Quelle» ist ein physischer Speicher für Produkte (der Eintrag enthält physische Koordinaten und eine postalische Adresse). «Lager» stellt eine logische Verbindung mehrerer „Quellen“ dar (inventory_source_stock_link)
link_id|stock_id|source_code|priority|
-------|--------|-----------|--------|
1| 1|default | 1|auf der Ebene, wo die Bindung an den Verkaufskanal stattfindet (inventory_stock_sales_channel)
type |code|stock_id|
-------|----|--------|
website|base| 1|Aufgrund der Datenstruktur sind verschiedene Typen von Verkaufskanälen vorgesehen, aber standardmäßig wird nur die Verbindung „stock«-«, aber in Zukunft planen wir, verschiedene Kopien» verwendet (der Link zur Webseite erfolgt über den Code der Webseite — base).
Ein „lager» kann mit mehreren „Quellen« verbunden sein, während ein „Quelle» mit mehreren „Lagern» verknüpft sein kann (viele-zu-viele Beziehung). Die Ausnahme bilden die Default-„Quelle» und „lager«. Sie werden nicht an andere Entitäten gebunden (Einschränkung auf Codierungsebene — es tritt ein Fehler auf: „Can not save link related to Default Source or Default Stock„). Eine detailliertere Beschreibung der MSI-Struktur in Magento 2 kann in dem Artikel „«.
Ich werde die Default-Konfiguration verwenden und alle Lagerinformationen der Quelle hinzufügen default, die im Verkaufskanal verwendet wird, der mit der Webseite mit dem Code base verknüpft ist (entspricht dem Kundenbereich des Shops — siehe. 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}Nach dem Hinzufügen von Lagerdaten zum Produkt im Admin-Bereich ergibt sich folgendes Bild:

Medien
Bei der „manuellen“ Hinzufügung von Bildern zum Produkt über das Admin-Panel werden die entsprechenden Informationen in den folgenden Tabellen gespeichert:
catalog_product_entity_media_gallery: Medienregister (Bilder und Video-Dateien);catalog_product_entity_media_gallery_value: Verknüpfung von Medien mit Produkten und Shops (lokalisierung);catalog_product_entity_media_gallery_value_to_entity: Verknüpfung von Medien nur mit Produkten (wahrscheinlich Default-Medieninhalt für das Produkt);catalog_product_entity_varchar: hier werden Rollen gespeichert, in denen das Bild verwendet wird;
und die eigentlichen Bilder werden im Katalog gespeichert .\/pub\/media\/catalog\/product\/x\/y\/x und y — der erste und zweite Buchstabe des Dateinamens des Bildes. Zum Beispiel sollte die Datei image.png gespeichert werden als ./pub/media/catalog/product/i/m/image.png, damit die Plattform es als Bild bei der Beschreibung von Produkten aus dem Katalog verwenden kann.
catalog_product_entity_media_gallery
Registrieren Sie die in .\/pub\/media\/catalog\/product\/ Mediumdatei (der Prozess der Platzierung der Datei in diesem Artikel wird nicht behandelt):
function createMediaGallery($imgPathPrefixed)
{
$attrId = \/\* holen Sie sich die Attribut-ID anhand des Attributcodes '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' oder 'video' *\/
'media_type' => 'image',
'disabled' => false
];
$conn->insert($table, $bind);
$result = $conn->lastInsertId($table);
return $result;
}Bei der Registrierung wird der neuen Mediumdatei eine Identifikationsnummer zugewiesen.
catalog_product_entity_media_gallery_value
Wir verbinden die registrierte Mediumdatei mit dem entsprechenden Produkt für das Default-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,
\/\* verwenden Sie standardmäßig die Admin-Store-Ansicht *\/
'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 verbinden die registrierte Mediumdatei ohne Anbindung an einen bestimmten Store mit dem entsprechenden Produkt. Es ist unklar, wo genau diese Daten verwendet werden und warum die Daten der vorherigen Tabelle nicht verwendet werden können, aber diese Tabelle existiert und die Daten werden beim Hinzufügen eines Bildes zu einem Produkt hier gespeichert. Deshalb 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
Mediumdateien können mit verschiedenen Rollen verwendet werden (der Code des entsprechenden Attributs ist in Klammern angegeben):
- Basis (
Image) - Kleinbild (
small_image) - Miniaturansicht (
thumbnail) - Musterbild (
swatch_image)
Die Zuordnung von Rollen zu Mediumdateien erfolgt genau hier in catalog_product_entity_varchar. Der Code zur Zuordnung ist ähnlich wie der Code im Abschnitt „Basisattribute des Produkts«.
Nach dem Hinzufügen eines Bildes zum Produkt im Admin sieht es so aus:

Kategorien
Haupttabellen, die Daten zu Kategorien enthalten:
catalog_category_entity: Kategoriekatalog;catalog_category_product: Verbindung von Produkten und Kategorien;catalog_category_entity_*: Werte von EAV-Attributen;
Ursprünglich enthält die Kategoriedatenbank der leeren Magento-Anwendung 2 Kategorien (ich habe die Spaltennamen abgekü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 id=1 ist der Hauptknoten des gesamten Magento-Katalogs und ist weder im Admin noch im Frontend verfügbar. Die Kategorie mit id=2 (Default Category) ist die Stammkategorie für den Hauptshop der Hauptwebsite (Hauptwebsite Shop), die beim Bereitstellen der Anwendung erstellt wird (siehe Admin / Stores / Alle Stores). Außerdem ist die Stammkategorie des Shops im Frontend ebenfalls nicht verfügbar, nur ihre Unterkategorien.
Da das Thema dieses Artikels hauptsächlich den Datenimport von Produkten betrifft, werde ich keine direkte Datenbankeintragung bei der Erstellung von Kategorien verwenden, sondern die von Magento bereitgestellten Klassen (Modelle und Repositories) nutzen. Die direkte Datenbankeintragung wird nur verwendet, um das importierte Produkt mit der Kategorie zu verknüpfen (die Zuordnung der Kategorie erfolgt über ihren Namen, bei der Zuordnung wird die id der Kategorie abgerufen):
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);
}Nachdem die Verknüpfung des Produkts mit den Kategorien „Kategorie 1“ und „Kategorie 2“ hinzugefügt wurde, sieht es im Admin ungefähr so aus:

Zusätzliche Aktionen
Nach Abschluss des Datenimports müssen die folgenden zusätzlichen Schritte durchgeführt werden:
- Datenindizierung: Aufruf in der Konsole
./bin/magento indexer:reindex; - Regenerierung von URLs für Produkte/Kategorien: Es kann die Erweiterung „«
Produkte im Admin nach Ausführung der zusätzlichen Schritte:

und im Frontend:

Zusammenfassung
Dasselbe Set an Produkten (10 Stück) wird, wie im vorherigen Artikel, mindestens um einen Faktor schneller importiert (1 Sekunde im Vergleich zu 10). Für eine genauere Geschwindigkeitsbewertung sind mehr Produkte erforderlich — mehrere Hundert, am besten Tausende. Dennoch kann selbst bei einem so kleinen Datenvolumen der Schluss gezogen werden, dass die Nutzung des von Magento bereitgestellten Werkzeugs (Modelle und Repositories) erheblich (ich betone — deutlich!) beschleunigen die Entwicklung der benötigten Funktionalität, senken jedoch gleichzeitig erheblich (ich betone — deutlich!) die Geschwindigkeit, mit der Daten in die Datenbank gelangen.
Am Ende stellte sich heraus, dass Wasser nass ist, was keine überraschende Erkenntnis ist. Dennoch habe ich jetzt den Code, um weiter zu experimentieren und möglicherweise interessantere Ergebnisse zu erzielen.
Quelle: habr.com
