Magento 2: importimi i produkteve direkt në bazën e të dhënave

NĂ« artikulli i mĂ«parshĂ«m UnĂ« pĂ«rshkrova procesin e importit tĂ« produkteve nĂ« Magento 2 nĂ« mĂ«nyrĂ«n e zakonshme — pĂ«rmes modeleve dhe repozitorĂ«ve. MĂ«nyra e zakonshme ka njĂ« shpejtĂ«si tĂ« pĂ«rpunimit tĂ« tĂ« dhĂ«nave qĂ« Ă«shtĂ« mjaft e ulĂ«t. NĂ« laptopin tim dilte rreth njĂ« produkt nĂ« sekondĂ«. NĂ« kĂ«tĂ« vazhdim, shqyrtoj njĂ« mĂ«nyrĂ« alternative tĂ« importit tĂ« produkteve — duke shkruar direkt nĂ« bazĂ«n e tĂ« dhĂ«nave, duke anashkaluar mekanizmat standardĂ« tĂ« Magento 2 (modelet, fabrikat, repozitorĂ«t). Seqvenca e hapave qĂ« sigurojnĂ« importin e produkteve mund tĂ« adaptohet pĂ«r çdo gjuhĂ« programimi qĂ« Ă«shtĂ« e aftĂ« tĂ« punojĂ« me MySQL.

Kufizimi: Në Magento ka funksionalitet të gatshëm për importimin e të dhënave dhe me siguri do t'ju mjaftojë. Megjithatë, nëse ju nevojitet më shumë kontroll mbi procesin e importit, që nuk kufizohet në përgatitjen e një skedari CSV për atë që është, ju mirëpresim nën kat.

Magento 2: importimi i produkteve direkt në bazën e të dhënave

Kodi, që u krijua si rezultat i të shkruarit të të dyja artikujve, mund të shihet në modulit Magento "flancer32/mage2_ext_demo_import". Këtu janë disa kufizime që unë kam ndjekur për të thjeshtuar kodin e modulit demo:

  • Produkti vetĂ«m krijohet, nuk pĂ«rditĂ«sohet.
  • NjĂ« depo
  • Importohen vetĂ«m emrat e kategorive, pa strukturĂ«n e tyre
  • Strukturat e tĂ« dhĂ«nave i pĂ«rkasin versionit 2.3

JSON për importimin e një produkti të veçantë:

{
  "sku": "MVA20D-UBV-3",
  "name": "Mbyllës për plombimin VA47-29 IEK",
  "desc": "Sigurimi i aksesit në pajisje ...",
  "desc_short": "Mbyllësi për plombimin VA47-29 IEK është i destinuar për ...",
  "price": 5.00,
  "qty": 25,
  "categories": ["Kategoria 1", "Kategoria 2"],
  "image_path": "mva20d_ubv_3.png"
}

Përmbledhja e etapa kryesore të importit

  • regjistrimi i vetĂ« produktit
  • lidhja e produktit me web-in
  • atributet bazĂ« tĂ« produktit (EAV)
  • tĂ« dhĂ«nat e inventarit (sasia e produktit nĂ« depo)
  • mediat (imazhet)
  • lidhja me kategoritĂ« e katalologut

Regjistrimi i produktit

Informacioni bazë për produktin ndodhet në 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`)
)

Informacioni minimalisht i nevojshëm për krijimin e një regjistri produktesh:

  • attribute_set_id
  • sku

shtesë:

  • type_id — nĂ«se nuk e caktuam, do tĂ« pĂ«rdoret 'simple'

Për regjistrimin e drejtpërdrejtë në bazë përdor DB-adapterin e Magento-s:

function create($sku, $typeId, $attrSetId)
{
    \/** @var MagentoFrameworkAppResourceConnection $this->resource *\/\n    \/** @var MagentoFrameworkDBAdapterPdoMysql $conn *\/\n    $conn = $this->resource->getConnection();\n    $table = $this->resource->getTableName('catalog_product_entity');\n    $bind = [\n        'sku' => $sku,\n        'type_id' => $typeId,\n        'attribute_set_id' => $attrSetId\n    ];\n    $conn->insert($table, $bind);\n    $result = $conn->lastInsertId($table);\n    return $result;\n}

Pas regjistrimit të produktit në catalog_product_entity ai bëhet i dukshëm në administratë, në gridin e produkteve (Catalog / Products).

Magento 2: importimi i produkteve direkt në bazën e të dhënave

Lidhja e produktit me web-faqen

Lidhja e produktit me faqen definon në cilat dyqane dhe në cilat vitrina produkti do të jetë i aksesueshëm në front.

function linkToWebsite($prodId, $websiteId)
{
    \/** @var MagentoFrameworkAppResourceConnection $this->resource *\/\n    \/** @var MagentoFrameworkDBAdapterPdoMysql $conn *\/\n    $conn = $this->resource->getConnection();\n    $table = $this->resource->getTableName('catalog_product_website');\n    $bind = [\n        'product_id' => $prodId,\n        'website_id' => $websiteId\n    ];\n    $conn->insert($table, $bind);\n}

Magento 2: importimi i produkteve direkt në bazën e të dhënave

Atributet bazë të produktit

Një produkt i saporegistruar ende nuk ka as emër dhe as përshkrim. Të gjitha këto bëhen nëpërmjet EAV-atributet. Ja lista e atributve bazë të produktit, të cilat janë të nevojshme për të siguruar që prodotti të paraqitet mjaft saktë në front:

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

Një atribut i veçantë për produktin shtohet kështu (detajet për marrë identifikatorin dhe tipin e atributit sipas kodit të tij janë lënë jashtë):

public function create($prodId, $attrCode, $attrValue)
{
    $attrId = \/\* merr ID-në e atributit sipas kodit të atributit *\/\n    $attrType = \/\* merr tipin e atributit [datetime|decimal|int|text|varchar] sipas kodit të atributit *\/\n    if ($attrId) {\n        \/** @var MagentoFrameworkAppResourceConnection $this->resource *\/\n        \/** @var MagentoFrameworkDBAdapterPdoMysql $conn *\/\n        $conn = $this->resource->getConnection();\n        $tblName = 'catalog_product_entity_' . $attrType;\n        $table = $this->resource->getTableName($tblName);\n        $bind = [\n            'attribute_id' => $attrId,\n            'entity_id' => $prodId,\n            \/\* vendos të gjitha atributet në pamjen e dyqanit të parazgjedhur me id=0 (administratë) *\/\n            'store_id' => 0,\n            'value' => $attrValue\n        ];\n        $conn->insert($table, $bind);\n    }\n}

Përmes kodit të atributit, përcaktojmë ID-në dhe tipin e të dhënave (datetime, decimal, int, text, varchar), pastaj në tabelen përkatëse shkruajmë të dhënat për vitrinën administrative (store_id = 0).

Pas shtimit të atributeve të përmendura të produktit, pamja në administratë duket kështu:

Magento 2: importimi i produkteve direkt në bazën e të dhënave

Të dhënat e inventarit

Nga versioni 2.3, Magento ka dy grupe tabelash që funksionojnë paralelisht për ruajtjen e informacionit të inventarit (sasia e produktit):

  • cataloginventory_*: struktura e vjetĂ«r;
  • inventory_*: struktura e re (MSI — Multi Source Inventory);

Duhet të shtoni të dhëna inventari në të dy struktura, pasi struktura e re ende nuk është plotësisht e pavarur nga e vjetra (duket se për default depon në strukturën e re është përdorur tabela cataloginventory_stock_status si inventory_stock_1).

cataloginventory_

GjatĂ« implementimit tĂ« Magneto 2.3, fillimisht kemi 2 regjistrime nĂ« store_website, qĂ« pĂ«rkon me dy faqe — administratĂ«n dhe atĂ« kryesore tĂ« klientit:

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

Në tabelën cataloginventory_stock kemi vetëm një regjistrim:

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

D.m.th., në strukturën e vjetër kemi vetëm një «depo» (stock) dhe ajo është lidhur me faqen administruese. Shtimi i burimeve të reja sources/stocks në MSI (strukturën e re) nuk sjell krijimin e regjistrimeve të reja në cataloginventory_stock.

Të dhënat e inventarit për produktet në strukturën e vjetër inicialisht shkruhen në tabelat:

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

inventory_

Fillimisht, struktura e re për ruajtjen e të dhënave të inventarit përmban 1 «burimi» (inventory_source):

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

and one «magazina» (inventory_stock):

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

«Burimi» represents a physical storage for products (the record contains physical coordinates and postal address). «Magazina» represents a logical grouping of several «sources» (inventory_source_stock_link)

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

at which the binding to the sales channel occurs (inventory_stock_sales_channel)

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

Judging by the data structure, various types of sales channels are assumed, but by default, only the connection «stock«-«website» (the link to the website goes by the website code — base).

One «magazina» can be linked to multiple «sources«, while one «burimi» can be linked to multiple «magazina» (the relationship is «many-to-many»). Exceptions are the default «burimi» dhe "magazina«. They cannot be re-linked to other entities (a code-level restriction — it throws an error «Can not save link related to Default Source or Default Stock«). More details about the MSI structure in Magento 2 can be found in the article «Inventory Management System using CQRS and Event Sourcing. Design«.

I will use the default configuration and add all inventory information to the source default, which is involved in the sales channel related to the website with the code base (corresponds to the client side of the store — see. 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);
}

After adding inventory data to the product in the admin panel, it looks like this:

Magento 2: importimi i produkteve direkt në bazën e të dhënave

Media

When adding images to a product manually through the admin panel, the corresponding information is recorded in the following tables:

  • catalog_product_entity_media_gallery: media registry (images and video files);
  • catalog_product_entity_media_gallery_value: linking media to products and storefronts (localization);
  • catalog_product_entity_media_gallery_value_to_entity: linking media only to products (presumably default media content for the product);
  • catalog_product_entity_varchar: kĂ«tu ruajnĂ« rolet ku pĂ«rdoret imazhi;

ndĂ«rsa vetĂ« imazhet ruhen nĂ« katalog .\/pub\/media\/catalog\/product\/x\/y\/index x dhe y — shkronjat e para dhe tĂ« dyta tĂ« emrit tĂ« skedarit me imazhin. PĂ«r shembull, skedari image.png duhet tĂ« ruhet si ./pub/media/catalog/product/i/m/image.png, nĂ« mĂ«nyrĂ« qĂ« platforma ta pĂ«rdorĂ« si imazh nĂ« pĂ«rshkrimin e produkteve nga katalogu.

Regjistrojmë skedarin mediatik në .\/pub\/media\/catalog\/product\/ skedari mediatik (procesi i vendosjes së skedarit në këtë artikull nuk shqyrtohet):

function createMediaGallery($imgPathPrefixed)
{
    $attrId = /* merr id-në e atributit sipas kodit të atributit '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' ose 'video' */
        'media_type' => 'image',
        'disabled' => false
    ];
    $conn->insert($table, $bind);
    $result = $conn->lastInsertId($table);
    return $result;
}

Kur regjistrohet, një skedari të ri media i jepet një identifikues.

Lidhim skedarin mediatik të regjistruar me produktin për pasqyrën default:

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,
        /* përdor pamjen e dyqanit administrues si default */
        'store_id' => 0,
        'entity_id' => $prodId,
        'label' => null,
        /* ne kemi vetëm një imazh */
        'position' => 1,
        'disabled' => false
    ];
    $conn->insert($table, $bind);
}

Lidhim skedarin mediatik të regjistruar me produktin pa lidhje me ndonjë pasqyrë. Nuk është e qartë se ku përdoren këto të dhëna dhe pse nuk mund të referohemi te të dhënat e tabelës së mëparshme, por kjo tabelë ekziston dhe të dhënat regjistrohen në të kur shtohet një imazh në produkt. Prandaj, ashtu.

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

Skedari mediatik mund të përdoret me role të ndryshme (në kllapa është kodi i atributit përkatës):

  • Base (image)
  • Small Image (small_image)
  • Thumbnail (thumbnail)
  • Swatch Image (swatch_image)

Lidhja e roleve me skedarin mediatik ndodh në catalog_product_entity_varchar. Kodi i lidhjes është ngjashëm me kodin në seksionin «Atributet bazë të produktit«.

Pas pastrimit të një imazhi në produktin në panelin e administratorit, duket kështu:

Magento 2: importimi i produkteve direkt në bazën e të dhënave

Kategoritë

Tabelat kryesore që përmbajnë të dhëna për kategoritë:

  • catalog_category_entity: regjistri i kategorive;
  • catalog_category_product: lidhja midis produkteve dhe kategorive;
  • catalog_category_entity_*: vlerat e atributeve EAV;

Fillimisht, nĂ« njĂ« aplikacion Magento tĂ« zbrazĂ«t, regjistri i kategorive pĂ«rmban 2 kategori (unĂ« shkurtoj emrat e kolonave: 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|

Kategoria me id=1 është rrënja e gjithë katalogut Magento dhe nuk është e aksesueshme as në panelin e administratorit, as në front. Kategoria me id=2 (Default Category) është kategoria rrënjë për dyqanin kryesor të sitit kryesor (Main Website Store), e krijuar gjatë shpërndarjes së aplikacionit (shih. Admin / Stores / All Stores). Për më tepër, vetë kategoria rrënjë e dyqanit në front gjithashtu nuk është e aksesueshme, vetëm nënkategoritë e saj.

Duke qenë se subjekti i këtij artikulli është importi i të dhënave për produktet, nuk do ta përdor regjistrimin e drejtpërdrejtë në bazë gjatë krijimit të kategorive, por do të përdor klasat e ofruara nga vetë Magento (modelet dhe depozitë). Regjistrimi i drejtpërdrejtë në bazë përdoret vetëm për të lidhur produktin e importuar me kategorinë (përputhja e kategorisë ndodh sipas emrit të saj, gjatë përputhjes nxirret id e kategorisë):

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

Pas shtimit të lidhjes së produktit me kategoritë "Kategoria 1" dhe "Kategoria 2", detajet e produktit në panelin e administratorit duken kështu:

Magento 2: importimi i produkteve direkt në bazën e të dhënave

Veprime shtesë

Pas përfundimit të importit të të dhënave, duhet të kryhen veprime të tjera:

  • indeksimi i tĂ« dhĂ«nave: thirrja nĂ« konsolĂ« ./bin/magento indexer:reindex;
  • rregjenerimi i URL-ve pĂ«r produktet/kategoritĂ«: mund tĂ« pĂ«rdoret zgjatja "elgentos/regenerate-catalog-urls«

Produkte në panelin e administratorit pas kryerjes së veprimeve shtesë:

Magento 2: importimi i produkteve direkt në bazën e të dhënave

dhe në front:

Magento 2: importimi i produkteve direkt në bazën e të dhënave

CV

I njĂ«jtĂ« grup produktesh (10 copĂ«), siç ishte nĂ« artikullin e kaluar, importohet sĂ« paku njĂ« rend tĂ«rĂ« mĂ« shpejt (1 sekondĂ« kundrejt 10). PĂ«r njĂ« vlerĂ«sim mĂ« tĂ« saktĂ« tĂ« shpejtĂ«sisĂ« nevojiten mĂ« shumĂ« produkte — disa qindra, e mĂ« mirĂ« shumĂ« mijĂ«ra. MegjithatĂ«, madje edhe me kaq njĂ« numĂ«r tĂ« vogĂ«l tĂ« tĂ« dhĂ«nave hyrĂ«se, mund tĂ« arrihet nĂ« pĂ«rfundimin qĂ« pĂ«rdorimi i mjeteve tĂ« ofruara nga Magento (modelet dhe depozitat) ndihmon ndjeshĂ«m (theksoj — ndjeshĂ«m!) pĂ«r tĂ« pĂ«rshpejtuar zhvillimin e funksionalitetit tĂ« kĂ«rkuar, por njĂ«kohĂ«sisht ndihmojnĂ« ndjeshĂ«m (theksoj — ndjeshĂ«m!) nĂ« zvogĂ«limin e shpejtĂ«sisĂ« sĂ« futjes sĂ« tĂ« dhĂ«nave nĂ« bazĂ«.

Si përfundim, uji doli të ishte i lagësht dhe kjo nuk është ndonjë shpikje. Megjithatë, tani kam kodin për të vazhduar dhe ndoshta për të arritur përfundime më interesante.

Burimi: habr.com

Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS đŸ”„ Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS | ProHoster