Magento 2: Import von Produkten aus externen Quellen

Magento ist eine E-Commerce-Lösung, d.h. sie ist stĂ€rker auf den Verkauf von Produkten ausgerichtet als auf die damit verbundenen Lager-, Logistik- oder Finanzbuchhaltungsaufgaben. FĂŒr letztere sind andere Anwendungen (wie zum Beispiel ERP-Systeme) besser geeignet. Daher besteht in der Praxis hĂ€ufig die Notwendigkeit, Magento mit diesen anderen Systemen (z.B. mit 1C) zu integrieren.

Im Großen und Ganzen kann die Integration auf die Datenreplikation reduziert werden:

  • Katalog (Produkte, Kategorien);
  • Bestandsdaten (ProduktbestĂ€nde und Preise);
  • Kunden;
  • Bestellungen;

Magento bietet zur Manipulation von Daten in der Datenbank eine separate Objektklasse an — das Repository. Aufgrund der speziellen Natur von Magento ist das HinzufĂŒgen von Daten zur Datenbank ĂŒber Repositories leicht zu codieren, geschieht aber, sagen wir mal, nicht schnell. In diesem Artikel betrachte ich die Hauptphasen des programmatischen HinzufĂŒgens eines Produkts zu Magento 2 auf die "klassische" Weise — unter Verwendung von Repo-Klassen.

Kunden und Bestellungen werden normalerweise in die andere Richtung repliziert — von Magento in externe ERP-Systeme. Daher ist es damit einfacher, auf der Seite von Magento mĂŒssen einfach die entsprechenden Daten ausgewĂ€hlt werden, und dann — "von unserer Seite sind die Kugeln geflogen«.

Prinzipien der Dateneinschreibung in die Datenbank

Momentan erfolgt das Erstellen von speicherbaren Objekten in der Datenbank programmatisch in Magento ĂŒber Factory:

function __construct (MagentoCmsModelBlockFactory $blockFactory) {
    $this->blockFactory = $blockFactory;
}

/** @var MagentoCmsModelBlock $block * /
$block = $this->blockFactory->create();

und die Einspeisung in die Datenbank erfolgt ĂŒber Repository:

function __construct (MagentoCmsApiBlockRepositoryInterface $blockRepo) {
    $this->blockRepo = $blockRepo;
}

$this->blockRepo->save($block);

Der Ansatz mit "Factory" und "Repository" kann fĂŒr alle grundlegenden Modelle im Anwendungsbereich von Magento 2 verwendet werden.

Basisinformationen ĂŒber das Produkt

Ich betrachte die Datenstruktur, die der Version Magento 2.3 entspricht. Die grundlegendsten Informationen ĂŒber das Produkt befinden sich in der Tabelle catalog_product_entity (Produktenregister):

entity_id
attribute_set_id
type_id
sku
has_options
required_options
created_at
updated_at

Ich beschrÀnke mich auf einen Produkttyp (type_id='simple'), einem Satz von Standardattributen (attribute_set_id=4) und ignoriere die Attribute has_options und required_options. Da die Attribute entity_id, created_at und updated_at automatisch generiert werden, benötigen wir im Grunde nur die Angabe von sku. Ich mache es so:

/** @var MagentoCatalogApiDataProductInterfaceFactory $factProd */
/** @var MagentoCatalogApiProductRepositoryInterface $repoProd */
/** @var MagentoCatalogApiDataProductInterface $prod */
$prod = $factProd->create();
$prod->setAttributeSetId(4);
$prod->setTypeId('simple');
$prod->setSku($sku);
$repoProd->save($prod);

und erhalte eine Ausnahme:

Der Wert des Attributs "Produktname" ist leer. Setzen Sie das Attribut und versuchen Sie es erneut.

Ich fĂŒge den Produktnamen in die Anfrage ein und erhalte die Meldung, dass das Attribut fehlt. Preis. Nach HinzufĂŒgen des Preises wird das Produkt in die Datenbank aufgenommen:

$prod = $factProd->create();
$prod->setAttributeSetId(4);
$prod->setTypeId('simple');
$prod->setSku($sku);
$prod->setName($name);
$prod->setPrice($price);
$repoProd->save($prod);

Der Produktname wird in der Tabelle der varchar-Attribute des Produkts gespeichert (catalog_product_entity_varchar), der Preis in der Tabelle catalog_product_entity_decimal. Vor dem HinzufĂŒgen des Produkts ist es ratsam, ausdrĂŒcklich festzulegen, dass wir das Admin-Frontend zum Importieren von Daten verwenden:

/** @var MagentoStoreModelStoreManagerInterface $manStore */
$manStore->setCurrentStore(0);

ZusÀtzliche Attribute

Die Bearbeitung zusĂ€tzlicher Produktattribute in Magento ist eine Freude. Das EAV-Datenmodell fĂŒr die wichtigsten EntitĂ€ten (siehe Tabelle eav_entity_type) ist eines der SchlĂŒsselfeatures dieser Plattform. FĂŒgen Sie einfach die entsprechenden Attribute zum Produktmodell hinzu:

$prodEntity->setData('description', $desc);
$prodEntity->setData('short_description', $desc_short);
// oder
$prodEntity->setDescription($desc);
$prodEntity->setShortDescription($desc_short);

und beim Speichern des Modells ĂŒber das Repo-Objekt:

$repoProd->save($prod);

werden die zusÀtzlichen Attribute ebenfalls in den entsprechenden Datenbanktabellen gespeichert.

Inventardaten

Einfach gesagt, die Menge des Produkts auf Lager. In Magento 2.3 unterscheiden sich die Strukturen in der Datenbank, die das Format der Lagerdatenspeicherung beschreiben, deutlich von den vorherigen. Dennoch ist das HinzufĂŒgen der Menge des Produkts auf Lager ĂŒber das Produktmodell nicht viel komplizierter als das HinzufĂŒgen anderer Attribute:

/** @var MagentoCatalogModelProduct $prodEntity */
/** @var MagentoCatalogApiProductRepositoryInterface $repoProd */
$inventory = [
    'is_in_stock' => true,
    'qty' => 1234
];
$prodEntity->setData('quantity_and_stock_status', $inventory);
$repoProd->save($prodEntity);

Medien

In der Regel unterscheidet sich die MedienprĂ€sentation des Produkts fĂŒr den Kunden im Online-Shop (E-Commerce) von der MedienprĂ€sentation desselben Produkts fĂŒr den Mitarbeiter im internen ERP-System. Im ersten Fall ist es wĂŒnschenswert, das „Produkt im besten Licht“ zu zeigen, im zweiten Fall reicht es aus, einen allgemeinen Überblick ĂŒber das Produkt zu geben. Dennoch ist der Transfer auch nur eines PrimĂ€rbildes des Produkts ein recht gĂ€ngiger Fall beim Import von Daten.

Beim HinzufĂŒgen eines Bildes ĂŒber das Admin-Panel wird das Bild zunĂ€chst im temporĂ€ren Verzeichnis (./pub/media/tmp/catalog/product) gespeichert und wird erst beim Speichern des Produkts in das Medienverzeichnis (./pub/media/catalog/product) verschoben. Auch beim HinzufĂŒgen ĂŒber das Admin-Panel werden dem Bild die Tags Image, small_image, thumbnail, swatch_image.

/** @var MagentoCatalogApiProductRepositoryInterface $repoProd */
/** @var MagentoCatalogModelProductGalleryCreateHandler $hndlGalleryCreate */
/* $imagePath = '/path/to/file.png';  $imagePathRelative = '/f/i/file.png' */
$imagePathRelative = $this->imagePlaceToTmpMedia($imagePath);
/* reload product with gallery data */
$product = $repoProd->get($sku);
/* add image to product's gallery */
$gallery['images'][] = [
    'file' => $imagePathRelative,
    'media_type' => 'image'
    'label' => ''
];
$product->setData('media_gallery', $gallery);
/* set usage areas */
$product->setData('image', $imagePathRelative);
$product->setData('small_image', $imagePathRelative);
$product->setData('thumbnail', $imagePathRelative);
$product->setData('swatch_image', $imagePathRelative);
/* create product's gallery */
$hndlGalleryCreate->execute($product);

Aus irgendeinem Grund hĂ€ngt das Medium nur nach dem vorherigen Speichern des Produkts und dem erneuten Abrufen aus dem Repository. Und der Attribut muss angegeben werden label beim HinzufĂŒgen eines Eintrags zur Mediengalerie des Produkts (sonst erhalten wir eine Ausnahme Undefined index: label in .../module-catalog/Model/Product/Gallery/CreateHandler.php in Zeile 516).

Kategorien

Oft kann die Struktur der Kategorien des Shops und der Backend-Anwendungen oder deren Produktplatzierung erheblich variieren. Die Strategien zur Übertragung von Daten ĂŒber Kategorien und Produkte darin hĂ€ngen von vielen Faktoren ab. In diesem Beispiel halte ich mich an Folgendes:

  • Die Kategorien des Backends und des Shops werden anhand des Namens zugeordnet;
  • wenn eine Kategorie importiert wird, die im Shop nicht vorhanden ist, wird sie unter der Wurzelkategorie (Default Category) erstellt, und ihre weitere Positionierung im Katalog des Shops erfolgt manuell;
  • Die Zuordnung des Produkts zu einer Kategorie erfolgt nur bei seiner Erstellung im Shop (erstem Import);

Die Hauptinformationen zur Kategorie befinden sich in der Tabelle catalog_category_entity (Katalog der Kategorien). Kategorie in Magento erstellen:

/** @var MagentoCatalogApiDataCategoryInterfaceFactory $factCat */
/** @var MagentoCatalogApiCategoryRepositoryInterface $repoCat */
$cat = $factCat->create();
$cat->setName($name);
$cat->setIsActive(true);
$repoCat->save($cat);

Die Zuordnung des Produkts zur Kategorie erfolgt ĂŒber die ID der Kategorie und die SKU des Produkts:

/** @var MagentoCatalogModelCategoryProductLinkFactory $factCatProdLink */
/** @var MagentoCatalogApiCategoryLinkRepositoryInterface $repoCatLink */
$link = $factCatProdLink->create();
$link->setCategoryId($catMageId);
$link->setSku($prodSku);
$repoCatLink->save($link);

Insgesamt

Es ist nicht sehr schwierig, Code zum programmgesteuerten HinzufĂŒgen eines Produkts zu Magento 2 zu schreiben. Alles, was ich bisher gesagt habe, habe ich in ein Demomodul "flancer32/mage2_ext_demo_import" zusammengefasst. Im Modul gibt es nur einen Konsolenbefehl fl32:import:prod, der Produkte importiert, die in der JSON-Datei "./etc/data/products.json«:

[
  {
    "sku": "...",
    "name": "...",
    "desc": "...",
    "desc_short": "...",
    "price": ...,
    "qty": ...,
    "categories": ["..."],
    "image_path": "..."
  }
]

Die Bilder fĂŒr den Import befinden sich im Katalog ./etc/data/img.

Die Importzeit fĂŒr 10 Produkte auf diese Weise betrĂ€gt ungefĂ€hr 10 Sekunden auf meinem Laptop. Wenn man diesen Gedanken weiterfĂŒhrt, ist es nicht schwer zu erkennen, dass man pro Stunde etwa 3600 Produkte importieren kann, und der Import von 100.000 Produkten könnte etwa 30 Stunden dauern. Der Austausch des Laptops gegen einen Server kann die Situation etwas entschĂ€rfen. Vielleicht sogar um ein Vielfaches. Aber nicht in GrĂ¶ĂŸenordnungen. Möglicherweise ist diese langsame Geschwindigkeit teilweise einer der GrĂŒnde fĂŒr das Entstehen des Projekts magento/async-import.

Eine grundlegende Lösung zur Erhöhung der Importgeschwindigkeit könnte die direkte Speicherung in die Datenbank sein, jedoch gehen in diesem Fall alle «Vorteile» der Erweiterbarkeit von Magento verloren – alles, was «erweitert» werden muss, mĂŒsste man selbst umsetzen. Dennoch lohnt es sich. Wenn es gelingt, werde ich in dem nĂ€chsten Artikel den Ansatz mit der direkten Datenbankspeicherung in Betracht ziehen.

Quelle: habr.com

60GB SSD 8Gb DDR4