Magento 2: importare prodotti da fonti esterne

Magento è una soluzione di e-commerce, ad es. è più finalizzato alla vendita dei prodotti che al magazzinaggio, alla logistica o alla contabilità finanziaria che accompagna le vendite. Altre applicazioni (ad esempio sistemi ERP) sono più adatte per le applicazioni di accompagnamento. Pertanto, molto spesso nella pratica dell'utilizzo di Magento si pone il compito di integrare il negozio con questi altri sistemi (ad esempio 1C).

In generale, l’integrazione può essere ridotta alla replica dei dati mediante:

  • catalogo (prodotti, categorie);
  • dati di inventario (saldi dei prodotti nei magazzini e prezzi);
  • clienti;
  • ordini;

Magento offre una classe separata di oggetti per manipolare i dati nel database: repository. A causa delle specificità di Magento, aggiungere dati al database tramite repository è facile da codificare, ma è, diciamo, lento. In questa pubblicazione, considero le fasi principali dell'aggiunta programmatica di un prodotto a Magento 2 nel modo "classico", utilizzando le classi repository.

I clienti e gli ordini vengono solitamente replicati nella direzione opposta, da Magento ai sistemi ERP esterni. Quindi con loro è più semplice, dal lato Magento devi solo selezionare i dati appropriati, e poi “i proiettili volarono fuori dalla nostra parte«.

Principi di registrazione dei dati in un database

Al momento, la creazione di oggetti salvati nel database in modo programmatico in Magento viene eseguita tramite Fabbrica:

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

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

e la scrittura nel database viene eseguita Repository:

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

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

L'approccio "Factory" e "Repository" può essere utilizzato per tutti i principali modelli nel dominio Magento 2.

Informazioni di base sul prodotto

Sto guardando una struttura dati che corrisponde alla versione Magento 2.3. Le informazioni di base sul prodotto sono riportate nella tabella catalog_product_entity (registro prodotti):

entity_id
attribute_set_id
type_id
sku
has_options
required_options
created_at
updated_at

Sono limitato a un tipo di prodotto (type_id='simple'), un insieme di attributi predefiniti (attribute_set_id=4) e ignorare gli attributi has_options и required_options. Poiché gli attributi entity_id, created_at и updated_at vengono generati automaticamente, quindi, infatti, per aggiungere un nuovo prodotto basta impostare sku. Lo faccio:

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

e ottengo un'eccezione:

The "Product Name" attribute value is empty. Set the attribute and try again.

Aggiungo il nome del prodotto alla richiesta e ricevo un messaggio che indica la mancanza dell'attributo Price. Dopo aver aggiunto il prezzo, il prodotto viene aggiunto al database:

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

Il nome del prodotto è archiviato nella tabella degli attributi varchar del prodotto (catalog_product_entity_varchar), prezzo - nella tabella catalog_product_entity_decimal. Prima di aggiungere un prodotto, è consigliabile indicare esplicitamente che stiamo utilizzando la vetrina amministrativa per importare i dati:

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

Attributi aggiuntivi

Elaborare attributi aggiuntivi del prodotto utilizzando Magento è un piacere. Modello dati EAV per le principali entità (vedi tabella eav_entity_type) è una delle caratteristiche principali di questa piattaforma. Aggiungiamo semplicemente gli attributi appropriati al modello del prodotto:

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

e quando si salva il modello tramite l'oggetto repo:

$repoProd->save($prod);

verranno memorizzati anche attributi aggiuntivi nelle tabelle del database corrispondenti.

Dati di inventario

In termini semplici: la quantità di prodotto in stock. In Magento 2.3, le strutture nel database che descrivono il formato per la memorizzazione dei dati di inventario sono significativamente diverso da quanto accaduto prima. Tuttavia, aggiungere la quantità di un prodotto in stock tramite il modello di prodotto non è molto più difficile che aggiungere altri attributi:

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

media

Di norma, il supporto mediatico per un prodotto per un cliente in un negozio (e-commerce) differisce dal supporto mediatico per lo stesso prodotto per un dipendente in un sistema di contabilità interna (ERP). Nel primo caso è consigliabile mostrare il prodotto dal vivo, nel secondo è sufficiente dare un'idea generale del prodotto. Tuttavia, trasferire almeno l’immagine primaria di un prodotto è abbastanza comune. case durante l'importazione dei dati.

Quando si aggiunge un'immagine tramite il pannello di amministrazione, l'immagine viene prima salvata in una directory temporanea (./pub/media/tmp/catalog/product) e solo al momento del salvataggio il prodotto viene spostato nella directory multimediale (./pub/media/catalog/product). Inoltre, quando aggiunta tramite il pannello di amministrazione, l'immagine viene taggata 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);

Per qualche motivo, il supporto viene collegato solo dopo aver salvato il prodotto e averlo nuovamente recuperato dal repository. Ed è necessario specificare l'attributo label quando si aggiunge una voce alla galleria multimediale del prodotto (altrimenti si ottiene un'eccezione Undefined index: label in .../module-catalog/Model/Product/Gallery/CreateHandler.php on line 516).

categoria

Spesso, la struttura delle categorie del negozio e dell'applicazione backend o il posizionamento dei prodotti al loro interno possono differire in modo significativo. Le strategie per la migrazione dei dati sulle categorie e sui prodotti al loro interno dipendono da molti fattori. In questo esempio mi attengo a quanto segue:

  • le categorie di backend e di negozio vengono confrontate per nome;
  • se viene importata una categoria che non è presente nel negozio, viene creata nella categoria principale (Default Category) e il suo ulteriore posizionamento nel catalogo del negozio è assunto manualmente;
  • un prodotto viene assegnato ad una categoria solo al momento della sua creazione nel negozio (prima importazione);

Le informazioni di base sulla categoria sono nella tabella catalog_category_entity (catalogo delle categorie). Creare una categoria in Magento:

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

Il collegamento di un prodotto a una categoria viene effettuato utilizzando l'ID della categoria e lo SKU del prodotto:

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

In totale

Scrivere codice per aggiungere un prodotto a livello di codice a Magento 2 è abbastanza semplice. Ho combinato tutto quanto sopra indicato in un modulo demo “flancer32/mage2_ext_demo_import". C'è solo un comando della console nel modulo fl32:import:prod, che importa i prodotti descritti nel file JSON "./etc/data/products.json":

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

Le immagini per l'importazione sono nel catalogo ./etc/data/img.

Il tempo necessario per importare 10 prodotti utilizzando questo metodo è di circa 10 secondi sul mio laptop. Se sviluppiamo ulteriormente questa idea, è facile giungere alla conclusione che è possibile importare circa 3600 prodotti all'ora e che possono essere necessarie circa 100 ore per importare 30 prodotti. La sostituzione di un laptop con un server consente di appianare in qualche modo la situazione. Magari anche più volte. Ma non per ordini di grandezza. Forse questa velocità e lentezza è in una certa misura una delle ragioni dell'emergere del progetto magento/async-import.

Una soluzione radicale per aumentare la velocità di importazione potrebbe essere l'inserimento diretto nel database, ma in questo caso tutte le “chicche” riguardanti l'estensibilità di Magento vanno perse: tutto ciò che è “avanzato” dovrai fare da solo. Tuttavia, ne vale la pena. Se funziona, prenderò in considerazione l'approccio con la scrittura diretta nel database nel prossimo articolo.

Fonte: habr.com

Aggiungi un commento