في مقال سابق ، وصفت عملية استيراد المنتجات إلى Magento 2 بالطريقة المعتادة - من خلال النماذج والمستودعات. تتميز الطريقة المعتادة بسرعة منخفضة للغاية لمعالجة البيانات. حوالي منتج واحد في الثانية خرج على جهاز الكمبيوتر المحمول. في هذه المتابعة ، أفكر في طريقة بديلة لاستيراد منتج - عن طريق التسجيل المباشر في قاعدة البيانات ، وتجاوز الآليات القياسية لـ Magento 2 (النماذج والمصانع والمستودعات). يمكن تكييف تسلسل خطوات استيراد المنتجات مع أي لغة برمجة يمكن أن تعمل مع MySQL.
إخلاء المسئولية : لدى Magento وظيفة جاهزة لاستيراد البيانات ، وعلى الأرجح ، لديك ما يكفي منها. ومع ذلك ، إذا كنت بحاجة إلى مزيد من التحكم الكامل في عملية الاستيراد ، فلا تقتصر على إعداد ملف CSV لما هو عليه - مرحبًا بك في cat.

يمكن الاطلاع على الكود الناتج عن كتابة كلا المادتين في وحدة flancer32 / mage2_ext_demo_import Magento. فيما يلي بعض القيود التي اتبعتها لتبسيط رمز وحدة العرض التوضيحي:
- يتم إنشاء المنتجات فقط ، وليس تحديثها.
- مستودع واحد
- يتم استيراد أسماء الفئات فقط ، بدون هيكلها
- هياكل البيانات تتوافق مع الإصدار 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`) )
الحد الأدنى من المعلومات اللازمة لإنشاء إدخال في سجل المنتج:
إضافية:
type_id
- إذا لم type_id
ذلك ، فسيتم استخدام "بسيط"
للتسجيل المباشر في قاعدة البيانات ، يمكنني استخدام محول DB من Magento نفسه:
function create($sku, $typeId, $attrSetId) { $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
يصبح مرئيًا في لوحة المسؤول ، في شبكة المنتج ( Catalog / Products ).

ربط المنتج والموقع
تحدد علاقة المنتج بالموقع أي المتاجر وفي حالات العرض سيكون المنتج متاحًا في المقدمة.
function linkToWebsite($prodId, $websiteId) { $conn = $this->resource->getConnection(); $table = $this->resource->getTableName('catalog_product_website'); $bind = [ 'product_id' => $prodId, 'website_id' => $websiteId ]; $conn->insert($table, $bind); }

سمات المنتج الأساسية
المنتج المسجل حديثًا ليس له اسم أو وصف حتى الآن. كل هذا يتم من خلال سمات EAV . فيما يلي قائمة بسمات المنتج الأساسية اللازمة لضمان عرض المنتج بشكل صحيح في المقدمة:
name
price
description
short_description
status
tax_class_id
url_key
visibility
تتم إضافة سمة منفصلة للمنتج مثل هذا (تم حذف تفاصيل الحصول على المعرف ونوع السمة بواسطة الكود الخاص به):
public function create($prodId, $attrCode, $attrValue) { $attrId = $attrType = if ($attrId) { $conn = $this->resource->getConnection(); $tblName = 'catalog_product_entity_' . $attrType; $table = $this->resource->getTableName($tblName); $bind = [ 'attribute_id' => $attrId, 'entity_id' => $prodId, 'store_id' => 0, 'value' => $attrValue ]; $conn->insert($table, $bind); } }
باستخدام رمز السمة ، حدد المعرف ونوع البيانات (الوقت ، decimal
، int
، text
، varchar
) ، ثم في الجدول المقابل نكتب البيانات لواجهة المتجر الإدارية ( store_id = 0
).
بعد إضافة السمات أعلاه إلى المنتج ، نحصل على هذه الصورة في لوحة الإدارة:

بيانات المخزون
بدءًا من الإصدار 2.3 ، تحتوي Magento في وقت واحد على مجموعتين من الجداول التي توفر تخزين معلومات المخزون (كمية المنتج):
cataloginventory_*
: الهيكل القديم ؛inventory_*
: بنية جديدة (MSI - جرد متعدد المصادر) ؛
تحتاج إلى إضافة بيانات المخزون لكلا الهياكل ، لأن البنية الجديدة ليست مستقلة تمامًا عن الهيكل القديم (يبدو أن جدول cataloginventory_stock_status
كـ inventory_stock_1
للمستودع default
في الهيكل الجديد).
كتالوج
عند نشر 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 |
أي في هيكلنا القديم يوجد "مستودع" واحد فقط (مرتبط) وهو مرتبط بالموقع الإداري. لا تؤدي إضافة sources
/ stocks
جديدة إلى MSI من خلال stocks
المسؤول (بنية جديدة) إلى إدخالات جديدة في cataloginventory_stock
.
تتم كتابة بيانات المخزون على المنتجات في الهيكل القديم في الجداول في البداية:
cataloginventory_stock_item
cataloginventory_stock_status
cataloginventory_stock_item
function createOldItem($prodId, $qty) { $isQtyDecimal = (((int)$qty) != $qty); $isInStock = ($qty > 0); $conn = $this->resource->getConnection(); $table = $this->resource->getTableName('cataloginventory_stock_item'); $bind = [ 'product_id' => $prodId, 'stock_id' => 1, 'qty' => $qty, 'is_qty_decimal' => $isQtyDecimal, 'is_in_stock' => $isInStock, 'website_id' => 0 ]; $conn->insert($table, $bind); }
Cataloginventory_stock_status
function createOldStatus($prodId, $qty) { $isInStock = ($qty > 0); $conn = $this->resource->getConnection(); $table = $this->resource->getTableName('cataloginventory_stock_status'); $bind = [ 'product_id' => $prodId, 'stock_id' => 1, 'qty' => $qty, 'stock_status' => \Magento\CatalogInventory\Api\Data\StockStatusInterface::STATUS_IN_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
رمز موقع الويب).
يمكن ربط " مستودع " واحد بعدة " مصادر " ، ويمكن ربط " مصدر " واحد بعدة " مستودعات " (علاقة كثير إلى كثير). الاستثناءات هي default'ovye " المصدر " و " المستودع ". لا ترتبط بالكيانات الأخرى (التقييد على مستوى الكود - الخطأ " يتعذر حفظ الرابط المتعلق بالمصادر الافتراضية أو الأسهم الافتراضية "). يمكنك قراءة المزيد حول بنية MSI في Magento 2 في مقالة " نظام إدارة المستودعات باستخدام CQRS ومصادر الأحداث. التصميم ".
سأستخدم التكوين الافتراضي وأضيف جميع معلومات المخزون إلى المصدر default
، والذي يتم استخدامه في قناة المبيعات المرتبطة بموقع الويب base
(يتوافق مع جزء العميل من المتجر - راجع store_website
):
function createNewItem($sku, $qty) { $conn = $this->resource->getConnection(); $table = $this->resource->getTableName('inventory_source_item'); $bind = [ 'source_code' => 'default', 'sku' => $sku, 'quantity' => $qty, 'status' => \Magento\InventoryApi\Api\Data\SourceItemInterface::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
الأحرف الأولى والثانية من اسم ملف الصورة. على سبيل المثال ، يجب حفظ ملف ./pub/media/catalog/product/i/m/image.png
حتى يتمكن النظام الأساسي من استخدامه كصورة عند وصف المنتجات من الكتالوج.
نحن نسجل ملف الوسائط الموجود في ./pub/media/catalog/product/
(لا تعتبر عملية وضع الملف في هذه المقالة):
function createMediaGallery($imgPathPrefixed) { $attrId = $conn = $this->resource->getConnection(); $table = $this->resource->getTableName('catalog_product_entity_media_gallery'); $bind = [ 'attribute_id' => $attrId, 'value' => $imgPathPrefixed, 'media_type' => 'image', 'disabled' => false ]; $conn->insert($table, $bind); $result = $conn->lastInsertId($table); return $result; }
عند التسجيل ، يتم تعيين ملف وسائط جديد معرف.
نربط ملف الوسائط المسجلة بالمنتج المقابل للعرض الافتراضي:
function createGalleryValue($mediaId, $prodId) { $conn = $this->resource->getConnection(); $table = $this->resource->getTableName('catalog_product_entity_media_gallery_value'); $bind = [ 'value_id' => $mediaId, 'store_id' => 0, 'entity_id' => $prodId, 'label' => null, 'position' => 1, 'disabled' => false ]; $conn->insert($table, $bind); }
نحن نربط ملف الوسائط المسجل بالمنتج المقابل دون الرجوع إلى أي واجهة. ليس من الواضح أين يتم استخدام هذه البيانات بالضبط ولماذا يستحيل الوصول إلى بيانات الجدول السابق ، ولكن هذا الجدول موجود ويتم كتابة البيانات عند إضافة صورة إلى المنتج. لذلك ، مثل هذا.
function createGalleryValueToEntity($mediaId, $prodId) { $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
) - صورة صغيرة (صورة صغيرة)
- صورة مصغرة (صورة
thumbnail
) - حامل الصورة (
swatch_image
)
يحدث ربط الأدوار بملف وسائط فقط في catalog_product_entity_varchar
. يشبه رمز الربط الكود الموجود في قسم " سمات المنتج الأساسية ".
بعد إضافة الصورة إلى المنتج في لوحة المسؤول ، اتضح كما يلي:

الفئات
الجداول الرئيسية التي تحتوي على بيانات حسب الفئة:
catalog_category_entity
: سجل الفئات ؛catalog_category_product
: رابطة المنتجات والفئات ؛catalog_category_entity_*
: قيم سمات EAV ؛
في البداية ، في تطبيق Magento فارغ ، يحتوي سجل الفئات على فئتين (قمت باختصار أسماء الأعمدة: 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|
الفئة ذات المعرف = 1 هي جذر دليل Magento بأكمله ولا تتوفر في لوحة الإدارة أو في المقدمة. الفئة ذات المعرف = 2 ( الفئة الافتراضية ) هي فئة الجذر للمتجر الرئيسي للموقع الرئيسي ( متجر الموقع الرئيسي ) الذي تم إنشاؤه عند نشر التطبيق (انظر المسؤول / المتاجر / جميع المتاجر ). علاوة على ذلك ، فإن فئة الجذر الخاصة بالمخزن في المقدمة غير متاحة أيضًا ، فقط فئاتها الفرعية.
نظرًا لأن موضوع هذه المقالة لا يزال يستورد بيانات المنتج ، فلن أستخدم التسجيل المباشر في قاعدة البيانات عند إنشاء الفئات ، لكنني سأستخدم الفئات التي توفرها Magento نفسها (النماذج والمستودعات). يستخدم التسجيل المباشر في قاعدة البيانات فقط لربط المنتج الذي تم استيراده بالفئة (يتم تعيين الفئة حسب اسمه ، ويتم استخراج معرف الفئة عند المطابقة):
function create($prodId, $catId) { $conn = $this->resource->getConnection(); $table = $this->resource->getTableName('catalog_category_product'); $bind = [ 'category_id' => $catId, 'product_id' => $prodId, ]; $conn->insert($table, $bind); }
بعد إضافة رابط المنتج إلى الفئتين "الفئة 1" و "الفئة 2" ، تبدو تفاصيل المنتج في لوحة المسؤول مثل هذا:

إجراءات إضافية
بعد اكتمال عملية استيراد البيانات ، يلزمك تنفيذ الخطوات الإضافية التالية:
المنتجات في لوحة المسؤول بعد الانتهاء من الخطوات الإضافية:

وفي المقدمة:

ملخص
يتم استيراد نفس مجموعة المنتجات (10 قطع) كما في المقالة السابقة بترتيب من حجمها على الأقل أسرع (ثانية واحدة مقابل 10). للحصول على تقدير أكثر دقة للسرعة ، تحتاج إلى عدد أكبر من المنتجات - عدة مئات ، ويفضل أن يكون الآلاف. ومع ذلك ، حتى مع وجود مثل هذه الكمية الصغيرة من بيانات المدخلات ، يمكن أن نستنتج أن استخدام الأدوات التي توفرها Magento (النماذج والمستودعات) بشكل كبير (التأكيد - بشكل كبير !) تسريع تطوير الوظيفة المطلوبة ، ولكن بشكل كبير (التأكيد - بشكل كبير !) تقليل سرعة الحصول على البيانات في قاعدة البيانات.
نتيجة لذلك ، تبين أن الماء رطب وهذا ليس الوحي. ومع ذلك ، لدي الآن رمز للعب عليه وربما استخلص استنتاجات أكثر إثارة للاهتمام.