Magento 2: Produkte direkt in die Datenbank importieren

In einem früheren Artikel habe ich den Prozess des Importierens von Produkten in Magento 2 auf die übliche Weise beschrieben - über Modelle und Repositorys. Das übliche Verfahren zeichnet sich durch eine sehr geringe Datenverarbeitungsgeschwindigkeit aus. Auf meinem Laptop kam ungefähr ein Produkt pro Sekunde heraus. In dieser Fortsetzung betrachte ich eine alternative Möglichkeit zum Importieren eines Produkts - durch direkte Aufzeichnung in der 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 arbeiten kann.


Haftungsausschluss : Magento verfügt über eine vorgefertigte Funktion zum Importieren von Daten , von der Sie höchstwahrscheinlich genug haben. Wenn Sie jedoch eine umfassendere Kontrolle über den Importvorgang benötigen, müssen Sie nicht nur eine CSV-Datei für das vorbereiten, was sie ist - willkommen bei cat.


Bild


Der Code, der sich aus dem Schreiben beider Artikel ergibt, kann im flancer32 / mage2_ext_demo_import Magento-Modul angezeigt werden. Hier sind einige der Einschränkungen, die ich befolgt habe, um den Code des Demomoduls zu vereinfachen:


  • Produkte werden nur erstellt, nicht aktualisiert.
  • Ein Lagerhaus
  • 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" } 

Übersicht über die wichtigsten Importschritte


  • Produktregistrierung
  • Produkt und Website verlinken
  • grundlegende Produktattribute (EAV)
  • Bestandsdaten (Menge des auf Lager befindlichen Produkts)
  • Medien (Bilder)
  • Link zu 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`) ) 

Mindestinformationen zum Erstellen eines Eintrags in der Produktregistrierung:


  • attribute_set_id
  • sku

zusätzlich:


  • type_id - wenn nicht type_id , wird 'simple' verwendet

Für die direkte Aufzeichnung in der Datenbank verwende ich den DB-Adapter von Magento selbst:


 function create($sku, $typeId, $attrSetId) { /** @var \Magento\Framework\App\ResourceConnection $this->resource */ /** @var \Magento\Framework\DB\Adapter\Pdo\Mysql $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; } 

Nachdem Sie ein Produkt in catalog_product_entity registriert catalog_product_entity es im Admin-Bereich im Produktraster ( Katalog / Produkte ) catalog_product_entity .


Bild


Produkt und Website verknüpfen


Die Beziehung des Produkts zur Website bestimmt, in welchen Geschäften und in welchen Vitrinen das Produkt vorne erhältlich sein wird.


 function linkToWebsite($prodId, $websiteId) { /** @var \Magento\Framework\App\ResourceConnection $this->resource */ /** @var \Magento\Framework\DB\Adapter\Pdo\Mysql $conn */ $conn = $this->resource->getConnection(); $table = $this->resource->getTableName('catalog_product_website'); $bind = [ 'product_id' => $prodId, 'website_id' => $websiteId ]; $conn->insert($table, $bind); } 

Bild


Grundlegende Produktattribute


Ein frisch registriertes Produkt hat noch keinen Namen oder eine Beschreibung. All dies erfolgt über die EAV-Attribute . Hier ist eine Liste grundlegender Produktattribute, die erforderlich sind, um sicherzustellen, dass das Produkt vorne korrekt angezeigt wird:


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

So wird dem Produkt ein separates Attribut hinzugefügt (die Details zum Abrufen des Bezeichners und des Attributtyps anhand seines Codes 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 \Magento\Framework\App\ResourceConnection $this->resource */ /** @var \Magento\Framework\DB\Adapter\Pdo\Mysql $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); } } 

Bestimmen Sie anhand des Attributcodes die ID und den Datentyp ( datetime / datetime , decimal , int , text , varchar ). In die entsprechende Tabelle schreiben wir dann die Daten für die administrative Storefront ( store_id = 0 ).


Nachdem wir dem Produkt die oben genannten Attribute hinzugefügt haben, wird dieses Bild im Admin-Bereich angezeigt:


Bild


Inventardaten


Ab Version 2.3 verfügt Magento gleichzeitig über zwei Tabellensätze, in denen Inventarinformationen (Produktmenge) gespeichert werden:


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

Sie müssen beiden Strukturen Inventardaten hinzufügen, weil Die neue Struktur ist noch nicht vollständig unabhängig von der alten (anscheinend wird die Tabelle cataloginventory_stock_status als inventory_stock_1 für das default in der neuen Struktur verwendet).


cataloginventory_


Bei der Bereitstellung von Magneto 2.3 haben wir zunächst zwei Einträge in store_website , die zwei Sites entsprechen - dem administrativen und dem 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| 

In der Tabelle cataloginventory_stock haben wir 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 es ist an die Verwaltungswebsite gebunden. Das Hinzufügen neuer sources / stocks zum MSI über das Admin- stocks (neue Struktur) führt nicht zu neuen Einträgen in cataloginventory_stock .


Bestandsdaten zu Produkten in der alten Struktur werden zunächst in die folgenden Tabellen geschrieben:


  • cataloginventory_stock_item
  • cataloginventory_stock_status

cataloginventory_stock_item


 function createOldItem($prodId, $qty) { $isQtyDecimal = (((int)$qty) != $qty); $isInStock = ($qty > 0); /** @var \Magento\Framework\App\ResourceConnection $this->resource */ /** @var \Magento\Framework\DB\Adapter\Pdo\Mysql $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 \Magento\Framework\App\ResourceConnection $this->resource */ /** @var \Magento\Framework\DB\Adapter\Pdo\Mysql $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' => \Magento\CatalogInventory\Api\Data\StockStatusInterface::STATUS_IN_STOCK, /* default stock is bound to admin website (see `cataloginventory_stock`) */ 'website_id' => 0 ]; $conn->insert($table, $bind); } 

inventar_


Die neue Struktur zum Speichern von Inventardaten 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 " Lager " ( inventory_stock ):


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

Eine „ Quelle “ ist ein physischer Speicher für Produkte (der Datensatz enthält physische Koordinaten und eine Postanschrift). Ein " Lager " ist eine logische Vereinigung mehrerer "Quellen" ( inventory_source_stock_link )


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

auf dessen Ebene ein Link zum Vertriebskanal besteht ( inventory_stock_sales_channel )


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

Gemessen an der Datenstruktur werden verschiedene Arten von Vertriebskanälen angenommen, aber standardmäßig wird nur die Verbindung " Lager " - " Website " verwendet (der Link zur Website wird durch die Website-Codebasis angegeben).


Ein „ Lager “ kann mit mehreren „ Quellen “ verknüpft werden, und eine „ Quelle “ kann mit mehreren „ Lagern “ verknüpft werden (Viele-zu-Viele-Beziehung). Ausnahmen sind default'ovye " source " und " warehouse ". Sie sind nicht an andere Entitäten gebunden (Einschränkung auf Codeebene - der Fehler " Link kann nicht in Bezug auf Standardquelle oder Standardbestand gespeichert werden " stürzt ab). Weitere Informationen zur MSI-Struktur in Magento 2 finden Sie im Artikel " Warehouse Management System mit CQRS und Event Sourcing. Design ".


Ich werde die Standardkonfiguration verwenden und alle Inventarinformationen zur default hinzufügen, die in dem Vertriebskanal verwendet wird, der der Website mit dem store_website (entspricht dem Client-Teil des Geschäfts - siehe store_website ):


 function createNewItem($sku, $qty) { /** @var \Magento\Framework\App\ResourceConnection $this->resource */ /** @var \Magento\Framework\DB\Adapter\Pdo\Mysql $conn */ $conn = $this->resource->getConnection(); $table = $this->resource->getTableName('inventory_source_item'); $bind = [ 'source_code' => 'default', 'sku' => $sku, 'quantity' => $qty, 'status' => \Magento\InventoryApi\Api\Data\SourceItemInterface::STATUS_IN_STOCK ]; $conn->insert($table, $bind); } 

Nach dem Hinzufügen von Inventardaten zum Produkt im Admin-Bereich erhalten wir dieses Bild:


Bild


Medien


Wenn Sie dem Produkt über das Admin-Panel "manuell" Bilder hinzufügen, werden die relevanten Informationen in den folgenden Tabellen aufgezeichnet:


  • catalog_product_entity_media_gallery : Medienregistrierung (Bilder und Videodateien);
  • catalog_product_entity_media_gallery_value : Verknüpfen von Medien mit Produkten und Storefronts (Lokalisierung);
  • catalog_product_entity_media_gallery_value_to_entity : Binden von Medien nur an Produkte (vermutlich Standardmedieninhalt für das Produkt);
  • catalog_product_entity_varchar : Hier werden Rollen gespeichert, die das Bild verwenden.

und die Bilder selbst werden im Verzeichnis ./pub/media/catalog/product/x/y/ , wobei x und y der erste und zweite Buchstabe des Bilddateinamens sind. Beispielsweise muss die Datei image.png als ./pub/media/catalog/product/i/m/image.png werden, damit die Plattform sie als Image bei der Beschreibung von Produkten aus dem Katalog verwenden kann.



Wir registrieren die Mediendatei unter ./pub/media/catalog/product/ (der Vorgang des Platzierens der Datei in diesem Artikel wird nicht berücksichtigt):


 function createMediaGallery($imgPathPrefixed) { $attrId = /* get attribute ID by attribute code 'media_gallery' */ /** @var \Magento\Framework\App\ResourceConnection $this->resource */ /** @var \Magento\Framework\DB\Adapter\Pdo\Mysql $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 das Standard-Schaufenster:


 function createGalleryValue($mediaId, $prodId) { /** @var \Magento\Framework\App\ResourceConnection $this->resource */ /** @var \Magento\Framework\DB\Adapter\Pdo\Mysql $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 auf eine Storefront zu verweisen. Es ist nicht klar, wo genau diese Daten verwendet werden und warum es nicht möglich ist, auf die Daten der vorherigen Tabelle zuzugreifen, aber diese Tabelle existiert und die Daten werden beim Hinzufügen eines Bildes zum Produkt darauf geschrieben. Deshalb so.


 function createGalleryValueToEntity($mediaId, $prodId) { /** @var \Magento\Framework\App\ResourceConnection $this->resource */ /** @var \Magento\Framework\DB\Adapter\Pdo\Mysql $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 Code des entsprechenden Attributs ist in Klammern angegeben):


  • Basis ( image )
  • Kleines Bild ( small_image )
  • Vorschaubild ( thumbnail )
  • Swatch Image ( swatch_image )

Das Binden von Rollen an eine Mediendatei erfolgt nur in catalog_product_entity_varchar . Der Bindungscode ähnelt dem Code im Abschnitt " Grundlegende Produktattribute ".


Nach dem Hinzufügen des Bildes zum Produkt im Admin-Bereich sieht es folgendermaßen aus:


Bild


Kategorien


Haupttabellen, die Daten nach Kategorie enthalten:


  • catalog_category_entity : Register der Kategorien;
  • catalog_category_product : Zuordnung von Produkten und Kategorien;
  • catalog_category_entity_* : Werte von EAV-Attributen;

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 id = 1 ist das Stammverzeichnis des gesamten Magento-Verzeichnisses und steht weder im Admin-Bereich noch auf der Vorderseite zur Verfügung. Die Kategorie mit der ID = 2 ( Standardkategorie ) ist die Stammkategorie für den Hauptspeicher der Hauptwebsite ( Hauptwebsite-Speicher ), die während der Anwendungsbereitstellung erstellt wurde (siehe Admin / Stores / All Stores ). Darüber hinaus ist auch die Stammkategorie des Geschäfts an der Vorderseite nicht verfügbar, nur die Unterkategorien.


Da das Thema dieses Artikels immer noch Produktdaten importiert, verwende ich beim Erstellen von Kategorien keine direkte Aufzeichnung in der Datenbank, sondern die von Magento selbst bereitgestellten Klassen (Modelle und Repositorys). Die direkte Aufzeichnung in der Datenbank wird nur zum Verknüpfen des importierten Produkts mit der Kategorie verwendet (die Kategorie wird anhand ihres Namens zugeordnet, die Kategorie-ID wird beim Abgleich extrahiert):


 function create($prodId, $catId) { /** @var \Magento\Framework\App\ResourceConnection $this->resource */ /** @var \Magento\Framework\DB\Adapter\Pdo\Mysql $conn */ $conn = $this->resource->getConnection(); $table = $this->resource->getTableName('catalog_category_product'); $bind = [ 'category_id' => $catId, 'product_id' => $prodId, ]; $conn->insert($table, $bind); } 

Nach dem Hinzufügen eines Produktlinks zu den Kategorien "Kategorie 1" und "Kategorie 2" sehen die Produktdetails im Admin-Bereich ungefähr so ​​aus:


Bild


Zusätzliche Aktionen


Nach Abschluss des Datenimports müssen Sie die folgenden zusätzlichen Schritte ausführen:


  • ./bin/magento indexer:reindex : Ein Aufruf in der Konsole ./bin/magento indexer:reindex ;
  • URL-Regeneration für Produkte / Kategorien: Sie können die Erweiterung " elgentos / regenerate-catalog-urls " verwenden.

Produkte im Admin-Bereich nach Abschluss weiterer Schritte:


Bild


und vorne:


Bild


Zusammenfassung


Der gleiche Satz von Produkten (10 Stück) wie im vorherigen Artikel wird mindestens eine Größenordnung schneller importiert (1 Sekunde gegenüber 10). Für eine genauere Schätzung der Geschwindigkeit benötigen Sie eine größere Anzahl von Produkten - mehrere hundert und vorzugsweise Tausende. Trotzdem kann trotz einer so geringen Menge an Eingabedaten der Schluss gezogen werden, dass die Verwendung der von Magento bereitgestellten Tools (Modelle und Repositorys) die Entwicklung der erforderlichen Funktionalität erheblich beschleunigt (hervorheben - deutlich !), Aber erheblich (hervorheben - deutlich !) Reduziert Geschwindigkeit, mit der Daten in die Datenbank gelangen.


Infolgedessen stellte sich heraus, dass das Wasser nass war, und dies ist keine Offenbarung. Jetzt habe ich jedoch Code zum Spielen und möglicherweise interessantere Schlussfolgerungen.

Source: https://habr.com/ru/post/de440262/


All Articles