Magento 2: uvoz proizvoda direktno u bazu podataka

В prethodni članak Proces uvoza proizvoda u Magento 2 opisao sam na uobičajen način - kroz modele i repozitorije. Uobičajena metoda ima vrlo nisku brzinu obrade podataka. Moj laptop je proizvodio otprilike jedan proizvod u sekundi. U ovom nastavku razmatram alternativni način uvoza proizvoda - direktnim ulaskom u bazu podataka, zaobilazeći standardne Magento 2 mehanizme (modeli, fabrike, repozitorijumi). Redoslijed koraka za uvoz proizvoda može se prilagoditi bilo kojem programskom jeziku koji može raditi sa MySQL.

odricanje: Magento ima gotovu funkcionalnost za uvoz podataka i najvjerovatnije će vam to biti dovoljno. Međutim, ako vam je potrebna potpunija kontrola nad procesom uvoza, ne ograničena na pripremu CSV datoteke za ono što imate, dobrodošli u cat.

Magento 2: uvoz proizvoda direktno u bazu podataka

Kod nastao pisanjem oba članka može se vidjeti u Magento modulu "flancer32/mage2_ext_demo_import". Evo nekih ograničenja koja sam slijedio kako bih pojednostavio kod demo modula:

  • Proizvodi se samo kreiraju, ne ažuriraju.
  • Jedno skladište
  • Uvoze se samo nazivi kategorija, bez njihove strukture
  • Strukture podataka su u skladu sa verzijom 2.3

JSON za uvoz jednog proizvoda:

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

Pregled glavnih faza uvoza

  • registracija samog proizvoda
  • vezu između proizvoda i web stranice
  • osnovni atributi proizvoda (EAV)
  • podaci o zalihama (količina proizvoda na zalihama)
  • mediji (slike)
  • povezanost sa kataloškim kategorijama

Registracija proizvoda

Osnovne informacije o proizvodu možete pronaći u 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`)
)

Minimalne informacije potrebne za kreiranje unosa u registar proizvoda su:

  • attribute_set_id
  • sku

dodatno:

  • type_id — ako to ne navedemo, tada će se koristiti 'jednostavno'

Da pišem direktno u bazu podataka, koristim DB adapter samog Magenta:

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

Nakon registracije proizvoda kod catalog_product_entity postaje vidljiv u admin panelu, u mreži proizvoda (Katalog/Proizvodi).

Magento 2: uvoz proizvoda direktno u bazu podataka

Odnos između proizvoda i web stranice

Povezanost proizvoda sa sajtom određuje u kojim prodavnicama i displejima će proizvod biti dostupan na prednjoj strani.

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: uvoz proizvoda direktno u bazu podataka

Osnovni atributi proizvoda

Novoregistrirani proizvod još nema naziv ni opis. Sve se to radi kroz EAV atributi. Evo liste osnovnih atributa proizvoda koji su potrebni da bi proizvod bio ispravno prikazan na prednjoj strani:

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

Zaseban atribut se dodaje proizvodu kao što je ovaj (detalji o dobijanju identifikatora i tipa atributa iz njegovog koda su izostavljeni):

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

Koristeći kod atributa, određujemo njegov id i tip podataka (datetime, decimal, int, text, varchar), zatim upišite podatke za administrativni prozor u odgovarajuću tabelu (store_id = 0).

Nakon što proizvodu dodate gore navedene atribute, u admin panelu ćete dobiti ovu sliku:

Magento 2: uvoz proizvoda direktno u bazu podataka

Podaci o zalihama

Počevši od verzije 2.3 u Magentu, postoje dva paralelna skupa tabela koje omogućavaju skladištenje informacija o zalihama (količina proizvoda):

  • cataloginventory_*: stara struktura;
  • inventory_*: nova struktura (MSI - Multi Source Inventory);

Potrebno je dodati podatke o zalihama u obje strukture, jer nova struktura još nije potpuno nezavisna od stare (vrlo je vjerovatno da će za default magacin u novoj strukturi uključen je sto cataloginventory_stock_status kao inventory_stock_1).

kataloški inventar_

Kada implementiramo Magneto 2.3 u početku imamo 2 unosa store_website, što odgovara dvije lokacije - administrativnom i glavnom klijentu:

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

Table cataloginventory_stock imamo samo jedan unos:

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

Odnosno, u našoj staroj strukturi postoji samo jedno „skladište“ (stock) i povezan je sa administrativnom web lokacijom. Dodavanje novih preko admin panela sources/stocks u MSI (nova struktura) ne rezultira novim unosima u cataloginventory_stock.

Podaci o zalihama proizvoda u staroj strukturi inicijalno se evidentiraju u tabelama:

  • 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_

U početku, nova struktura za pohranjivanje podataka o zalihama sadrži 1 "izvor"(inventory_source):

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

i jedan"skladište"(inventory_stock):

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

«Izvor» predstavlja fizičko skladište za proizvode (zapis sadrži fizičke koordinate i poštansku adresu). "Skladište"je logičan spoj nekoliko "izvora" (inventory_source_stock_link)

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

na nivou na kojem dolazi do povezivanja sa prodajnim kanalom (inventory_stock_sales_channel)

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

Sudeći po strukturi podataka, pretpostavljaju se različiti tipovi prodajnih kanala, ali standardno samo veza “zaliha"-"sajt"(link do web stranice prati kôd web stranice - base).

jedan "skladište"može se povezati na nekoliko"izvori"i jedan"izvor" - na nekoliko "skladišta"(odnos mnogo-prema-više). Izuzeci su zadani "izvor"I"skladište". Nisu ponovo povezani sa drugim entitetima (ograničenje na nivou koda - greška “Nije moguće sačuvati vezu koja se odnosi na zadani izvor ili zadanu zalihu"). Više detalja o MSI strukturi u Magento 2 možete pronaći u članku “Sistem upravljanja skladištem koristeći CQRS i Event Sourcing. Dizajn".

Koristit ću zadanu konfiguraciju i dodati sve informacije o inventaru izvoru default, koji je uključen u prodajni kanal povezan s web-mjestom s kodom base (odgovara prednjem kraju prodavnice - vidi 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);
}

Nakon dodavanja podataka o zalihama proizvodu u admin panelu, dobijate ovu sliku:

Magento 2: uvoz proizvoda direktno u bazu podataka

Mediji

Prilikom “ručnog” dodavanja slike proizvodu preko admin panela, relevantne informacije su zapisane u sljedećim tabelama:

  • catalog_product_entity_media_gallery: registar medija (slike i video fajlovi);
  • catalog_product_entity_media_gallery_value: povezivanje medija sa proizvodima i izlozima (lokalizacija);
  • catalog_product_entity_media_gallery_value_to_entity: povezivanje medija samo sa proizvodima (vjerovatno zadani medijski sadržaj za proizvod);
  • catalog_product_entity_varchar: Ovdje se pohranjuju uloge u kojima se slika koristi;

a same slike se spremaju u direktorij ./pub/media/catalog/product/x/y/gde x и y — prvo i drugo slovo naziva datoteke slike. Na primjer, fajl image.png treba sačuvati kao ./pub/media/catalog/product/i/m/image.png, tako da ga platforma može koristiti kao sliku pri opisivanju proizvoda iz kataloga.

Registracija objavljena u ./pub/media/catalog/product/ medijski fajl (proces postavljanja samog fajla nije razmatran u ovom članku):

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

Kada se registruje, novom medijskom fajlu se dodeljuje identifikator.

Registrirani medijski fajl povezujemo s odgovarajućim proizvodom za zadani izlog:

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

Mi povezujemo registrovanu medijsku datoteku sa odgovarajućim proizvodom bez vezivanja za bilo koji izlog. Nije jasno gdje se tačno ti podaci koriste i zašto je nemoguće pristupiti podacima iz prethodne tabele, ali ova tabela postoji i podaci se u nju upisuju kada se slika doda u proizvod. To je to.

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

Medijska datoteka se može koristiti s različitim ulogama (odgovarajući kod atributa je naveden u zagradama):

  • baza(image)
  • mala slika (small_image)
  • Sličica (thumbnail)
  • Uzorak slike (swatch_image)

Povezivanje uloga sa medijskim fajlom je upravo ono što se dešava u njemu catalog_product_entity_varchar. Vezivni kod je sličan kodu u "Osnovni atributi proizvoda".

Nakon dodavanja slike proizvodu u admin panel to izgleda ovako:

Magento 2: uvoz proizvoda direktno u bazu podataka

Kategorije

Glavne tabele koje sadrže podatke po kategorijama:

  • catalog_category_entity: registar kategorija;
  • catalog_category_product: povezanost proizvoda i kategorija;
  • catalog_category_entity_*: EAV vrijednosti atributa;

U početku, u praznoj Magento aplikaciji, registar kategorija sadrži 2 kategorije (skratio sam nazive kolona: 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|

Kategorija s id=1 je korijen cijelog Magento kataloga i nije dostupna ni u admin panelu ni na naslovnoj stranici. Kategorija sa id=2 (Zadana kategorija) je osnovna kategorija za glavnu trgovinu glavne stranice (Glavna web stranica prodavnica) kreiran kada se aplikacija implementira (pogledajte. Admin / Prodavnice / Sve prodavnice). Štaviše, osnovna kategorija same prodavnice takođe nije dostupna na prednjoj strani, već samo njene potkategorije.

Budući da je tema ovog članka još uvijek uvoz podataka o proizvodima, neću koristiti direktan ulazak u bazu podataka prilikom kreiranja kategorija, već ću koristiti klase koje pruža sam Magento (modeli i spremišta). Direktan ulazak u bazu podataka koristi se samo za povezivanje uvezenog proizvoda sa kategorijom (kategorija se poklapa sa njenim imenom, a id kategorije se preuzima tokom uparivanja):

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

Nakon dodavanja veze proizvoda kategorijama “Kategorija 1” i “Kategorija 2”, detalji o proizvodu na admin panelu izgledaju otprilike ovako:

Magento 2: uvoz proizvoda direktno u bazu podataka

Dodatne radnje

Kada se uvoz podataka završi, potrebno je izvršiti sljedeće dodatne korake:

  • indeksiranje podataka: poziv u konzoli ./bin/magento indexer:reindex;
  • obnavljanje URL-ova za proizvode/kategorije: možete koristiti ekstenziju “elgentos/regenerate-catalog-urls«

Proizvodi u admin panelu nakon izvođenja dodatnih radnji:

Magento 2: uvoz proizvoda direktno u bazu podataka

i na prednjoj strani:

Magento 2: uvoz proizvoda direktno u bazu podataka

Rezime

Isti set proizvoda (10 komada) kao u prethodnom članku uvozi se barem red veličine brže (1 sekunda naspram 10). Da biste preciznije procijenili brzinu, potreban vam je veći broj proizvoda - nekoliko stotina, ili još bolje hiljade. Međutim, čak i uz tako malu veličinu ulaznih podataka, možemo zaključiti da je upotreba alata koje pruža Magento (modeli i repozitoriji) značajna (naglašavam - mnogo!) ubrzavaju razvoj potrebne funkcionalnosti, ali u isto vrijeme značajno (naglašavam - mnogo!) smanjiti brzinu kojom podaci ulaze u bazu podataka.

Kao rezultat toga, voda se pokazala mokrom i to nije otkriće. Međutim, sada imam kod za igru ​​i možda ću doći do nekih zanimljivijih zaključaka.

izvor: www.habr.com