Magento 2: Produkte direkt in die Datenbank importieren

В vorheriger Artikel Ich habe den Prozess des Importierens von Produkten in Magento 2 auf die übliche Weise beschrieben – über Modelle und Repositorys. Die übliche Methode weist eine sehr geringe Datenverarbeitungsgeschwindigkeit auf. Mein Laptop produzierte etwa ein Produkt pro Sekunde. In dieser Fortsetzung betrachte ich eine alternative Möglichkeit, ein Produkt zu importieren – durch direkte Eingabe in die Datenbank unter Umgehung der Standardmechanismen von Magento 2 (Modelle, Fabriken, Repositorys). Die Reihenfolge der Schritte zum Importieren von Produkten kann an jede Programmiersprache angepasst werden, die mit MySQL funktioniert.

Haftungsausschluss: Magento verfügt über vorgefertigte Funktionen für Datenimport und höchstwahrscheinlich wird es für Sie ausreichen. Wenn Sie jedoch eine vollständigere Kontrolle über den Importvorgang benötigen und sich nicht nur auf die Vorbereitung einer CSV-Datei für das, was Sie haben, beschränken möchten, sind Sie bei cat willkommen.

Magento 2: Produkte direkt in die Datenbank importieren

Der beim Schreiben beider Artikel entstandene Code kann im Magento-Modul eingesehen werden.lancer32/mage2_ext_demo_import". Hier sind einige Einschränkungen, die ich befolgt habe, um den Code des Demomoduls zu vereinfachen:

  • Produkte werden nur erstellt, nicht aktualisiert.
  • Ein Lager
  • Es werden nur Kategorienamen ohne deren Struktur importiert
  • Datenstrukturen entsprechen Version 2.3

JSON zum Importieren eines einzelnen Produkts:

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

Überblick über die wichtigsten Importphasen

  • Registrierung des Produkts selbst
  • Verbindung zwischen Produkt und Website
  • Grundlegende Produktattribute (EAV)
  • Bestandsdaten (Menge des vorrätigen Produkts)
  • Medien (Bilder)
  • Zusammenhang mit Katalogkategorien

Produktregistrierung

Grundlegende Produktinformationen finden Sie 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`)
)

Die für die Erstellung eines Eintrags im Produktregister erforderlichen Mindestinformationen sind:

  • attribute_set_id
  • sku

zusätzlich:

  • type_id — Wenn wir es nicht angeben, wird „einfach“ verwendet

Um direkt in die Datenbank zu schreiben, 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 bei catalog_product_entity Es wird im Admin-Panel im Produktraster sichtbar (Katalog/Produkte).

Magento 2: Produkte direkt in die Datenbank importieren

Beziehung zwischen Produkt und Website

Die Zuordnung des Produkts zur Website bestimmt, in welchen Geschäften und Displays das Produkt vorne 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);
}

Magento 2: Produkte direkt in die Datenbank importieren

Grundlegende Produktattribute

Das neu registrierte Produkt hat noch keinen Namen oder keine Beschreibung. All dies geschieht durch EAV-Attribute. Hier ist eine Liste der grundlegenden Produktattribute, die erforderlich sind, damit das Produkt auf der Vorderseite korrekt angezeigt wird:

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

Einem Produkt wie diesem wird ein separates Attribut hinzugefügt (die Details zum Erhalten der Kennung und des Typs des Attributs aus seinem Code werden weggelassen):

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

Mithilfe des Attributcodes bestimmen wir seine ID und seinen Datentyp (datetime, decimal, int, text, varchar), dann schreiben Sie die Daten für das Verwaltungsfenster in die entsprechende Tabelle (store_id = 0).

Nachdem Sie dem Produkt die oben genannten Attribute hinzugefügt haben, erhalten Sie dieses Bild im Admin-Bereich:

Magento 2: Produkte direkt in die Datenbank importieren

Inventurdaten

Ab Version 2.3 gibt es in Magento zwei parallele Tabellensätze, die die Speicherung von Bestandsinformationen (Produktmenge) ermöglichen:

  • cataloginventory_*: alte Struktur;
  • inventory_*: neue Struktur (MSI - Multi Source Inventory);

Sie müssen zu beiden Strukturen Bestandsdaten hinzufügen, weil Die neue Struktur ist noch nicht völlig unabhängig von der alten (es ist sehr wahrscheinlich, dass z default Lager in der neuen Struktur ist eine Tabelle beteiligt cataloginventory_stock_status als inventory_stock_1).

Kataloginventar_

Bei der Bereitstellung von Magneto 2.3 haben wir zunächst 2 Einträge in store_website, was zwei Sites entspricht – Verwaltungs- und Hauptclient:

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

Tisch cataloginventory_stock wir haben nur einen Eintrag:

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

Das heißt, in unserer alten Struktur gibt es nur ein „Lager“ (stock) und ist mit der Verwaltungswebsite verlinkt. Neue über das Admin-Panel hinzufügen sources/stocks in MSI (neue Struktur) führt nicht zu neuen Einträgen in cataloginventory_stock.

Bestandsdaten zu Produkten in der alten Struktur werden zunächst in Tabellen erfasst:

  • cataloginventory_stock_item
  • cataloginventory_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,
        /* 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);
}

Inventar_

Die neue Struktur zur Speicherung von Bestandsdaten enthält zunächst 1 "Quelle"(inventory_source):

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

und ein "Lagerhaus"(inventory_stock):

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

«Quelle» stellt den physischen Speicher für Produkte dar (der Datensatz enthält physische Koordinaten und Postanschrift). "Lagerhaus„ist eine logische Vereinigung mehrerer „Quellen“ (inventory_source_stock_link)

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

auf der Ebene, auf der die Verbindung zum Vertriebskanal erfolgt (inventory_stock_sales_channel)

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

Der Datenstruktur nach zu urteilen, werden verschiedene Arten von Vertriebskanälen angenommen, standardmäßig jedoch nur die Verbindung „-bestands-"-"Website „(Der Link zur Website folgt dem Website-Code – base).

Eins "Lagerhaus„kann mit mehreren verknüpft werden“Quellen"und ein "Quelle" - zu mehreren "Lagerhäuser„(Viele-zu-Viele-Beziehung). Die Ausnahmen sind standardmäßig „Quelle"Und"Lagerhaus". Sie werden nicht erneut mit anderen Entitäten verknüpft (Einschränkung auf Codeebene – der Fehler „Der Link zur Standardquelle oder zum Standardbestand kann nicht gespeichert werden"). Weitere Details zur MSI-Struktur in Magento 2 finden Sie im Artikel „Lagerverwaltungssystem mit CQRS und Event Sourcing. Design«.

Ich werde die Standardkonfiguration verwenden und alle Inventarinformationen zur Quelle hinzufügen default, der an dem Vertriebskanal beteiligt ist, der mit der Website mit dem Code verbunden ist base (entspricht dem vorderen Ende des Ladens – 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);
}

Nachdem Sie im Admin-Bereich Bestandsdaten zum Produkt hinzugefügt haben, erhalten Sie dieses Bild:

Magento 2: Produkte direkt in die Datenbank importieren

Medien

Beim „manuellen“ Hinzufügen eines Bildes zu einem Produkt über das Admin-Panel werden die relevanten Informationen in den folgenden Tabellen notiert:

  • catalog_product_entity_media_gallery: Medienregister (Bilder und Videodateien);
  • catalog_product_entity_media_gallery_value: Verknüpfung von Medien mit Produkten und Präsentationen (Lokalisierung);
  • catalog_product_entity_media_gallery_value_to_entity: Medien nur mit Produkten verknüpfen (vermutlich Standard-Medieninhalte für das Produkt);
  • catalog_product_entity_varchar: Hier werden die Rollen hinterlegt, in denen das Bild verwendet wird;

und die Bilder selbst werden im Verzeichnis gespeichert ./pub/media/catalog/product/x/y/Wo x и y – der erste und zweite Buchstabe des Bilddateinamens. Zum Beispiel Datei image.png sollte 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.

Registrieren veröffentlicht in ./pub/media/catalog/product/ Mediendatei (der Vorgang des Platzierens der Datei selbst wird in diesem Artikel nicht behandelt):

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

Bei der Registrierung wird einer neuen Mediendatei eine Kennung zugewiesen.

Wir verknüpfen die registrierte Mediendatei mit dem entsprechenden Produkt für die Standard-Storefront:

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

Wir verknüpfen die registrierte Mediendatei mit dem entsprechenden Produkt, ohne an eine Storefront gebunden zu sein. Es ist nicht klar, wo genau diese Daten verwendet werden und warum es nicht möglich ist, auf die Daten aus der vorherigen Tabelle zuzugreifen, aber diese Tabelle existiert und die Daten werden in sie geschrieben, wenn dem Produkt ein Bild hinzugefügt wird. Das war's.

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

Eine Mediendatei kann mit verschiedenen Rollen verwendet werden (der entsprechende Attributcode ist in Klammern angegeben):

  • Basis (image)
  • Kleines Bild (small_image)
  • Miniaturansicht (thumbnail)
  • Musterbild (swatch_image)

Das Verknüpfen von Rollen mit einer Mediendatei ist genau das, was in geschieht catalog_product_entity_varchar. Der Bindungscode ähnelt dem Code im „Grundlegende Produktattribute«.

Nachdem Sie dem Produkt im Admin-Bereich ein Bild hinzugefügt haben, sieht es so aus:

Magento 2: Produkte direkt in die Datenbank importieren

Kategorie

Haupttabellen mit Daten nach Kategorie:

  • catalog_category_entity: Kategorienregister;
  • catalog_category_product: Verbindung zwischen Produkten und Kategorien;
  • catalog_category_entity_*: EAV-Attributwerte;

In einer leeren Magento-Anwendung enthält die Kategorieregistrierung zunächst zwei 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 das Stammverzeichnis des gesamten Magento-Katalogs und ist weder im Admin-Panel noch auf der Startseite verfügbar. Kategorie mit ID=2 (Default Category) ist die Stammkategorie für den Hauptshop der Hauptseite (Hauptwebsite-Speicher), die bei der Bereitstellung der Anwendung erstellt wird (siehe. Admin / Geschäfte / Alle Geschäfte). Darüber hinaus ist vorne auch nicht die Stammkategorie des Shops selbst verfügbar, sondern nur seine Unterkategorien.

Da das Thema dieses Artikels immer noch der Import von Daten zu Produkten ist, werde ich beim Erstellen von Kategorien nicht auf die direkte Eingabe in die Datenbank zurückgreifen, sondern auf die von Magento selbst bereitgestellten Klassen (Modelle und Repositories) zurückgreifen. Der direkte Eintrag in die Datenbank dient nur dazu, das importierte Produkt einer Kategorie zuzuordnen (die Kategorie wird anhand ihres Namens abgeglichen und die Kategorie-ID wird während des Abgleichs 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 Sie einen Produktlink zu den Kategorien „Kategorie 1“ und „Kategorie 2“ hinzugefügt haben, sehen die Produktdetails im Admin-Panel etwa so aus:

Magento 2: Produkte direkt in die Datenbank importieren

Zusätzliche Aktionen

Sobald der Datenimport abgeschlossen ist, müssen Sie die folgenden zusätzlichen Schritte ausführen:

  • Datenindizierung: Aufruf in der Konsole ./bin/magento indexer:reindex;
  • URLs für Produkte/Kategorien neu generieren: Sie können die Erweiterung „elgentos/regenerate-catalog-urls«

Produkte im Admin-Panel nach Durchführung zusätzlicher Aktionen:

Magento 2: Produkte direkt in die Datenbank importieren

und vorne:

Magento 2: Produkte direkt in die Datenbank importieren

Zusammenfassung

Der gleiche Produktsatz (10 Stück) wie im vorherigen Artikel wird mindestens eine Größenordnung schneller importiert (1 Sekunde gegenüber 10). Um die Geschwindigkeit genauer einschätzen zu können, benötigt man eine größere Anzahl an Produkten – mehrere Hundert, besser noch Tausende. Selbst bei einer so geringen Menge an Eingabedaten können wir jedoch zu dem Schluss kommen, dass die Verwendung der von Magento bereitgestellten Tools (Modelle und Repositorys) von Bedeutung ist (ich betone – viel!) Beschleunigen Sie die Entwicklung der erforderlichen Funktionalität, aber gleichzeitig erheblich (ich betone - viel!) reduzieren die Geschwindigkeit, mit der Daten in die Datenbank gelangen.

Infolgedessen stellte sich heraus, dass das Wasser nass war, und das ist keine Offenbarung. Jetzt habe ich jedoch den Code, mit dem ich herumspielen und vielleicht zu weiteren interessanten Schlussfolgerungen kommen kann.

Source: habr.com