Magento 2: uvoz proizvoda izravno u bazu podataka

В prethodni članak Opisao sam proces uvoza proizvoda u Magento 2 na uobičajen način – kroz modele i repozitorije. Uobičajena metoda ima vrlo nisku brzinu obrade podataka. Moje prijenosno računalo proizvodilo je otprilike jedan proizvod u sekundi. U ovom nastavku razmatram alternativni način uvoza proizvoda - izravnim unosom u bazu podataka, zaobilazeći standardne Magento 2 mehanizme (modeli, tvornice, repozitoriji). Redoslijed koraka za uvoz proizvoda može se prilagoditi bilo kojem programskom jeziku koji može raditi s MySQL-om.

Izjava o odricanju od odgovornosti: Magento ima gotovu funkcionalnost za uvoz podataka i, najvjerojatnije, bit će vam dovoljno. Međutim, ako trebate potpuniju kontrolu nad postupkom uvoza, ne ograničavajući se na pripremu CSV datoteke za ono što imate, dobrodošli u cat.

Magento 2: uvoz proizvoda izravno u bazu podataka

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

  • Proizvodi se samo kreiraju, a ne ažuriraju.
  • Jedno skladište
  • Uvoze se samo nazivi kategorija, bez njihove strukture
  • Strukture podataka u skladu su s 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
  • veza između proizvoda i web stranice
  • osnovne karakteristike proizvoda (EAV)
  • podaci o zalihama (količina proizvoda na skladištu)
  • mediji (slike)
  • povezanost s 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`)
)

Minimalni podaci potrebni za stvaranje unosa u registar proizvoda su:

  • attribute_set_id
  • sku

dodatno:

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

Za izravno pisanje 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 administratorskoj ploči, u mreži proizvoda (Katalog/Proizvodi).

Magento 2: uvoz proizvoda izravno u bazu podataka

Odnos između proizvoda i web stranice

Povezivanje proizvoda sa web mjestom određuje u kojim trgovinama i izlozima ć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 izravno u bazu podataka

Osnovne karakteristike proizvoda

Novoregistrirani proizvod još nema naziv ni opis. Sve se to radi putem EAV atributi. Ovdje je popis 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

Ovakvom proizvodu dodaje se zaseban atribut (pojedinosti o dobivanju identifikatora i vrsti 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);
    }
}

Pomoću šifre atributa određujemo njegov ID i tip podataka (datetime, decimal, int, text, varchar), zatim upiši podatke za administrativni prozor u odgovarajuću tablicu (store_id = 0).

Nakon dodavanja gornjih atributa proizvodu, dobit ćete ovu sliku u administratorskoj ploči:

Magento 2: uvoz proizvoda izravno u bazu podataka

Podaci o zalihama

Počevši od verzije 2.3 u Magentu, postoje dva paralelna skupa tablica koje omogućuju pohranu informacija o inventaru (količina proizvoda):

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

Trebate dodati podatke o inventaru u obje strukture, jer nova struktura još nije potpuno neovisna o staroj (vrlo je vjerojatno da će za default skladište u novoj strukturi uključen je stol cataloginventory_stock_status kao inventory_stock_1).

kataloški inventar_

Prilikom postavljanja Magneta 2.3 u početku imamo 2 unosa store_website, što odgovara dvije stranice - administrativnoj i glavnoj klijentskoj:

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

Stol 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 s administrativnom web stranicom. Dodavanje novih putem administratorske ploče sources/stocks u MSI (nova struktura) ne rezultira novim unosima u cataloginventory_stock.

Podaci o zalihama o proizvodima u staroj strukturi početno se bilježe u tablicama:

  • cataloginventory_stock_item
  • cataloginventory_stock_status

kataloški inventar_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);
}

kataloški_inventar_stanje_zaliha

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 pohranu 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 "dućan'(inventory_stock):

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

«Источник» predstavlja fizičko skladište za proizvode (zapis sadrži fizičke koordinate i poštansku adresu). "Skladište"logičan je spoj nekoliko "izvora" (inventory_source_stock_link)

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

na razini na kojoj se javlja veza s kanalom prodaje (inventory_stock_sales_channel)

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

Sudeći prema strukturi podataka, pretpostavljaju se različite vrste prodajnih kanala, ali prema zadanim postavkama samo veza “zaliha"-"web stranicu"(veza do web stranice slijedi kod web stranice - base).

Jedan "dućan"može se povezati s nekoliko"izvori"i jedan "izvor" - na nekoliko "skladišta"(odnos više-prema-više). Iznimke su zadane "izvor"A"dućan". Nisu ponovno povezani s drugim entitetima (ograničenje na razini koda - pogreška "Nije moguće spremiti 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 “Sustav upravljanja skladištem koristeći CQRS i Event Sourcing. Oblikovati”.

Koristit ću zadanu konfiguraciju i dodati sve informacije o inventaru u izvor default, koji je uključen u prodajni kanal povezan s web mjestom s kodom base (odgovara prednjem dijelu trgovine - 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 inventaru proizvodu na administrativnoj ploči, dobit ćete ovu sliku:

Magento 2: uvoz proizvoda izravno u bazu podataka

media

Prilikom “ručnog” dodavanja slike proizvodu kroz administrativnu ploču, relevantni podaci su zapisani u sljedećim tablicama:

  • catalog_product_entity_media_gallery: registar medija (slike i video datoteke);
  • catalog_product_entity_media_gallery_value: povezivanje medija s proizvodima i izlozima (lokalizacija);
  • catalog_product_entity_media_gallery_value_to_entity: povezivanje medija samo s proizvodima (vjerojatno zadani medijski sadržaj za proizvod);
  • catalog_product_entity_varchar: Ovdje su pohranjene uloge u kojima se slika koristi;

a same slike spremaju se u imenik ./pub/media/catalog/product/x/y/Gdje x и y — prvo i drugo slovo naziva slikovne datoteke. Na primjer, datoteka image.png treba spremiti kao ./pub/media/catalog/product/i/m/image.png, kako bi ga platforma mogla koristiti kao sliku pri opisu proizvoda iz kataloga.

Registar objavljen u ./pub/media/catalog/product/ medijska datoteka (sam proces postavljanja datoteke 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 registrira, novoj medijskoj datoteci se dodjeljuje identifikator.

Registriranu medijsku datoteku 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);
}

Registriranu medijsku datoteku povezujemo s odgovarajućim proizvodom bez vezivanja za bilo koji izlog. Nije jasno gdje se točno koriste ti podaci i zašto je nemoguće pristupiti podacima iz prethodne tablice, ali ova tablica postoji i podaci se u nju upisuju kada se doda slika na proizvod. Znaci 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);
}

kataloški_proizvod_entitet_varchar

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

  • Baza(image)
  • Mala slika (small_image)
  • Sličica (thumbnail)
  • Uzorak slike (swatch_image)

Povezivanje uloga s medijskom datotekom točno je ono što se događa u catalog_product_entity_varchar. Kod za vezanje sličan je kodu u "Osnovne karakteristike proizvoda”.

Nakon dodavanja slike proizvodu u administratorskoj ploči izgleda ovako:

Magento 2: uvoz proizvoda izravno u bazu podataka

Категории

Glavne tablice koje sadrže podatke po kategorijama:

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

U početku, u praznoj Magento aplikaciji, registar kategorija sadrži 2 kategorije (skratio sam nazive stupaca: 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 korijen je cijelog Magento kataloga i nije dostupna niti u admin panelu niti na naslovnoj stranici. Kategorija s id=2 (Zadana kategorija) je korijenska kategorija za glavnu trgovinu glavne stranice (Glavna trgovina web stranice) stvoreno kada je aplikacija postavljena (pogledajte. Admin / Trgovine / Sve trgovine). Štoviše, korijenska kategorija same trgovine također nije dostupna na prednjoj strani, samo njezine potkategorije.

Budući da je tema ovog članka još uvijek uvoz podataka o proizvodima, neću koristiti direktan unos u bazu prilikom kreiranja kategorija, već ću koristiti klase koje nudi sam Magento (modeli i repozitoriji). Izravan unos u bazu podataka koristi se samo za povezivanje uvezenog proizvoda s kategorijom (kategorija se uparuje prema nazivu, a ID kategorije se dohvaća tijekom 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 u kategorije "Kategorija 1" i "Kategorija 2", pojedinosti o proizvodu na administratorskoj ploči izgledaju otprilike ovako:

Magento 2: uvoz proizvoda izravno u bazu podataka

Dodatne radnje

Nakon dovršetka uvoza podataka morate izvršiti sljedeće dodatne korake:

  • indeksiranje podataka: poziv u konzoli ./bin/magento indexer:reindex;
  • ponovno generiranje URL-ova za proizvode/kategorije: možete koristiti proširenje “elgentos/regeneriraj-kataloške-urlove«

Proizvodi u administratorskoj ploči nakon izvođenja dodatnih radnji:

Magento 2: uvoz proizvoda izravno u bazu podataka

a sprijeda:

Magento 2: uvoz proizvoda izravno u bazu podataka

Rezime

Isti skup proizvoda (10 komada) kao u prethodnom članku uvozi se barem jedan red veličine brže (1 sekunda u odnosu na 10). Za točniju procjenu brzine potreban vam je veći broj proizvoda - nekoliko stotina, ili još bolje tisuća. No, čak i s tako malom količinom ulaznih podataka, možemo zaključiti da je korištenje alata koje Magento nudi (modeli i repozitoriji) značajno (naglašavam - mnogo!) ubrzati 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 kôd s kojim se mogu igrati i možda doći do nekih zanimljivijih zaključaka.

Izvor: www.habr.com