Magento 2:将产品直接导入数据库

В 以前的文章 我描述了以通常的方式(通过模型和存储库)将产品导入 Magento 2 的过程。 通常的方法数据处理速度很低。 我的笔记本电脑每秒大约生产一种产品。 在本文中,我考虑了另一种导入产品的方法 - 直接进入数据库,绕过标准的 Magento 2 机制(模型、工厂、存储库)。 导入产品的步骤顺序可以适应任何可以与 MySQL 一起使用的编程语言。

免责声明:Magento 有现成的功能 数据导入 而且,很可能,这对您来说就足够了。 但是,如果您需要对导入过程进行更完整的控制,而不仅限于为您所拥有的内容准备 CSV 文件,欢迎使用 cat。

Magento 2:将产品直接导入数据库

编写这两篇文章所产生的代码可以在 Magento 模块中查看“flancer32/mage2_ext_demo_import”。 以下是我为简化演示模块代码而遵循的一些限制:

  • 产品只被创建,不被更新。
  • 仓库一处
  • 仅导入类别名称,不导入其结构
  • 数据结构符合2.3版本

用于导入单个产品的 JSON:

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

进口主要阶段概述

  • 产品本身的注册
  • 产品和网站之间的连接
  • 基本产品属性 (EAV)
  • 库存数据(库存产品数量)
  • 媒体(图片)
  • 与目录类别的连接

产品注册

基本产品信息可以在 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`)
)

在产品注册表中创建条目所需的最少信息是:

  • attribute_set_id
  • sku

额外的:

  • type_id — 如果我们不指定它,那么将使用“simple”

为了直接写入数据库,我使用 Magento 本身的数据库适配器:

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

注册产品后 catalog_product_entity 它在管理面板、产品网格中可见(目录/产品).

Magento 2:将产品直接导入数据库

产品与网站的关系

产品与站点的关联决定了该产品将在哪些商店和陈列中出现在前台。

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:将产品直接导入数据库

产品基本属性

新注册的产品还没有名称或描述。 这一切都是通过 EAV 属性。 以下是产品在正面正确显示所需的基本产品属性列表:

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

像这样向产品添加单独的属性(省略从其代码获取属性的标识符和类型的详细信息):

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

使用属性代码,我们确定它的 id 和数据类型(datetime, decimal, int, text, varchar),然后将管理窗口的数据写入相应的表中(store_id = 0).

将上述属性添加到产品后,您将在管理面板中看到以下图片:

Magento 2:将产品直接导入数据库

库存数据

从 Magento 2.3 版本开始,有两组并行的表提供库存信息(产品数量)的存储:

  • cataloginventory_*:旧结构;
  • inventory_*:新结构(MSI - 多源库存);

您需要将库存数据添加到这两个结构中,因为新结构尚未完全独立于旧结构(很可能对于 default 新结构中的仓库涉及到一个表 cataloginventory_stock_statusinventory_stock_1).

目录库存_

部署 Magneto 2.3 时,我们最初有 2 个条目 store_website,对应两个站点 - 管理站点和主客户端:

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

桌子 cataloginventory_stock 我们只有一项:

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

也就是说,在我们的旧结构中只有一个“仓库”(stock)并链接到管理网站。 通过管理面板添加新的 sources/stocks MSI(新结构)中不会导致新条目 cataloginventory_stock.

旧结构中产品的库存数据最初记录在表中:

  • cataloginventory_stock_item
  • cataloginventory_stock_status

目录库存_库存_项目

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

目录库存_库存状态

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

存货_

最初,用于存储库存数据的新结构包含 1““(inventory_source):

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

和一个“仓库“(inventory_stock):

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

«» 代表产品的物理存储(记录包含物理坐标和邮寄地址)。 ”仓库“是几个“来源”的逻辑联合(inventory_source_stock_link)

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

在与销售渠道的连接发生的级别(inventory_stock_sales_channel)

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

从数据结构来看,假设了各种类型的销售渠道,但默认只有“连接”库存“-”官网”(网站链接遵循网站代码 - base).

一 ”仓库“可以链接到多个”来源“还有一个”“ - 几个”仓库“(多对多关系)。 例外情况是默认的““和”仓库”。 它们不会重新链接到其他实体(代码级别的限制 - 错误“无法保存与默认来源或默认库存相关的链接”)。 有关 Magento 2 中 MSI 结构的更多详细信息可以在文章“使用 CQRS 和事件溯源的仓库管理系统。 设计«。

我将使用默认配置并将所有库存信息添加到源中 default,涉及与该代码网站关联的销售渠道 base (对应商店前端-参见 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);
}

在管理面板中将库存数据添加到产品后,您会得到以下图片:

Magento 2:将产品直接导入数据库

媒体

当通过管理面板“手动”向产品添加图像时,相关信息记录在下表中:

  • catalog_product_entity_media_gallery:媒体注册表(图像和视频文件);
  • catalog_product_entity_media_gallery_value:将媒体与产品和展示联系起来(本地化);
  • catalog_product_entity_media_gallery_value_to_entity:仅将媒体链接到产品(大概是产品的默认媒体内容);
  • catalog_product_entity_varchar:此处存储该图片所使用的角色;

并且图像本身保存到目录中 ./pub/media/catalog/product/x/y/哪里 x и y — 图像文件名的第一个和第二个字母。 例如,文件 image.png 应另存为 ./pub/media/catalog/product/i/m/image.png,以便平台在描述目录中的产品时可以将其用作图像。

注册发布于 ./pub/media/catalog/product/ 媒体文件(本文不讨论放置文件本身的过程):

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

注册后,新媒体文件会被分配一个标识符。

我们将注册的媒体文件与默认店面的相应产品相关联:

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

我们将注册的媒体文件与相应的产品相关联,而不绑定到任何店面。 目前尚不清楚该数据的具体用途以及为什么无法访问前一个表中的数据,但该表存在,并且在将图片添加到产品时会将数据写入其中。 就是这样了。

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

一个媒体文件可以用于不同的角色(括号中表示相应的属性代码):

  • 根据(image)
  • 小图像(small_image)
  • 缩略图(thumbnail)
  • 样本图像(swatch_image)

将角色链接到媒体文件正是发生在 catalog_product_entity_varchar。 绑定代码类似于“产品基本属性«。

在管理面板中将图像添加到产品后,它看起来像这样:

Magento 2:将产品直接导入数据库

类别

包含按类别数据的主表:

  • catalog_category_entity:类别登记;
  • catalog_category_product:产品和类别之间的联系;
  • catalog_category_entity_*:EAV属性值;

最初,在空的 Magento 应用程序中,类别注册表包含 2 个类别(我缩短了列名称: 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|

id=1 的类别是整个 Magento 目录的根目录,在管理面板或首页中不可用。 id=2 的类别 (默认分类) 是主站点主商店的根类别 (主要网站商店)在部署应用程序时创建(请参阅。 管理/商店/所有商店)。 而且,商店本身的根类别也不能在前面显示,只有它的子类别。

由于本文的主题仍然是导入产品数据,因此在创建类别时我不会使用直接进入数据库的方式,而是使用Magento本身提供的类(模型和存储库)。 直接录入数据库仅用于将导入的产品与类别关联(类别通过名称进行匹配,匹配时检索类别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);
}

将产品链接添加到类别“类别 1”和“类别 2”后,管理面板中的产品详细信息如下所示:

Magento 2:将产品直接导入数据库

其他行动

数据导入完成后,您需要完成以下附加步骤:

  • 数据索引:在控制台调用 ./bin/magento indexer:reindex;
  • 重新生成产品/类别的 URL:您可以使用扩展名“elgentos/重新生成目录 url«

执行其他操作后管理面板中的产品:

Magento 2:将产品直接导入数据库

在前面:

Magento 2:将产品直接导入数据库

总结

与上一篇文章中相同的一组产品(10 件)的导入速度至少快一个数量级(1 秒对 10 件)。 为了更准确地估计速度,您需要更多数量的产品 - 数百个,甚至数千个。 然而,即使输入数据如此小,我们也可以得出结论,使用 Magento 提供的工具(模型和存储库)是很重要的(我强调 - !)加快所需功能的开发,但同时显着(我强调 - !)降低数据进入数据库的速度。

结果发现水是湿的,这不是一个启示。 然而,现在我有了可以使用的代码,也许可以得出一些更有趣的结论。

来源: habr.com