Magento 2: importējiet produktus tieši datu bāzē

В iepriekšējais raksts Es aprakstīju produktu importēšanas procesu Magento 2 parastajā veidā - izmantojot modeļus un repozitorijus. Parastajai metodei ir ļoti zems datu apstrādes ātrums. Mans klēpjdators ražoja apmēram vienu produktu sekundē. Šajā turpinājumā es apsveru alternatīvu veidu, kā importēt produktu - ar tiešu ievadi datu bāzē, apejot standarta Magento 2 mehānismus (modeļus, rūpnīcas, krātuves). Produktu importēšanas darbību secību var pielāgot jebkurai programmēšanas valodai, kas var darboties ar MySQL.

Atbildības noraidīšana: Magento ir gatava funkcionalitāte datu importēšana un, visticamāk, tev ar to pietiks. Tomēr, ja jums ir nepieciešama pilnīgāka importēšanas procesa kontrole, neaprobežojoties tikai ar CSV faila sagatavošanu tam, kas jums ir, laipni lūdzam cat.

Magento 2: importējiet produktus tieši datu bāzē

Abu rakstu rakstīšanas rezultātā iegūto kodu var apskatīt Magento modulī "flancer32/mage2_ext_demo_import". Šeit ir daži ierobežojumi, kurus ievēroju, lai vienkāršotu demonstrācijas moduļa kodu:

  • Produkti tiek tikai izveidoti, nevis atjaunināti.
  • Viena noliktava
  • Tiek importēti tikai kategoriju nosaukumi bez to struktūras
  • Datu struktūras atbilst versijai 2.3

JSON viena produkta importēšanai:

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

Pārskats par galvenajiem importa posmiem

  • paša produkta reģistrācija
  • savienojums starp produktu un vietni
  • pamata produkta atribūti (EAV)
  • krājumu dati (produkta daudzums noliktavā)
  • mediji (attēli)
  • saistība ar kataloga kategorijām

Produktu reģistrācija

Pamatinformāciju par produktu var atrast 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`)
)

Minimālā informācija, kas nepieciešama, lai izveidotu ierakstu produktu reģistrā, ir:

  • attribute_set_id
  • sku

papildus:

  • type_id — ja mēs to nenorādīsim, tiks izmantots “vienkāršs”.

Lai rakstītu tieši datu bāzē, es izmantoju paša Magento DB adapteri:

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

Pēc preces reģistrācijas ar catalog_product_entity tas kļūst redzams administratora panelī, produktu režģī (Katalogs/Produkti).

Magento 2: importējiet produktus tieši datu bāzē

Attiecības starp produktu un vietni

Produkta saistība ar vietni nosaka, kuros veikalos un displejos produkts būs pieejams priekšpusē.

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ējiet produktus tieši datu bāzē

Produkta pamatatribūti

Jaunreģistrētajai precei vēl nav ne nosaukuma, ne apraksta. Tas viss tiek darīts cauri EAV atribūti. Šeit ir saraksts ar pamata produkta atribūtiem, kas nepieciešami, lai produkts tiktu pareizi attēlots priekšpusē:

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

Šādam produktam tiek pievienots atsevišķs atribūts (detaļas par identifikatora iegūšanu un atribūta veidu no tā koda ir izlaista):

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

Izmantojot atribūta kodu, mēs nosakām tā id un datu tipu (datetime, decimal, int, text, varchar), pēc tam ierakstiet administratīvā loga datus attiecīgajā tabulā (store_id = 0).

Pēc iepriekš minēto atribūtu pievienošanas produktam administrēšanas panelī tiek parādīts šāds attēls:

Magento 2: importējiet produktus tieši datu bāzē

Inventāra dati

Sākot ar Magento versiju 2.3, ir divas paralēlas tabulu kopas, kas nodrošina krājumu informācijas (produkta daudzuma) glabāšanu:

  • cataloginventory_*: veca konstrukcija;
  • inventory_*: jauna struktūra (MSI - Multi Source Inventory);

Abām struktūrām jāpievieno krājumu dati, jo jaunā struktūra vēl nav pilnībā neatkarīga no vecās (ļoti iespējams, ka priekš default noliktava jaunajā struktūrā ir iesaistīts galds cataloginventory_stock_statusinventory_stock_1).

kataloga inventārs_

Izvietojot Magneto 2.3, mums sākotnēji ir 2 ieraksti store_website, kas atbilst divām vietnēm - administratīvajam un galvenajam klientam:

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

Tabula cataloginventory_stock mums ir tikai viens ieraksts:

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

Tas ir, mūsu vecajā struktūrā ir tikai viena “noliktava” (stock), un tas ir saistīts ar administratīvo vietni. Jaunu pievienošana, izmantojot administrēšanas paneli sources/stocks MSI (jauna struktūra) nerada jaunus ierakstus cataloginventory_stock.

Krājumu dati par precēm vecajā struktūrā sākotnēji tiek ierakstīti tabulās:

  • cataloginventory_stock_item
  • cataloginventory_stock_status

kataloga krājuma_noliktavas_prece

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

kataloga krājumu_statuss

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

inventārs_

Sākotnēji jaunajā krājumu datu glabāšanas struktūrā ir 1 "avots"(inventory_source):

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

un viens"noliktava"(inventory_stock):

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

«Avots» apzīmē produktu fizisko krātuvi (ierakstā ir fiziskās koordinātes un pasta adrese). "Noliktava"ir loģiska vairāku "avotu" savienība (inventory_source_stock_link)

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

līmenī, kurā tiek izveidots savienojums ar pārdošanas kanālu (inventory_stock_sales_channel)

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

Spriežot pēc datu struktūras, tiek pieņemti dažāda veida pārdošanas kanāli, bet pēc noklusējuma tikai savienojums “krājums"-"mājas lapa"(saite uz vietni seko vietnes kodam - base).

viens"noliktava"var saistīt ar vairākiem"avoti"un viens"avots"- vairākiem"noliktavas"(daudzi pret daudziem attiecības). Izņēmumi ir noklusējuma "avots"Un"noliktava". Tie nav atkārtoti saistīti ar citām entītijām (ierobežojums koda līmenī - kļūda "Nevar saglabāt saiti, kas saistīta ar noklusējuma avotu vai noklusējuma krājumu"). Sīkāku informāciju par MSI struktūru Magento 2 var atrast rakstā "Noliktavas vadības sistēma, izmantojot CQRS un Event Sourcing. Dizains".

Es izmantošu noklusējuma konfigurāciju un pievienošu visu inventāra informāciju avotam default, kas ir iesaistīts pārdošanas kanālā, kas saistīts ar vietni ar kodu base (atbilst veikala priekšpusei - sk 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);
}

Pēc krājumu datu pievienošanas produktam administratora panelī tiek parādīts šāds attēls:

Magento 2: importējiet produktus tieši datu bāzē

Mediju

“Manuāli” pievienojot produktam attēlu, izmantojot administratora paneli, attiecīgā informācija tiek ierakstīta šādās tabulās:

  • catalog_product_entity_media_gallery: mediju reģistrs (attēli un video faili);
  • catalog_product_entity_media_gallery_value: mediju saistīšana ar produktiem un vitrīnām (lokalizācija);
  • catalog_product_entity_media_gallery_value_to_entity: multivides saistīšana tikai ar produktiem (iespējams, produkta noklusējuma multivides saturs);
  • catalog_product_entity_varchar: šeit tiek saglabātas lomas, kurās tiek izmantots attēls;

un paši attēli tiek saglabāti direktorijā ./pub/media/catalog/product/x/y/Kur x и y — attēla faila nosaukuma pirmais un otrais burts. Piemēram, fails image.png jāsaglabā kā ./pub/media/catalog/product/i/m/image.png, lai platforma to varētu izmantot kā attēlu, aprakstot produktus no kataloga.

Reģistrācija ievietota ./pub/media/catalog/product/ multivides fails (paša faila ievietošanas process šajā rakstā nav apspriests):

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

Reģistrējoties jaunam multivides failam tiek piešķirts identifikators.

Mēs saistām reģistrēto multivides failu ar atbilstošo produktu noklusējuma veikala mājaslapai:

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

Mēs saistām reģistrēto multivides failu ar atbilstošo produktu, nesaistoties ar kādu veikalu. Nav skaidrs, kur tieši šie dati tiek izmantoti un kāpēc nav iespējams piekļūt datiem no iepriekšējās tabulas, taču šī tabula pastāv un dati tajā tiek ierakstīti, kad precei tiek pievienota bilde. Tā nu tas arī viss.

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

Multivides failu var izmantot ar dažādām lomām (iekavās norādīts atbilstošais atribūta kods):

  • Bāze(image)
  • Mazs attēls (small_image)
  • Sīktēls (thumbnail)
  • Swatch Image (swatch_image)

Lomu saistīšana ar multivides failu ir tieši tas, kas notiek catalog_product_entity_varchar. Saistošais kods ir līdzīgs kodam laukā "Produkta pamatatribūti".

Pēc attēla pievienošanas produktam administrēšanas panelī tas izskatās šādi:

Magento 2: importējiet produktus tieši datu bāzē

Kategorija

Galvenās tabulas, kas satur datus pēc kategorijas:

  • catalog_category_entity: kategoriju reģistrs;
  • catalog_category_product: saikne starp produktiem un kategorijām;
  • catalog_category_entity_*: EAV atribūtu vērtības;

Sākotnēji tukšā Magento lietojumprogrammā kategoriju reģistrā ir 2 kategorijas (es saīsināju kolonnu nosaukumus: crt Sākot no created_at, upd Sākot no 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 ar id=1 ir visa Magento kataloga sakne, un tā nav pieejama ne administrēšanas panelī, ne pirmajā lapā. Kategorija ar id=2 (Noklusējuma kategorija) ir galvenās vietnes galvenā veikala saknes kategorija (Galvenā vietnes veikals), kas izveidots lietojumprogrammas izvietošanas laikā (sk. Administrators / Veikali / Visi veikali). Turklāt paša veikala saknes kategorija arī nav pieejama priekšpusē, tikai tās apakškategorijas.

Tā kā šī raksta tēma joprojām ir datu importēšana par produktiem, tad, veidojot kategorijas, neizmantošu tiešu ievadi datu bāzē, bet izmantošu paša Magento nodrošinātās klases (modeļus un repozitorijus). Tieša ievade datu bāzē tiek izmantota tikai, lai saistītu importēto produktu ar kategoriju (kategorija tiek saskaņota ar tās nosaukumu, un atbilstības laikā tiek izgūts kategorijas 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);
}

Pēc produkta saites pievienošanas kategorijām “1. kategorija” un “2. kategorija” produkta informācija administrēšanas panelī izskatās apmēram šādi:

Magento 2: importējiet produktus tieši datu bāzē

Papildu darbības

Kad datu importēšana ir pabeigta, jums jāveic šādas papildu darbības:

  • datu indeksēšana: zvaniet konsolē ./bin/magento indexer:reindex;
  • produktu/kategoriju vietrāžu URL atjaunošana: varat izmantot paplašinājumu “elgentos/regenerate-catalog-urls«

Produkti administratora panelī pēc papildu darbību veikšanas:

Magento 2: importējiet produktus tieši datu bāzē

un priekšā:

Magento 2: importējiet produktus tieši datu bāzē

Kopsavilkums

Tas pats produktu komplekts (10 gab.) kā iepriekšējā rakstā tiek importēts vismaz par lielumu ātrāk (1 sekunde pret 10). Lai precīzāk novērtētu ātrumu, nepieciešams lielāks produktu skaits – vairāki simti vai vēl labāk tūkstoši. Tomēr pat ar tik mazu ievades datu apjomu varam secināt, ka Magento nodrošināto rīku (modeļu un repozitoriju) izmantošana ir nozīmīga (uzsveru - daudz!) paātrināt nepieciešamās funkcionalitātes izstrādi, bet tajā pašā laikā būtiski (uzsveru - daudz!) Samaziniet datu nokļūšanas datubāzē ātrumu.

Rezultātā ūdens izrādījās slapjš un tā nav nekāda atklāsme. Tomēr tagad man ir kods, ar ko spēlēt un, iespējams, nonākt pie vēl interesantākiem secinājumiem.

Avots: www.habr.com