Magento 2: محصولات را مستقیماً به پایگاه داده وارد کنید

В مقاله قبلی من روند واردات محصولات به مجنتو 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 - اگر آن را مشخص نکنیم، از "ساده" استفاده می شود

برای نوشتن مستقیم در پایگاه داده، از آداپتور 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 در پنل مدیریت، در شبکه محصول (کاتالوگ/محصولات).

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

با استفاده از کد ویژگی، شناسه و نوع داده آن را تعیین می کنیم (datetime, decimal, int, text, varchar، سپس داده های مربوط به پنجره مدیریت را در جدول مناسب بنویسید (store_id = 0).

پس از افزودن ویژگی های بالا به محصول، این تصویر را در پنل مدیریت دریافت می کنید:

Magento 2: محصولات را مستقیماً به پایگاه داده وارد کنید

داده های موجودی

با شروع از نسخه 2.3 در مجنتو، دو مجموعه جداول موازی وجود دارد که ذخیره اطلاعات موجودی (مقدار محصول) را فراهم می کند:

  • cataloginventory_*: سازه قدیمی;
  • inventory_*: ساختار جدید (MSI - Inventory Multi Source);

شما باید داده های موجودی را به هر دو ساختار اضافه کنید، زیرا ساختار جدید هنوز کاملاً مستقل از ساختار قدیمی نیست (به احتمال بسیار زیاد برای default انبار در ساختار جدید یک جدول درگیر است cataloginventory_stock_status مانند inventory_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

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_

در ابتدا، ساختار جدید برای ذخیره سازی داده های موجودی شامل 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).

یکی "انبار"می تواند به چندین پیوند مرتبط شود"منابع"و یکی "منبع"- به چندین"انبارها"(رابطه چند به چند). استثناها پیش فرض هستند "منبع"و"انبار". آنها مجدداً به نهادهای دیگر پیوند داده نمی شوند (محدودیت در سطح کد - خطا "نمی توان پیوند مربوط به منبع پیش فرض یا سهام پیش فرض را ذخیره کرد"). جزئیات بیشتر در مورد ساختار MSI در Magento 2 را می توانید در مقاله پیدا کنید.سیستم مدیریت انبار با استفاده از CQRS و Event Sourcing. طرح".

من از پیکربندی پیش فرض استفاده می کنم و تمام اطلاعات موجودی را به منبع اضافه می کنم 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);
}

پس از افزودن داده های موجودی به محصول در پنل مدیریت، این تصویر را دریافت می کنید:

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 ریشه کل کاتالوگ مجنتو است و نه در پنل مدیریت و نه در صفحه اول موجود نیست. دسته با id=2 (دسته پیش فرض) دسته اصلی برای فروشگاه اصلی سایت اصلی است (فروشگاه اصلی وب سایت) هنگام استقرار برنامه ایجاد می شود (نگاه کنید به. مدیر / فروشگاه ها / همه فروشگاه ها). علاوه بر این، دسته اصلی خود فروشگاه نیز در قسمت جلو موجود نیست، فقط زیر مجموعه های آن موجود است.

از آنجایی که موضوع این مقاله هنوز وارد کردن داده‌ها بر روی محصولات است، هنگام ایجاد دسته‌ها از ورود مستقیم به پایگاه داده استفاده نمی‌کنم، بلکه از کلاس‌های ارائه شده توسط خود Magento (مدل‌ها و مخازن) استفاده خواهم کرد. ورود مستقیم به پایگاه داده فقط برای مرتبط کردن محصول وارداتی با یک دسته استفاده می شود (دسته با نام آن مطابقت دارد و شناسه دسته در حین تطبیق بازیابی می شود):

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

پس از افزودن پیوند محصول به دسته‌های «دسته ۱» و «دسته ۲»، جزئیات محصول در پنل مدیریت چیزی شبیه به این است:

Magento 2: محصولات را مستقیماً به پایگاه داده وارد کنید

اقدامات اضافی

پس از تکمیل وارد کردن داده ها، باید مراحل اضافی زیر را انجام دهید:

  • نمایه سازی داده ها: در کنسول تماس بگیرید ./bin/magento indexer:reindex;
  • بازسازی URL برای محصولات/دسته ها: می توانید از پسوند " استفاده کنیدelgentos/regenerate-catalog-urls«

Продукты в админке после выполнения дополнительных действий:

Magento 2: محصولات را مستقیماً به پایگاه داده وارد کنید

و در جلو:

Magento 2: محصولات را مستقیماً به پایگاه داده وارد کنید

خلاصه

همان مجموعه محصولات (10 عدد) مانند مقاله قبلی حداقل یک مرتبه سریعتر وارد می شود (1 ثانیه در مقابل 10). برای تخمین دقیق‌تر سرعت، به تعداد بیشتری محصول نیاز دارید - چند صد یا بهتر است هزاران. با این حال، حتی با چنین اندازه کوچکی از داده های ورودی، می توان نتیجه گرفت که استفاده از ابزارهای ارائه شده توسط Magento (مدل ها و مخازن) قابل توجه است (تاکید می کنم - بسیار!) سرعت توسعه عملکرد مورد نیاز را افزایش دهید، اما در عین حال به طور قابل توجهی (تاکید می کنم - بسیار!) سرعت ورود داده ها به پایگاه داده را کاهش دهید.

در نتیجه آب خیس شد و این وحی نیست. با این حال، اکنون کدی برای بازی کردن دارم و شاید به نتایج جالب تری برسم.

منبع: www.habr.com