Магенто 2: увозите производе директно у базу података

В претходни чланак Процес увоза производа у Магенто 2 описао сам на уобичајен начин – кроз моделе и репозиторије. Уобичајени метод има веома малу брзину обраде података. Мој лаптоп је производио око један производ у секунди. У овом наставку разматрам алтернативни начин увоза производа – директним уласком у базу података, заобилазећи стандардне Магенто 2 механизме (модели, фабрике, ризнице). Редослед корака за увоз производа може се прилагодити било ком програмском језику који може да ради са МиСКЛ.

Одрицање од одговорности: Магенто има готову функционалност за увоз података и, највероватније, биће вам довољно. Међутим, ако вам је потребна потпунија контрола над процесом увоза, не ограничавајући се на припрему ЦСВ датотеке за оно што имате, добродошли у цат.

Магенто 2: увозите производе директно у базу података

Код који је резултат писања оба чланка може се видети у Магенто модулу "фланцер32/маге2_ект_демо_импорт„. Ево неких ограничења која сам следио да бих поједноставио код демо модула:

  • Производи се само креирају, не ажурирају.
  • Једно складиште
  • Увозе се само називи категорија, без њихове структуре
  • Структуре података су у складу са верзијом 2.3

ЈСОН за увоз једног производа:

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

Преглед главних фаза увоза

  • регистрација самог производа
  • везу између производа и веб странице
  • основни атрибути производа (ЕАВ)
  • подаци о залихама (количина производа на залихама)
  • медији (слике)
  • повезаност са каталошким категоријама

Регистрацију производа

Основне информације о производу могу се наћи у 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 — ако га не наведемо, користиће се „једноставно“.

Да пишем директно у базу података, користим ДБ адаптер самог Магента:

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 постаје видљив у админ панелу, у мрежи производа (Каталог/Производи).

Магенто 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);
}

Магенто 2: увозите производе директно у базу података

Основни атрибути производа

Новорегистровани производ још нема назив ни опис. Све ово се ради кроз ЕАВ атрибути. Ево листе основних атрибута производа који су потребни да би производ био исправно приказан на предњој страни:

  • 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).

Након додавања горњих атрибута производу, добијате ову слику у админ панелу:

Магенто 2: увозите производе директно у базу података

Подаци о инвентару

Почевши од верзије 2.3 у Магенто-у, постоје два паралелна скупа табела које обезбеђују складиштење информација о залихама (количина производа):

  • cataloginventory_*: стара структура;
  • inventory_*: нова структура (МСИ - Мулти Соурце Инвентори);

Потребно је да додате податке о залихама у обе структуре, јер нова структура још није потпуно независна од старе (врло је вероватно да за default магацин у новој структури укључен је сто cataloginventory_stock_status као inventory_stock_1).

цаталогинвентори_

Када примењујемо Магнето 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 у МСИ (нова структура) не резултира новим уносима у 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).

Један "магацин"може бити повезан са неколико"до извора"и један "извор"- на неколико"складишта„(однос „много-према-више“). Изузеци су подразумевани "извор"И"магацин„. Они нису поново повезани са другим ентитетима (ограничење на нивоу кода - грешка “Не могу да сачувам везу која се односи на подразумевани извор или подразумевану залиху"). Више детаља о МСИ структури у Магенто 2 можете пронаћи у чланку “Систем управљања складиштем користећи ЦКРС и Евент Соурцинг. Дизајн".

Користићу подразумевану конфигурацију и додати све информације о инвентару извору 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);
}

Након додавања података о инвентару производу у админ панелу, добијате ову слику:

Магенто 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);
}

цаталог_продуцт_ентити_варцхар

Медија датотека се може користити са различитим улогама (одговарајући код атрибута је наведен у заградама):

  • База (image)
  • Мала слика (small_image)
  • сличица (thumbnail)
  • Узорак слике (swatch_image)

Повезивање улога са медијском датотеком је управо оно што се дешава у catalog_product_entity_varchar. Код за везивање је сличан коду у „Основни атрибути производа".

Након додавања слике производу у админ панел, то изгледа овако:

Магенто 2: увозите производе директно у базу података

Категории

Главне табеле које садрже податке по категоријама:

  • catalog_category_entity: регистар категорија;
  • catalog_category_product: повезаност производа и категорија;
  • catalog_category_entity_*: ЕАВ вредности атрибута;

У почетку, у празној Магенто апликацији, регистар категорија садржи 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|

Категорија са ид=1 је корен целог Магенто каталога и није доступна ни у админ панелу ни на насловној страни. Категорија са ид=2 (Подразумевана категорија) је основна категорија за главну продавницу главног сајта (Главна веб локација продавница) креиран када се апликација примени (погледајте. Администратор / Продавнице / Све продавнице). Штавише, основна категорија саме продавнице такође није доступна на предњој страни, већ само њене поткатегорије.

Пошто је тема овог чланка и даље увоз података о производима, нећу користити директан улазак у базу података приликом креирања категорија, већ ћу користити класе које обезбеђује сам Магенто (модели и репозиторијуми). Директан улазак у базу података се користи само за повезивање увезеног производа са категоријом (категорија се поклапа са њеним именом, а ИД категорије се преузима током подударања):

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“, детаљи о производу на административној табли изгледају отприлике овако:

Магенто 2: увозите производе директно у базу података

Додатне акције

Када се увоз података заврши, потребно је да довршите следеће додатне кораке:

  • индексирање података: позив у конзоли ./bin/magento indexer:reindex;
  • регенерисање УРЛ-ова за производе/категорије: можете користити екстензију “елгентос/регенерате-цаталог-урлс«

Производи у админ панелу након обављања додатних радњи:

Магенто 2: увозите производе директно у базу података

и на предњој страни:

Магенто 2: увозите производе директно у базу података

Резиме

Исти сет производа (10 комада) као у претходном чланку се увози бар за ред величине брже (1 секунда наспрам 10). Да бисте прецизније проценили брзину, потребан вам је већи број производа - неколико стотина, или још боље хиљаде. Међутим, чак и са тако малом величином улазних података, можемо закључити да је употреба алата које пружа Магенто (модели и репозиторијуми) значајна (наглашавам - много!) убрзавају развој потребне функционалности, али у исто време значајно (наглашавам - много!) смањити брзину којом подаци улазе у базу података.

Као резултат тога, вода се показала мокра и то није откриће. Међутим, сада имам код да се поиграм и можда дођем до неких занимљивијих закључака.

Извор: ввв.хабр.цом