Magento 2: mengimport produk terus ke dalam pangkalan data

Π’ artikel sebelumnya Saya menerangkan proses mengimport produk ke dalam Magento 2 dengan cara biasa - melalui model dan repositori. Kaedah biasa mempunyai kelajuan pemprosesan data yang sangat rendah. Komputer riba saya menghasilkan kira-kira satu produk sesaat. Dalam kesinambungan ini, saya mempertimbangkan cara alternatif untuk mengimport produk - dengan kemasukan terus ke dalam pangkalan data, memintas mekanisme standard Magento 2 (model, kilang, repositori). Urutan langkah untuk mengimport produk boleh disesuaikan dengan mana-mana bahasa pengaturcaraan yang boleh berfungsi dengan MySQL.

Penafian: Magento mempunyai fungsi sedia untuk import data dan, kemungkinan besar, ia akan mencukupi untuk anda. Walau bagaimanapun, jika anda memerlukan kawalan yang lebih lengkap ke atas proses import, tidak terhad kepada menyediakan fail CSV untuk apa yang anda miliki, selamat datang ke cat.

Magento 2: mengimport produk terus ke dalam pangkalan data

Kod yang terhasil daripada penulisan kedua-dua artikel boleh dilihat dalam modul Magento β€œflancer32/mage2_ext_demo_import". Berikut ialah beberapa sekatan yang saya ikuti untuk memudahkan kod modul demo:

  • Produk hanya dibuat, tidak dikemas kini.
  • Satu gudang
  • Hanya nama kategori yang diimport, tanpa strukturnya
  • Struktur data mematuhi versi 2.3

JSON untuk mengimport satu produk:

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

Gambaran keseluruhan peringkat utama import

  • pendaftaran produk itu sendiri
  • hubungan antara produk dan laman web
  • atribut produk asas (EAV)
  • data inventori (kuantiti produk dalam stok)
  • media (gambar)
  • sambungan dengan kategori katalog

Pendaftaran Produk

Maklumat produk asas boleh didapati di 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`)
)

Maklumat minimum yang diperlukan untuk membuat entri dalam daftar produk ialah:

  • attribute_set_id
  • sku

tambahan:

  • type_id β€” jika kami tidak menyatakannya, maka 'mudah' akan digunakan

Untuk menulis terus ke pangkalan data, saya menggunakan penyesuai DB Magento itu sendiri:

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

Selepas mendaftar produk dengan catalog_product_entity ia menjadi kelihatan dalam panel pentadbir, dalam grid produk (Katalog/Produk).

Magento 2: mengimport produk terus ke dalam pangkalan data

Hubungan antara produk dan laman web

Perkaitan produk dengan tapak menentukan tempat menyimpan dan memaparkan produk akan tersedia di hadapan.

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: mengimport produk terus ke dalam pangkalan data

Atribut produk asas

Produk yang baru didaftarkan belum mempunyai nama atau keterangan. Semua ini dilakukan melalui Atribut EAV. Berikut ialah senarai atribut produk asas yang diperlukan untuk produk dipaparkan dengan betul di bahagian hadapan:

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

Atribut yang berasingan ditambahkan pada produk seperti ini (butiran mendapatkan pengecam dan jenis atribut daripada kodnya ditinggalkan):

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

Menggunakan kod atribut, kami menentukan id dan jenis datanya (datetime, decimal, int, text, varchar), kemudian tulis data untuk tetingkap pentadbiran ke dalam jadual yang sesuai (store_id = 0).

Selepas menambah atribut di atas pada produk, anda mendapat gambar ini dalam panel pentadbir:

Magento 2: mengimport produk terus ke dalam pangkalan data

Data inventori

Bermula dari versi 2.3 dalam Magento, terdapat dua set jadual selari yang menyediakan penyimpanan maklumat inventori (kuantiti produk):

  • cataloginventory_*: struktur lama;
  • inventory_*: struktur baharu (MSI - Inventori Pelbagai Sumber);

Anda perlu menambah data inventori pada kedua-dua struktur, kerana struktur baru belum sepenuhnya bebas daripada yang lama (kemungkinan besar untuk default gudang dalam struktur baru meja terlibat cataloginventory_stock_status sebagai inventory_stock_1).

kataloginventori_

Apabila menggunakan Magneto 2.3 kami pada mulanya mempunyai 2 entri store_website, yang sepadan dengan dua tapak - pelanggan pentadbiran dan utama:

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

Jadual cataloginventory_stock kami hanya ada satu penyertaan:

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

Iaitu, dalam struktur lama kami hanya terdapat satu "gudang" (stock) dan ia dipautkan ke laman web pentadbiran. Menambah yang baharu melalui panel pentadbir sources/stocks dalam MSI (struktur baharu) tidak menghasilkan entri baharu dalam cataloginventory_stock.

Data inventori tentang produk dalam struktur lama pada mulanya direkodkan dalam jadual:

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

inventori_

Pada mulanya, struktur baharu untuk menyimpan data inventori mengandungi 1 "sumber"(inventory_source):

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

dan satu "gudang"(inventory_stock):

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

Β«SourceΒ» mewakili storan fizikal untuk produk (rekod mengandungi koordinat fizikal dan alamat surat-menyurat). "Gudang"adalah kesatuan logik beberapa "sumber" (inventory_source_stock_link)

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

pada tahap di mana sambungan kepada saluran jualan berlaku (inventory_stock_sales_channel)

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

Berdasarkan struktur data, pelbagai jenis saluran jualan diandaikan, tetapi secara lalai hanya sambungan "stok"-"laman web"(pautan ke tapak web mengikut kod tapak web - base).

satu"gudang"boleh dikaitkan dengan beberapa"sumber"dan satu "sumber"- kepada beberapa"gudangβ€œ(hubungan ramai-ke-banyak). Pengecualian adalah lalai "sumber"Dan"gudang". Mereka tidak dipautkan semula kepada entiti lain (had pada tahap kod - ralat β€œTidak dapat menyimpan pautan yang berkaitan dengan Sumber Lalai atau Stok Lalai"). Butiran lanjut tentang struktur MSI dalam Magento 2 boleh didapati dalam artikel β€œSistem pengurusan gudang menggunakan CQRS dan Event Sourcing. Reka bentuk".

Saya akan menggunakan konfigurasi lalai dan menambah semua maklumat inventori kepada sumber default, yang terlibat dalam saluran jualan yang dikaitkan dengan tapak web dengan kod base (sepadan dengan bahagian hadapan kedai - lihat 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);
}

Selepas menambah data inventori pada produk dalam panel pentadbir, anda mendapat gambar ini:

Magento 2: mengimport produk terus ke dalam pangkalan data

Media

Apabila "secara manual" menambahkan imej pada produk melalui panel pentadbir, maklumat yang berkaitan ditulis dalam jadual berikut:

  • catalog_product_entity_media_gallery: pendaftaran media (imej dan fail video);
  • catalog_product_entity_media_gallery_value: menghubungkan media kepada produk dan pameran (penyetempatan);
  • catalog_product_entity_media_gallery_value_to_entity: memautkan media kepada produk sahaja (mungkin kandungan media lalai untuk produk);
  • catalog_product_entity_varchar: Peranan di mana imej digunakan disimpan di sini;

dan imej itu sendiri disimpan ke direktori ./pub/media/catalog/product/x/y/Jika x ΠΈ y β€” huruf pertama dan kedua nama fail imej. Sebagai contoh, fail image.png harus disimpan sebagai ./pub/media/catalog/product/i/m/image.png, supaya platform boleh menggunakannya sebagai imej apabila menerangkan produk daripada katalog.

Daftar disiarkan dalam ./pub/media/catalog/product/ fail media (proses meletakkan fail itu sendiri tidak dibincangkan dalam artikel ini):

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

Apabila didaftarkan, fail media baharu diberikan pengecam.

Kami mengaitkan fail media berdaftar dengan produk yang sepadan untuk etalase lalai:

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

Kami mengaitkan fail media berdaftar dengan produk yang sepadan tanpa terikat dengan mana-mana etalase. Tidak jelas di mana tepatnya data ini digunakan dan mengapa adalah mustahil untuk mengakses data daripada jadual sebelumnya, tetapi jadual ini wujud dan data ditulis kepadanya apabila gambar ditambahkan pada produk. Jadi itu sahaja.

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

Fail media boleh digunakan dengan peranan yang berbeza (kod atribut yang sepadan ditunjukkan dalam kurungan):

  • asas(image)
  • Imej Kecil (small_image)
  • Gambar kecil (thumbnail)
  • Imej Swatch (swatch_image)

Memautkan peranan kepada fail media ialah perkara yang berlaku di dalamnya catalog_product_entity_varchar. Kod pengikatan adalah serupa dengan kod dalam "Atribut produk asas".

Selepas menambah imej pada produk dalam panel pentadbir, ia kelihatan seperti ini:

Magento 2: mengimport produk terus ke dalam pangkalan data

ΠšΠ°Ρ‚Π΅Π³ΠΎΡ€ΠΈΠΈ

Jadual utama yang mengandungi data mengikut kategori:

  • catalog_category_entity: daftar kategori;
  • catalog_category_product: sambungan antara produk dan kategori;
  • catalog_category_entity_*: nilai atribut EAV;

Pada mulanya, dalam aplikasi Magento kosong, pendaftaran kategori mengandungi 2 kategori (saya memendekkan nama lajur: 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|

Kategori dengan id=1 ialah punca keseluruhan katalog Magento dan tidak tersedia sama ada dalam panel pentadbir atau di halaman depan. Kategori dengan id=2 (Kategori Lalai) ialah kategori akar untuk stor utama tapak utama (Kedai Laman Web Utama) dicipta apabila aplikasi digunakan (lihat. Pentadbir / Kedai / Semua Kedai). Selain itu, kategori akar kedai itu sendiri juga tidak tersedia di bahagian depan, hanya subkategorinya.

Memandangkan topik artikel ini masih mengimport data pada produk, saya tidak akan menggunakan kemasukan terus ke dalam pangkalan data semasa membuat kategori, tetapi akan menggunakan kelas yang disediakan oleh Magento sendiri (model dan repositori). Kemasukan terus ke dalam pangkalan data hanya digunakan untuk mengaitkan produk yang diimport dengan kategori (kategori dipadankan dengan namanya dan id kategori diambil semasa pemadanan):

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

Selepas menambah pautan produk pada kategori "Kategori 1" dan "Kategori 2", butiran produk dalam panel pentadbir kelihatan seperti ini:

Magento 2: mengimport produk terus ke dalam pangkalan data

Tindakan tambahan

Setelah import data selesai, anda perlu melengkapkan langkah tambahan berikut:

  • pengindeksan data: panggil dalam konsol ./bin/magento indexer:reindex;
  • menjana semula URL untuk produk/kategori: anda boleh menggunakan sambungan β€œelgentos/regenerate-catalog-urlsΒ«

Produk dalam panel pentadbir selepas melakukan tindakan tambahan:

Magento 2: mengimport produk terus ke dalam pangkalan data

dan di hadapan:

Magento 2: mengimport produk terus ke dalam pangkalan data

Ringkasan

Set produk yang sama (10 keping) seperti dalam artikel sebelumnya diimport sekurang-kurangnya urutan magnitud lebih cepat (1 saat berbanding 10). Untuk menganggarkan kelajuan dengan lebih tepat, anda memerlukan bilangan produk yang lebih besar - beberapa ratus, atau lebih baik lagi beribu-ribu. Walau bagaimanapun, walaupun dengan saiz data input yang begitu kecil, kita boleh membuat kesimpulan bahawa penggunaan alat yang disediakan oleh Magento (model dan repositori) adalah penting (saya menekankan - banyak!) mempercepatkan pembangunan fungsi yang diperlukan, tetapi pada masa yang sama dengan ketara (saya menekankan - banyak!) mengurangkan kelajuan data masuk ke dalam pangkalan data.

Akibatnya, air itu ternyata basah dan ini bukanlah satu pendedahan. Walau bagaimanapun, kini saya mempunyai kod untuk bermain dan mungkin membuat beberapa kesimpulan yang lebih menarik.

Sumber: www.habr.com

Tambah komen