Magento 2: direktang mag-import ng mga produkto sa database

Π’ nakaraang artikulo Inilarawan ko ang proseso ng pag-import ng mga produkto sa Magento 2 sa karaniwang paraan - sa pamamagitan ng mga modelo at repositoryo. Ang karaniwang pamamaraan ay may napakababang bilis ng pagproseso ng data. Ang aking laptop ay gumagawa ng halos isang produkto bawat segundo. Sa pagpapatuloy na ito, isinasaalang-alang ko ang isang alternatibong paraan upang mag-import ng isang produkto - sa pamamagitan ng direktang pagpasok sa database, pag-bypass sa karaniwang mga mekanismo ng Magento 2 (mga modelo, pabrika, mga repositoryo). Ang pagkakasunud-sunod ng mga hakbang sa pag-import ng mga produkto ay maaaring iakma sa anumang programming language na maaaring gumana sa MySQL.

Pagtanggi sa pananagutan: Ang Magento ay may handa nang paggana para sa pag-import ng data at, malamang, sapat na ito para sa iyo. Gayunpaman, kung kailangan mo ng mas kumpletong kontrol sa proseso ng pag-import, hindi limitado sa paghahanda ng CSV file para sa kung ano ang mayroon ka, maligayang pagdating sa pusa.

Magento 2: direktang mag-import ng mga produkto sa database

Ang code na nagreresulta mula sa pagsulat ng parehong mga artikulo ay maaaring matingnan sa Magento module "flancer32/mage2_ext_demo_import". Narito ang ilang mga paghihigpit na sinunod ko upang gawing simple ang demo module code:

  • Ang mga produkto ay nilikha lamang, hindi na-update.
  • Isang bodega
  • Ang mga pangalan ng kategorya lamang ang ini-import, nang wala ang kanilang istraktura
  • Ang mga istruktura ng data ay sumusunod sa bersyon 2.3

JSON para sa pag-import ng isang produkto:

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

Pangkalahatang-ideya ng mga pangunahing yugto ng pag-import

  • pagpaparehistro ng produkto mismo
  • koneksyon sa pagitan ng produkto at website
  • mga pangunahing katangian ng produkto (EAV)
  • data ng imbentaryo (dami ng produkto sa stock)
  • media (mga larawan)
  • koneksyon sa mga kategorya ng catalog

Rehistro ng produkto

Ang pangunahing impormasyon ng produkto ay matatagpuan sa 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`)
)

Ang minimum na impormasyon na kinakailangan upang lumikha ng isang entry sa registry ng produkto ay:

  • attribute_set_id
  • sku

karagdagang:

  • type_id β€” kung hindi natin ito tinukoy, 'simple' ang gagamitin

Upang direktang magsulat sa database, ginagamit ko ang DB adapter ng Magento mismo:

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

Pagkatapos irehistro ang produkto sa catalog_product_entity makikita ito sa admin panel, sa grid ng produkto (Catalog/Mga Produkto).

Magento 2: direktang mag-import ng mga produkto sa database

Relasyon sa pagitan ng produkto at website

Ang kaugnayan ng produkto sa site ay tumutukoy kung saan ang mga tindahan at ipapakita ang produkto ay magiging available sa harap.

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: direktang mag-import ng mga produkto sa database

Mga pangunahing katangian ng produkto

Ang bagong rehistradong produkto ay wala pang pangalan o paglalarawan. Ang lahat ng ito ay ginagawa sa pamamagitan ng Mga katangian ng EAV. Narito ang isang listahan ng mga pangunahing katangian ng produkto na kailangan para maipakita nang tama ang produkto sa harap:

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

Ang isang hiwalay na katangian ay idinagdag sa isang produktong tulad nito (ang mga detalye ng pagkuha ng identifier at uri ng katangian mula sa code nito ay tinanggal):

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

Gamit ang attribute code, tinutukoy namin ang id at uri ng data nito (datetime, decimal, int, text, varchar), pagkatapos ay isulat ang data para sa administrative window sa naaangkop na talahanayan (store_id = 0).

Pagkatapos idagdag ang mga katangian sa itaas sa produkto, makukuha mo ang larawang ito sa admin panel:

Magento 2: direktang mag-import ng mga produkto sa database

Data ng imbentaryo

Simula sa bersyon 2.3 sa Magento, mayroong dalawang magkatulad na hanay ng mga talahanayan na nagbibigay ng imbakan ng impormasyon ng imbentaryo (dami ng produkto):

  • cataloginventory_*: lumang istraktura;
  • inventory_*: bagong istraktura (MSI - Multi Source Inventory);

Kailangan mong magdagdag ng data ng imbentaryo sa parehong mga istraktura, dahil ang bagong istraktura ay hindi pa ganap na independiyente sa luma (malamang na para sa default bodega sa bagong istraktura ang isang talahanayan ay kasangkot cataloginventory_stock_status bilang inventory_stock_1).

cataloginventory_

Kapag nagde-deploy ng Magneto 2.3, mayroon kaming 2 entry sa una store_website, na tumutugma sa dalawang site - administratibo at pangunahing kliyente:

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

mesa cataloginventory_stock isa lang ang entry natin:

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

Iyon ay, sa aming lumang istraktura mayroon lamang isang "bodega" (stock) at ito ay naka-link sa administratibong website. Pagdaragdag ng mga bago sa pamamagitan ng admin panel sources/stocks sa MSI (bagong istraktura) ay hindi nagreresulta sa mga bagong entry sa cataloginventory_stock.

Ang data ng imbentaryo tungkol sa mga produkto sa lumang istraktura ay unang naitala sa mga talahanayan:

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

imbentaryo_

Sa una, ang bagong istraktura para sa pag-iimbak ng data ng imbentaryo ay naglalaman ng 1 "pinagmulan"(inventory_source):

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

at isa "warehouse"(inventory_stock):

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

Β«PinagmulanΒ» kumakatawan sa pisikal na imbakan para sa mga produkto (ang talaan ay naglalaman ng mga pisikal na coordinate at mailing address). "Bodega"ay isang lohikal na unyon ng ilang "mga mapagkukunan" (inventory_source_stock_link)

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

sa antas kung saan nangyayari ang koneksyon sa channel ng pagbebenta (inventory_stock_sales_channel)

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

Sa paghusga sa istraktura ng data, ang iba't ibang uri ng mga channel ng pagbebenta ay ipinapalagay, ngunit bilang default lamang ang koneksyon "stock"-"website"(ang link sa website ay sumusunod sa website code - base).

isa"warehouse"maaaring iugnay sa ilang"mga mapagkukunan"at isa "pinagmulan"- sa ilan"mga bodega"(many-to-many relationship). Ang mga pagbubukod ay default "pinagmulan"At"warehouse". Hindi sila muling naka-link sa ibang mga entity (limitasyon sa antas ng code - ang error na "Hindi ma-save ang link na nauugnay sa Default na Pinagmulan o Default na Stock"). Higit pang mga detalye tungkol sa istraktura ng MSI sa Magento 2 ay matatagpuan sa artikulong "Sistema ng pamamahala ng bodega gamit ang CQRS at Event Sourcing. Disenyo".

Gagamitin ko ang default na configuration at idagdag ang lahat ng impormasyon ng imbentaryo sa pinagmulan default, na kasangkot sa channel ng pagbebenta na nauugnay sa website na may code base (naaayon sa harap na dulo ng tindahan - tingnan 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);
}

Pagkatapos magdagdag ng data ng imbentaryo sa produkto sa admin panel, makukuha mo ang larawang ito:

Magento 2: direktang mag-import ng mga produkto sa database

Media

Kapag "manu-manong" nagdaragdag ng isang larawan sa isang produkto sa pamamagitan ng admin panel, ang may-katuturang impormasyon ay isinusulat sa mga sumusunod na talahanayan:

  • catalog_product_entity_media_gallery: media registry (mga imahe at video file);
  • catalog_product_entity_media_gallery_value: pag-uugnay ng media sa mga produkto at showcase (lokalisasyon);
  • catalog_product_entity_media_gallery_value_to_entity: pagli-link ng media sa mga produkto lamang (malamang na default na nilalaman ng media para sa produkto);
  • catalog_product_entity_varchar: Ang mga tungkulin kung saan ginagamit ang larawan ay naka-imbak dito;

at ang mga imahe mismo ay nai-save sa direktoryo ./pub/media/catalog/product/x/y/Saan x ΠΈ y β€” ang una at pangalawang titik ng pangalan ng file ng imahe. Halimbawa, file image.png dapat i-save bilang ./pub/media/catalog/product/i/m/image.png, upang magamit ito ng platform bilang isang imahe kapag naglalarawan ng mga produkto mula sa catalog.

Naka-post sa register ./pub/media/catalog/product/ media file (ang proseso ng paglalagay ng file mismo ay hindi tinalakay sa artikulong ito):

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

Kapag nakarehistro, ang isang bagong media file ay bibigyan ng isang identifier.

Iniuugnay namin ang nakarehistrong media file sa kaukulang produkto para sa default na storefront:

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

Iniuugnay namin ang nakarehistrong media file sa kaukulang produkto nang hindi nakatali sa anumang storefront. Hindi malinaw kung saan eksaktong ginagamit ang data na ito at kung bakit imposibleng ma-access ang data mula sa nakaraang talahanayan, ngunit umiiral ang talahanayang ito at isinusulat dito ang data kapag may idinagdag na larawan sa produkto. Kaya ayun.

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

Maaaring gamitin ang isang media file na may iba't ibang tungkulin (ang kaukulang code ng katangian ay ipinahiwatig sa mga panaklong):

  • Base(image)
  • Maliit na Larawan (small_image)
  • Thumbnail (thumbnail)
  • Larawan ng Swatch (swatch_image)

Ang pag-uugnay ng mga tungkulin sa isang media file ay eksakto kung ano ang nangyayari sa catalog_product_entity_varchar. Ang binding code ay katulad ng code sa "Mga pangunahing katangian ng produkto".

Pagkatapos magdagdag ng larawan sa produkto sa admin panel, ganito ang hitsura:

Magento 2: direktang mag-import ng mga produkto sa database

Kategorya

Mga pangunahing talahanayan na naglalaman ng data ayon sa kategorya:

  • catalog_category_entity: рССстр ΠΊΠ°Ρ‚Π΅Π³ΠΎΡ€ΠΈΠΉ;
  • catalog_category_product: koneksyon sa pagitan ng mga produkto at kategorya;
  • catalog_category_entity_*: Mga halaga ng katangian ng EAV;

Sa una, sa isang walang laman na application ng Magento, ang pagpapatala ng kategorya ay naglalaman ng 2 kategorya (pinaikli ko ang mga pangalan ng column: 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|

Ang kategoryang may id=1 ay ang ugat ng buong Magento catalog at hindi available sa admin panel o sa front page. Kategorya na may id=2 (Default na Kategorya) ay ang root na kategorya para sa pangunahing tindahan ng pangunahing site (Pangunahing Website Store) nilikha kapag ang application ay na-deploy (tingnan. Admin / Tindahan / Lahat ng Tindahan). Bukod dito, ang root category ng store mismo ay hindi rin available sa harap, tanging ang mga subcategory nito.

Dahil ang paksa ng artikulong ito ay nag-i-import pa rin ng data sa mga produkto, hindi ako gagamit ng direktang pagpasok sa database kapag gumagawa ng mga kategorya, ngunit gagamitin ang mga klase na ibinigay ng Magento mismo (mga modelo at repositoryo). Ang direktang pagpasok sa database ay ginagamit lamang upang iugnay ang na-import na produkto sa isang kategorya (ang kategorya ay itinutugma sa pangalan nito, at ang category id ay kinukuha sa panahon ng pagtutugma):

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

Pagkatapos magdagdag ng link ng produkto sa mga kategoryang β€œKategorya 1” at β€œKategorya 2”, ganito ang hitsura ng mga detalye ng produkto sa admin panel:

Magento 2: direktang mag-import ng mga produkto sa database

Mga karagdagang aksyon

Kapag kumpleto na ang pag-import ng data, kailangan mong kumpletuhin ang mga sumusunod na karagdagang hakbang:

  • pag-index ng data: tumawag sa console ./bin/magento indexer:reindex;
  • muling pagbuo ng mga URL para sa mga produkto/kategorya: maaari mong gamitin ang extension na β€œelgentos/regenerate-catalog-urlsΒ«

Mga produkto sa admin panel pagkatapos magsagawa ng mga karagdagang pagkilos:

Magento 2: direktang mag-import ng mga produkto sa database

at sa harap:

Magento 2: direktang mag-import ng mga produkto sa database

Buod

Ang parehong hanay ng mga produkto (10 piraso) tulad ng sa nakaraang artikulo ay na-import nang hindi bababa sa isang order ng magnitude na mas mabilis (1 segundo kumpara sa 10). Upang mas tumpak na matantya ang bilis, kailangan mo ng mas malaking bilang ng mga produkto - ilang daang, o mas mabuti, libu-libo. Gayunpaman, kahit na may ganoong kaliit na laki ng data ng pag-input, maaari nating tapusin na ang paggamit ng mga tool na ibinigay ng Magento (mga modelo at repositoryo) ay makabuluhan (binigyang-diin ko - marami!) Pabilisin ang pag-unlad ng kinakailangang pag-andar, ngunit sa parehong oras ay makabuluhang (Binibigyang-diin ko - marami!) bawasan ang bilis ng pagpasok ng data sa database.

Dahil dito, naging basa ang tubig at hindi ito rebelasyon. Gayunpaman, ngayon ay mayroon akong code upang paglaruan at marahil ay dumating sa ilang mas kawili-wiling mga konklusyon.

Pinagmulan: www.habr.com