Magento 2: importuokite produktus tiesiai į duomenų bazę

В ankstesnis straipsnis Produktų importavimo į Magento 2 procesą aprašiau įprastu būdu – per modelius ir saugyklas. Įprastas metodas turi labai mažą duomenų apdorojimo greitį. Mano nešiojamas kompiuteris gamino maždaug vieną produktą per sekundę. Šiame tęsinyje svarstau alternatyvų būdą importuoti produktą – tiesioginiu įvedimu į duomenų bazę, apeinant standartinius Magento 2 mechanizmus (modelius, gamyklas, saugyklas). Produktų importavimo veiksmų seka gali būti pritaikyta bet kuriai programavimo kalbai, kuri gali veikti su MySQL.

Atsakomybės neigimas: Magento turi paruoštas funkcijas duomenų importas ir, greičiausiai, tau to užteks. Tačiau, jei jums reikia išsamesnės importo proceso kontrolės, neapsiribojant CSV failo paruošimu tam, ką turite, sveiki atvykę į cat.

Magento 2: importuokite produktus tiesiai į duomenų bazę

Kodą, gautą parašius abu straipsnius, galite peržiūrėti Magento modulyje "flancer32/mage2_ext_demo_import“. Štai keletas apribojimų, kurių laikiausi norėdamas supaprastinti demonstracinio modulio kodą:

  • Produktai tik kuriami, o ne atnaujinami.
  • Vienas sandėlis
  • Importuojami tik kategorijų pavadinimai be jų struktūros
  • Duomenų struktūros atitinka 2.3 versiją

JSON vienam produktui importuoti:

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

Pagrindinių importo etapų apžvalga

  • paties gaminio registracija
  • ryšys tarp produkto ir svetainės
  • pagrindinės produkto savybės (EAV)
  • atsargų duomenys (produkto kiekis sandėlyje)
  • žiniasklaida (nuotraukos)
  • ryšys su katalogo kategorijomis

Produkto registracija

Pagrindinę informaciją apie gaminį galite rasti 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`)
)

Minimali informacija, reikalinga norint sukurti įrašą produktų registre, yra:

  • attribute_set_id
  • sku

papildomas:

  • type_id — jei nenurodysime, bus naudojamas „paprastas“.

Norėdami rašyti tiesiai į duomenų bazę, naudoju paties Magento DB adapterį:

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

Užregistravus prekę su catalog_product_entity jis tampa matomas administratoriaus skydelyje, produkto tinklelyje (Katalogas/Produktai).

Magento 2: importuokite produktus tiesiai į duomenų bazę

Produkto ir svetainės ryšys

Produkto susiejimas su svetaine lemia, kuriose parduotuvėse ir parodose produktas bus prieinamas priekyje.

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: importuokite produktus tiesiai į duomenų bazę

Pagrindinės produkto savybės

Naujai registruotas produktas dar neturi pavadinimo ar aprašymo. Visa tai daroma per EAV atributika. Čia pateikiamas pagrindinių produkto atributų, reikalingų, kad produktas būtų tinkamai rodomas priekinėje pusėje, sąrašas:

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

Prie tokio produkto pridedamas atskiras atributas (išsami informacija apie identifikatoriaus gavimą ir atributo tipą iš jo kodo praleista):

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

Naudodami atributo kodą nustatome jo ID ir duomenų tipą (datetime, decimal, int, text, varchar), tada įrašykite administracinio lango duomenis į atitinkamą lentelę (store_id = 0).

Pridėję aukščiau nurodytus atributus prie produkto, administratoriaus skydelyje gausite šią nuotrauką:

Magento 2: importuokite produktus tiesiai į duomenų bazę

Inventorizacijos duomenys

Pradedant nuo 2.3 versijos Magento, yra du lygiagrečiai lentelių rinkiniai, kuriuose saugoma atsargų informacija (produkto kiekis):

  • cataloginventory_*: sena konstrukcija;
  • inventory_*: nauja struktūra (MSI – Multi Source Inventory);

Turite pridėti atsargų duomenis prie abiejų struktūrų, nes nauja struktūra dar nėra visiškai nepriklausoma nuo senosios (labai tikėtina, kad už default sandėlyje naujoje struktūroje yra įtrauktas stalas cataloginventory_stock_status kaip inventory_stock_1).

katalogo inventorius_

Diegdami Magneto 2.3 iš pradžių turime 2 įrašus store_website, kuri atitinka dvi svetaines – administracinę ir pagrindinę klientą:

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

Lentelė cataloginventory_stock turime tik vieną įrašą:

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

Tai yra, mūsų senoje struktūroje yra tik vienas „sandėlis“ (stock) ir ji yra susijusi su administracine svetaine. Naujų įtraukimas per administratoriaus skydelį sources/stocks MSI (nauja struktūra) nesukelia naujų įrašų cataloginventory_stock.

Atsargų duomenys apie senosios struktūros produktus iš pradžių įrašomi į lenteles:

  • cataloginventory_stock_item
  • cataloginventory_stock_status

Katalogo inventoriaus_atsargos_prekė

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

katalogo inventoriaus_atsargos_būsena

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

inventorius_

Iš pradžių naujoje atsargų duomenų saugojimo struktūroje yra 1 "šaltinis"(inventory_source):

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

ir vienas "sandėlis"(inventory_stock):

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

«šaltinis» reiškia fizinę produktų saugyklą (įraše yra fizinės koordinatės ir pašto adresas). “Sandėlis"yra logiška kelių "šaltinių" sąjunga (inventory_source_stock_link)

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

lygiu, kuriame atsiranda prisijungimas prie pardavimo kanalo (inventory_stock_sales_channel)

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

Sprendžiant iš duomenų struktūros, daroma prielaida, kad yra įvairių tipų pardavimo kanalai, tačiau pagal nutylėjimą tik ryšys “akcijos„-“Interneto svetainė"(nuoroda į svetainę yra po svetainės kodu - base).

vienas"sandėlis"gali būti susietas su keliais"šaltiniai"ir vienas "šaltinis"- keliems"sandėliai(santykis „daugelis su daugeliu“). Išimtys yra numatytosios "šaltinis"Ir"sandėlis“. Jie nėra iš naujo susieti su kitais objektais (ribojimas kodo lygiu - klaida “Nepavyko išsaugoti nuorodos, susijusios su numatytuoju šaltiniu arba numatytosiomis atsargomis“). Daugiau informacijos apie MSI struktūrą Magento 2 rasite straipsnyje "Sandėlio valdymo sistema naudojant CQRS ir Event Sourcing. Dizainas"

Naudosiu numatytąją konfigūraciją ir visą informaciją apie atsargas įtrauksiu į šaltinį default, kuris dalyvauja pardavimo kanale, susietame su kodu su svetaine base (atitinka parduotuvės priekinę dalį – žr 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);
}

Pridėję atsargų duomenis prie produkto administratoriaus skydelyje, gausite šį paveikslėlį:

Magento 2: importuokite produktus tiesiai į duomenų bazę

Žiniasklaida

Kai „rankiniu būdu“ pridedate vaizdą prie gaminio per administratoriaus skydelį, atitinkama informacija užrašoma šiose lentelėse:

  • catalog_product_entity_media_gallery: medijos registras (vaizdai ir vaizdo failai);
  • catalog_product_entity_media_gallery_value: žiniasklaidos susiejimas su produktais ir vitrinomis (lokalizacija);
  • catalog_product_entity_media_gallery_value_to_entity: medijos susiejimas tik su produktais (turbūt numatytasis produkto medijos turinys);
  • catalog_product_entity_varchar: čia saugomi vaidmenys, kuriuose naudojamas vaizdas;

o patys vaizdai išsaugomi kataloge ./pub/media/catalog/product/x/y/Kur x и y — pirmoji ir antroji vaizdo failo pavadinimo raidės. Pavyzdžiui, failas image.png turėtų būti išsaugotas kaip ./pub/media/catalog/product/i/m/image.png, kad platforma galėtų naudoti jį kaip vaizdą aprašant produktus iš katalogo.

Registracija paskelbta ./pub/media/catalog/product/ medijos failą (pats failo įdėjimo procesas šiame straipsnyje neaptariamas):

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

Užregistravus naujam medijos failui priskiriamas identifikatorius.

Mes susiejame registruotą medijos failą su atitinkamu numatytojo parduotuvės filialo produktu:

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

Mes susiejame registruotą medijos failą su atitinkamu produktu nesusiedami su jokiu parduotuvės filialu. Neaišku, kur tiksliai šie duomenys naudojami ir kodėl neįmanoma prieiti prie ankstesnės lentelės duomenų, tačiau ši lentelė egzistuoja ir į ją įrašomi duomenys, kai prie gaminio pridedamas paveikslėlis. Taigi viskas.

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

katalogo_produkto_entity_varchar

Medijos failas gali būti naudojamas su skirtingais vaidmenimis (skliausteliuose nurodytas atitinkamas atributo kodas):

  • Bazė(image)
  • Mažas vaizdas (small_image)
  • Miniatiūra (thumbnail)
  • Swatch vaizdas (swatch_image)

Vaidmenų susiejimas su medijos failu yra būtent tai, kas vyksta catalog_product_entity_varchar. Įrišimo kodas yra panašus į kodą „Pagrindinės produkto savybės"

Pridėjus vaizdą prie produkto administratoriaus skydelyje, jis atrodo taip:

Magento 2: importuokite produktus tiesiai į duomenų bazę

Категории

Pagrindinės lentelės su duomenimis pagal kategorijas:

  • catalog_category_entity: kategorijų registras;
  • catalog_category_product: ryšys tarp produktų ir kategorijų;
  • catalog_category_entity_*: EAV atributų reikšmės;

Iš pradžių tuščioje „Magento“ programoje kategorijų registre yra 2 kategorijos (stulpelių pavadinimus sutrumpinau: 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 su id=1 yra viso Magento katalogo šaknis ir nepasiekiama nei administratoriaus skydelyje, nei pirmajame puslapyje. Kategorija su id=2 (Numatytoji kategorija) yra pagrindinės svetainės pagrindinės parduotuvės šakninė kategorija (Pagrindinė internetinė parduotuvė), sukurtas diegiant programą (žr. Administratorius / Parduotuvės / Visos parduotuvės). Be to, pačios parduotuvės šakninės kategorijos priekyje taip pat nėra, tik jos subkategorijos.

Kadangi šio straipsnio tema vis dar yra duomenų apie gaminius importavimas, tai kurdamas kategorijas nenaudosiu tiesioginio įėjimo į duomenų bazę, o naudosiu paties Magento pateiktas klases (modelius ir saugyklas). Tiesioginis įėjimas į duomenų bazę naudojamas tik susieti importuotą prekę su kategorija (kategorija suderinama pagal jos pavadinimą, o derinimo metu gaunamas kategorijos ID):

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

Pridėjus produkto nuorodą į kategorijas „1 kategorija“ ir „2 kategorija“, išsami produkto informacija administratoriaus skydelyje atrodo maždaug taip:

Magento 2: importuokite produktus tiesiai į duomenų bazę

Papildomi veiksmai

Baigę importuoti duomenis, turėsite atlikti šiuos papildomus veiksmus:

  • duomenų indeksavimas: skambinkite konsolėje ./bin/magento indexer:reindex;
  • produktų / kategorijų URL atkūrimas: galite naudoti plėtinį „elgentos/regenerate-catalog-urls«

Produktai administratoriaus skydelyje atlikus papildomus veiksmus:

Magento 2: importuokite produktus tiesiai į duomenų bazę

ir priekyje:

Magento 2: importuokite produktus tiesiai į duomenų bazę

Santrauka

Tas pats gaminių rinkinys (10 vnt.), kaip ir ankstesniame straipsnyje, importuojamas bent dydžiu greičiau (1 sekundė, palyginti su 10). Norint tiksliau įvertinti greitį, reikia didesnio gaminių kiekio – kelių šimtų, o dar geriau tūkstančių. Tačiau net ir turėdami tokį mažą įvesties duomenų dydį galime daryti išvadą, kad Magento teikiamų įrankių (modelių ir saugyklų) naudojimas yra reikšmingas (pabrėžiu - daug!) paspartinti reikiamo funkcionalumo kūrimą, bet tuo pačiu žymiai (pabrėžiu - daug!) sumažinkite duomenų patekimo į duomenų bazę greitį.

Dėl to vanduo pasirodė šlapias ir tai nėra apreiškimas. Tačiau dabar turiu kodą, su kuriuo galiu žaisti, ir galbūt padarysiu įdomesnių išvadų.

Šaltinis: www.habr.com