В 私は製品の輸入プロセスについて説明しました Magento 2. 従来の方法 ― モデルとリポジトリを経由する方法。従来の方法は、データ処理速度が非常に遅いという特徴があります。私のノートパソコンでは、1秒間に約1つの製品しか生成できませんでした。この続きでは、標準的なメカニズムを迂回してデータベースに直接書き込むという、製品インポートの代替方法について検討します。 Magento 2(モデル、ファクトリ、リポジトリ)。製品をインポートする手順は、MySQLをサポートするあらゆるプログラミング言語に合わせて調整できます。
免責事項:V Magento 既製の機能があります そしておそらく、あなたにとってはそれで十分でしょう。 ただし、手持ちの CSV ファイルを準備するだけでなく、インポート プロセスをより完全に制御する必要がある場合は、cat へようこそ。

両記事の結果として得られたコードは以下で閲覧できます。 Magento-モジュール "」。 デモ モジュールのコードを簡素化するために私が従ったいくつかの制限を次に示します。
- 製品は作成されるだけであり、更新されません。
- XNUMXつの倉庫
- カテゴリ名のみがインポートされ、その構造はインポートされません。
- データ構造はバージョン 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"
}
輸入の主な段階の概要
- 製品自体の登録
- 製品とWebサイトのつながり
- 基本的な製品属性 (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_idsku
追加:
type_id— 指定しない場合は、「simple」が使用されます
データベースへの直接書き込みには、DBアダプタ自体を使用します。 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 管理パネルの製品グリッドに表示されます (カタログ・製品).

商品とWebサイトの関係
製品とサイトの関連付けにより、製品がどの店舗で販売され、店頭で展示されるかが決まります。
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);
}

基本的な製品特性
新しく登録された製品にはまだ名前も説明もありません。 これはすべて、 。 製品を前面に正しく表示するために必要な基本的な製品属性のリストを次に示します。
namepricedescriptionshort_descriptionstatustax_class_idurl_keyvisibility
次のように、別の属性がプロダクトに追加されます (コードから属性の識別子とタイプを取得する詳細は省略します)。
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).
上記の属性を製品に追加すると、管理パネルに次の画像が表示されます。

インベントリデータ
バージョン2.3以降 Magento 在庫情報(製品数量)を保存するためのテーブルが2セット並列に存在します。
cataloginventory_*:古い構造。inventory_*: 新しい構造 (MSI - マルチソースインベントリ);
両方の構造に在庫データを追加する必要があります。 新しい構造はまだ古い構造から完全に独立していません(おそらく、 default 新しい構造の倉庫にはテーブルが含まれます cataloginventory_stock_status として inventory_stock_1).
カタログ在庫_
Magneto 2.3 をデプロイする場合、最初は 2 つのエントリがあります。 store_website、これは XNUMX つのサイト (管理クライアントとメイン クライアント) に対応します。
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 エントリは XNUMX つだけです。
stock_id|website_id|stock_name|
--------|----------|----------|
1| 0|Default |
つまり、私たちの古い構造には「倉庫」が XNUMX つしかありません (stock)から管理サイトへリンクしております。 管理パネルから新しいものを追加する sources/stocks MSI (新しい構造) では、新しいエントリは生成されません。 cataloginventory_stock.
古い構造の製品に関する在庫データは、最初はテーブルに記録されます。
cataloginventory_stock_itemcataloginventory_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 |...|
そしてXNUMXつ」倉庫」(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|
データ構造から判断すると、さまざまな販売チャネルが想定されますが、デフォルトでは接続のみです。株式「-」ウェブサイト"(Web サイトへのリンクは Web サイトのコードに従います - base).
XNUMXつ "倉庫「複数にリンクできる」ソース「そして一つ」ソース「~数件まで」倉庫"(多対多の関係)。 例外はデフォルトです。」ソース"そして"倉庫」。 これらは他のエンティティに再リンクされません (コード レベルでの制限 - エラー「デフォルトのソースまたはデフォルトのストックに関連するリンクを保存できません)。MSI構造の詳細については、 Magento 2 は記事で読むことができます。"
デフォルトの構成を使用し、すべてのインベントリ情報をソースに追加します。 default、コードを持つ Web サイトに関連付けられた販売チャネルに関与しています 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);
}
管理パネルで製品に在庫データを追加すると、次の図が表示されます。

メディア
管理パネルを通じて製品に画像を「手動」で追加すると、関連情報が次の表に書き込まれます。
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 — 画像ファイル名の最初と XNUMX 文字目。 たとえば、ファイル 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);
}
カタログ_製品_エンティティ_varchar
メディア ファイルはさまざまな役割で使用できます (対応する属性コードが括弧内に示されています)。
- ベース(
image) - 小さい画像 (
small_image) - サムネイル (
thumbnail) - スウォッチ画像 (
swatch_image)
役割をメディア ファイルにリンクすると、まさに次のことが起こります。 catalog_product_entity_varchar。 バインディング コードは、「」のコードと似ています。基本的な製品特性"
管理パネルで製品に画像を追加すると、次のようになります。

Категории
カテゴリ別のデータを含む主なテーブル:
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」に製品リンクを追加すると、管理パネルの製品詳細は次のようになります。

追加のアクション
データのインポートが完了したら、次の追加手順を完了する必要があります。
- データのインデックス作成:コンソールでの呼び出し
./bin/magento indexer:reindex; - 製品/カテゴリの URL を再生成: 拡張子「」を使用できます。«
追加のアクションを実行した後の管理パネルの製品:

そして正面には:

まとめ
前回の記事と同じ製品セット(10品目)をインポートすると、少なくとも1桁速く(1秒対10秒)処理できます。より正確な速度推定には、数百、できれば数千といったより多くの製品数が必要です。しかし、入力データサイズがこれほど小さくても、提供されているツールを使用すれば、 Magento (モデルとリポジトリ)は、著しく(強調しておきますが、 はるかに!) 必要な機能の開発をスピードアップしますが、同時に大幅に (強調します - はるかに!) データがデータベースに入る速度を下げます。
その結果、水は濡れていることが判明しましたが、これは啓示ではありません。 しかし、今ではコードをいじってみると、もっと興味深い結論に達するかもしれません。
出所: habr.com
