Magento 2: importáljon termékeket közvetlenül az adatbázisba

В előző cikk Leírtam a termékek Magento 2-be történő importálásának folyamatát a szokásos módon - modelleken és tárolókon keresztül. A szokásos módszer nagyon alacsony adatfeldolgozási sebességgel rendelkezik. A laptopom körülbelül egy terméket termelt másodpercenként. Ebben a folytatásban fontolóra veszem a termék importálásának alternatív módját - az adatbázisba való közvetlen belépéssel, a szabványos Magento 2 mechanizmusok (modellek, gyárak, adattárak) megkerülésével. A termékek importálásához szükséges lépések sorrendje bármely olyan programozási nyelvhez igazítható, amely képes együttműködni a MySQL-lel.

A felelősség megtagadása: A Magento kész funkcionalitással rendelkezik adatimportálás és nagy valószínűséggel elég lesz neked. Ha azonban teljesebb ellenőrzésre van szüksége az importálási folyamat felett, és nem korlátozódik a CSV-fájl elkészítésére a meglévőhöz, üdvözöljük a cat.

Magento 2: importáljon termékeket közvetlenül az adatbázisba

A két cikk megírásából származó kód megtekinthető a Magento modulban "flancer32/mage2_ext_demo_import". Íme néhány korlátozás, amelyeket követtem a demómodul kódjának egyszerűsítése érdekében:

  • A termékeket csak létrehozzák, nem frissítik.
  • Egy raktár
  • Csak a kategórianevek importálása történik, szerkezetük nélkül
  • Az adatszerkezetek megfelelnek a 2.3-as verziónak

JSON egyetlen termék importálásához:

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

Az import főbb szakaszainak áttekintése

  • magának a terméknek a regisztrációja
  • kapcsolat a termék és a webhely között
  • alapvető terméktulajdonságok (EAV)
  • készletadatok (a termék raktáron lévő mennyisége)
  • média (képek)
  • kapcsolat a katalóguskategóriákkal

Termék regisztráció

Az alapvető termékinformációkat a 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`)
)

A terméknyilvántartásban való bejegyzés létrehozásához szükséges minimális információk:

  • attribute_set_id
  • sku

további:

  • type_id — ha nem adjuk meg, akkor az „egyszerű” lesz használva

Az adatbázisba való közvetlen íráshoz magának a Magento DB adapterét használom:

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

A termék regisztrációját követően catalog_product_entity láthatóvá válik az adminisztrációs panelen, a termékrácson (Katalógus/Termékek).

Magento 2: importáljon termékeket közvetlenül az adatbázisba

A termék és a webhely kapcsolata

A terméknek az oldallal való társítása határozza meg, hogy mely üzletekben és kiállítóhelyeken lesz elérhető a termék elöl.

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: importáljon termékeket közvetlenül az adatbázisba

Alapvető terméktulajdonságok

Az újonnan regisztrált terméknek még nincs neve vagy leírása. Mindez keresztül történik EAV attribútumok. Az alábbiakban felsoroljuk azokat az alapvető termékattribútumokat, amelyek szükségesek ahhoz, hogy a termék helyesen jelenjen meg az előlapon:

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

Egy ilyen termékhez külön attribútumot adunk (az attribútum kódjából az azonosító és az attribútum típusának megszerzésének részleteit kihagyjuk):

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

Az attribútumkód segítségével meghatározzuk annak azonosítóját és adattípusát (datetime, decimal, int, text, varchar), majd írja be az adminisztrációs ablak adatait a megfelelő táblába (store_id = 0).

Miután hozzáadta a fenti attribútumokat a termékhez, ez a kép jelenik meg az adminisztrációs panelen:

Magento 2: importáljon termékeket közvetlenül az adatbázisba

Leltári adatok

A Magento 2.3-as verziójától kezdve két párhuzamos táblakészlet található, amelyek a készletinformációkat (termékmennyiség) tárolják:

  • cataloginventory_*: régi szerkezet;
  • inventory_*: új struktúra (MSI - Multi Source Inventory);

Mindkét struktúrához hozzá kell adni a készletadatokat, mert az új szerkezet még nem teljesen független a régitől (nagyon valószínű, hogy azért default raktár az új struktúrában egy asztal szerepel cataloginventory_stock_status mint inventory_stock_1).

katalóguskészlet_

A Magneto 2.3 telepítésekor kezdetben 2 bejegyzésünk van store_website, amely két webhelynek felel meg - adminisztratív és fő kliens:

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

asztal cataloginventory_stock csak egy bejegyzésünk van:

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

Vagyis a régi struktúránkban csak egy „raktár” van (stock), és az adminisztratív webhelyhez kapcsolódik. Újak hozzáadása az adminisztrációs panelen keresztül sources/stocks az MSI-ben (új struktúra) nem eredményez új bejegyzéseket cataloginventory_stock.

A régi szerkezetű termékek készletadatait kezdetben táblázatokban rögzítjük:

  • cataloginventory_stock_item
  • cataloginventory_stock_status

katalóguskészlet_készlet_cikk

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

katalóguskészlet_készlet_állapota

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

leltár_

Kezdetben a készletadatok tárolására szolgáló új struktúra 1 "forrás"(inventory_source):

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

és egy "raktár"(inventory_stock):

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

«Forrás» a termékek fizikai tárolását jelöli (a rekord fizikai koordinátákat és levelezési címet tartalmaz). "Raktár"több "forrás" logikai egyesülése (inventory_source_stock_link)

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

azon a szinten, amelyen az értékesítési csatornához való kapcsolódás megtörténik (inventory_stock_sales_channel)

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

Az adatszerkezetből ítélve különféle típusú értékesítési csatornákat feltételezünk, de alapértelmezés szerint csak a kapcsolat „készlet"-" "(a weboldalra mutató hivatkozás a weboldal kódját követi - base).

egy"raktár"többhez köthető"forrás"és egy "forrás"- többnek"raktárak"(sok-több kapcsolat). A kivételek alapértelmezettek"forrás"És"raktár". Nem kapcsolódnak újra más entitásokhoz (kódszintű korlátozás - a hiba "Nem menthető az alapértelmezett forráshoz vagy az alapértelmezett készlethez kapcsolódó hivatkozás"). A Magento 2 MSI-struktúrájáról további részletek a cikkben találhatók.Raktárkezelési rendszer CQRS és Event Sourcing használatával. Tervezés”.

Az alapértelmezett konfigurációt fogom használni, és minden készletinformációt hozzáadok a forráshoz default, amely részt vesz a kóddal rendelkező weboldalhoz kapcsolódó értékesítési csatornában base (az üzlet elülső részének felel meg - lásd 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);
}

Miután hozzáadta a készletadatokat a termékhez az adminisztrációs panelen, ez a kép jelenik meg:

Magento 2: importáljon termékeket közvetlenül az adatbázisba

Média

Amikor „kézi” képet ad hozzá egy termékhez az adminisztrációs panelen keresztül, a vonatkozó információkat a következő táblázatok írják le:

  • catalog_product_entity_media_gallery: médianyilvántartás (kép- és videofájlok);
  • catalog_product_entity_media_gallery_value: a média összekapcsolása termékekkel és bemutatókkal (lokalizáció);
  • catalog_product_entity_media_gallery_value_to_entity: a média csak a termékekhez való összekapcsolása (feltehetően a termék alapértelmezett médiatartalma);
  • catalog_product_entity_varchar: Itt tárolódnak azok a szerepek, amelyekben a képet használják;

és maguk a képek a könyvtárba kerülnek ./pub/media/catalog/product/x/y/Ahol x и y — a képfájl nevének első és második betűje. Például fájl image.png néven kell menteni ./pub/media/catalog/product/i/m/image.png, így a platform képként használhatja a katalógusból származó termékek leírásakor.

Regisztráció feladva ./pub/media/catalog/product/ médiafájl (magának a fájl elhelyezésének folyamatáról ebben a cikkben nem lesz szó):

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

Regisztráláskor az új médiafájlhoz azonosítót rendelnek.

A regisztrált médiafájlt az alapértelmezett kirakat megfelelő termékéhez társítjuk:

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

A regisztrált médiafájlt a megfelelő termékhez társítjuk anélkül, hogy bármilyen kirakathoz kötnénk. Nem világos, hogy pontosan hol használják ezeket az adatokat, és miért nem lehet hozzáférni az előző táblázat adataihoz, de ez a tábla létezik, és az adatok akkor íródnak bele, amikor képet adnak a termékhez. Szóval ennyi.

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

Egy médiafájl különböző szerepekkel használható (a megfelelő attribútumkód zárójelben van feltüntetve):

  • Bázis(image)
  • Kis kép (small_image)
  • Miniatűr (thumbnail)
  • Képminta (swatch_image)

A szerepek médiafájlhoz való csatolása pontosan ez történik catalog_product_entity_varchar. A kötési kód hasonló a "Alapvető terméktulajdonságok”.

Miután hozzáadott egy képet a termékhez az adminisztrációs panelen, így néz ki:

Magento 2: importáljon termékeket közvetlenül az adatbázisba

Категории

Az adatokat kategóriánként tartalmazó fő táblázatok:

  • catalog_category_entity: kategóriák nyilvántartása;
  • catalog_category_product: kapcsolat a termékek és a kategóriák között;
  • catalog_category_entity_*: EAV attribútumértékek;

Kezdetben egy üres Magento alkalmazásban a kategória-nyilvántartás 2 kategóriát tartalmaz (az oszlopneveket lerövidítettem: 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|

Az id=1 kategória a teljes Magento katalógus gyökere, és nem érhető el sem az adminisztrációs panelen, sem a címlapon. Kategória id=2 (Alapértelmezett kategória) a fő webhely fő áruházának gyökérkategóriája (Fő webáruház) az alkalmazás telepítésekor jön létre (lásd. Admin / Üzletek / Minden üzlet). Ráadásul magának az üzletnek a gyökérkategóriája sem érhető el elöl, csak az alkategóriái.

Mivel ennek a cikknek a témája továbbra is a termékekre vonatkozó adatok importálása, ezért a kategóriák létrehozásakor nem használok közvetlen adatbevitelt az adatbázisba, hanem maga a Magento által biztosított osztályokat (modellek és repository-ok) fogom használni. Az adatbázisba való közvetlen belépés csak az importált termék kategóriához való társítására szolgál (a kategória a nevével párosul, és az egyeztetés során a kategóriaazonosító lekérésre kerül):

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

Miután hozzáadott egy terméklinket az „1. ​​kategória” és a „2. kategória” kategóriákhoz, a termékadatok az adminisztrációs panelen valahogy így néznek ki:

Magento 2: importáljon termékeket közvetlenül az adatbázisba

További műveletek

Az adatok importálása után a következő további lépéseket kell végrehajtania:

  • adatindexelés: hívás a konzolban ./bin/magento indexer:reindex;
  • URL-ek újragenerálása termékekhez/kategóriákhoz: használhatja a "" kiterjesztéstelgentos/regenerate-catalog-urls«

Termékek az adminisztrációs panelen további műveletek elvégzése után:

Magento 2: importáljon termékeket közvetlenül az adatbázisba

és elöl:

Magento 2: importáljon termékeket közvetlenül az adatbázisba

Összegzés

Ugyanaz a termékkészlet (10 darab), mint az előző cikkben, legalább egy nagyságrenddel gyorsabban importálódik (1 másodperc a 10-hez képest). A sebesség pontosabb becsléséhez nagyobb számú termékre van szükség – több százra, vagy jobb esetben több ezerre. Azonban még ilyen kis bemeneti adatok mellett is azt a következtetést vonhatjuk le, hogy a Magento által biztosított eszközök (modellek és adattárak) használata jelentős (hangsúlyozom - sok!) felgyorsítja a szükséges funkcionalitás fejlesztését, ugyanakkor jelentősen (hangsúlyozom - sok!) csökkenti az adatok adatbázisba kerülésének sebességét.

Ennek eredményeként a víz nedvesnek bizonyult, és ez nem kinyilatkoztatás. Most azonban megvan a kód, amellyel játszhatok, és talán érdekesebb következtetésekre jutok.

Forrás: will.com