Magento 2: Importar produtos diretamente para o banco de dados

В artigo anterior Descrevi o processo de importação de produtos em Magento 2. O método usual — por meio de modelos e repositórios. O método usual caracteriza-se por uma velocidade de processamento de dados muito baixa. No meu laptop, ele gerava cerca de um produto por segundo. Nesta continuação, considero um método alternativo de importação de produtos — escrevendo diretamente no banco de dados, ignorando os mecanismos padrão. Magento 2 (modelos, fábricas, repositórios). A sequência de etapas para importar produtos pode ser adaptada a qualquer linguagem de programação que suporte MySQL.

Aviso Legal: V Magento existe funcionalidade pronta para uso importação de dados e, provavelmente, será o suficiente para você. Porém, se você precisa de um controle mais completo sobre o processo de importação, não se limitando a preparar um arquivo CSV para o que você possui, seja bem-vindo ao cat.

Magento 2: Importar produtos diretamente para o banco de dados

O código resultante para ambos os artigos pode ser visualizado em Magento-módulo "flancer32/mage2_ext_demo_import". Aqui estão algumas restrições que segui para simplificar o código do módulo de demonstração:

  • Os produtos são apenas criados, não atualizados.
  • Um armazém
  • Somente nomes de categorias são importados, sem sua estrutura
  • As estruturas de dados estão em conformidade com a versão 2.3

JSON para importar um único produto:

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

Visão geral das principais etapas da importação

  • registro do próprio produto
  • conexão entre produto e site
  • atributos básicos do produto (EAV)
  • dados de estoque (quantidade de produto em estoque)
  • mídia (fotos)
  • conexão com categorias de catálogo

Registo de produto

Informações básicas do produto podem ser encontradas em 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`)
)

As informações mínimas necessárias para criar uma entrada no cadastro do produto são:

  • attribute_set_id
  • sku

adicional:

  • type_id — se não especificarmos, então 'simples' será usado

Para escrever diretamente no banco de dados, eu uso o próprio adaptador de banco de dados. 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;
}

Depois de registrar o produto no catalog_product_entity fica visível no painel de administração, na grade de produtos (Catálogo/Produtos).

Magento 2: Importar produtos diretamente para o banco de dados

Relação entre produto e site

A associação do produto ao site determina em quais lojas e expositores o produto estará disponível na vitrine.

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: Importar produtos diretamente para o banco de dados

Atributos básicos do produto

O produto recém-registrado ainda não possui nome ou descrição. Tudo isso é feito através Atributos EAV. Aqui está uma lista de atributos básicos do produto que são necessários para que o produto seja exibido corretamente na frente:

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

Um atributo separado é adicionado a um produto como este (os detalhes para obter o identificador e o tipo do atributo de seu código são omitidos):

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

Usando o código do atributo, determinamos seu id e tipo de dados (datetime, decimal, int, text, varchar) e, em seguida, grave os dados da janela administrativa na tabela apropriada (store_id = 0).

Depois de adicionar os atributos acima ao produto, você obtém esta imagem no painel de administração:

Magento 2: Importar produtos diretamente para o banco de dados

Dados de inventário

Desde a versão 2.3 Magento Existem dois conjuntos de tabelas em paralelo que fornecem armazenamento de informações de estoque (quantidade de produtos):

  • cataloginventory_*: estrutura antiga;
  • inventory_*: nova estrutura (MSI – Multi Source Inventory);

Você precisa adicionar dados de inventário a ambas as estruturas, porque a nova estrutura ainda não é completamente independente da antiga (é muito provável que para default armazém na nova estrutura uma tabela está envolvida cataloginventory_stock_status como inventory_stock_1).

catálogoinventário_

Ao implantar o Magneto 2.3, inicialmente temos 2 entradas em store_website, que corresponde a dois sites - administrativo e cliente principal:

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

Mesa cataloginventory_stock temos apenas uma entrada:

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

Ou seja, na nossa estrutura antiga existe apenas um “armazém” (stock) e está vinculado ao site administrativo. Adicionando novos através do painel de administração sources/stocks no MSI (nova estrutura) não resulta em novas entradas no cataloginventory_stock.

Os dados de estoque de produtos na estrutura antiga são inicialmente registrados em tabelas:

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

inventário_

Inicialmente, a nova estrutura de armazenamento de dados de estoque contém 1 "fonte"(inventory_source):

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

e um "armazém"(inventory_stock):

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

«Fonte» representa o armazenamento físico dos produtos (o registro contém coordenadas físicas e endereço para correspondência). "Armazém"é uma união lógica de várias" fontes "(inventory_source_stock_link)

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

no nível em que ocorre a conexão com o canal de vendas (inventory_stock_sales_channel)

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

A julgar pela estrutura de dados, são assumidos vários tipos de canais de vendas, mas por padrão apenas a conexão “estoque"-"site do produto"(o link para o site segue o código do site - base).

Um "armazém"pode ​​ser vinculado a vários"fontes"e um "fonte" - para vários "armazéns"(relacionamento muitos-para-muitos). As exceções são padrão "fonte"E"armazém". Eles não são vinculados novamente a outras entidades (limitação no nível do código - o erro “Não é possível salvar o link relacionado à origem padrão ou ao estoque padrãoMais detalhes sobre a estrutura MSI em Magento 2 pode ser lido no artigo "Sistema de gerenciamento de armazém utilizando CQRS e Event Sourcing. Projeto".

Usarei a configuração padrão e adicionarei todas as informações de inventário à fonte default, que está envolvido no canal de vendas associado ao site com o código base (corresponde à fachada da loja - ver 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);
}

Depois de adicionar dados de inventário ao produto no painel de administração, você terá esta imagem:

Magento 2: Importar produtos diretamente para o banco de dados

mídia

Ao adicionar “manualmente” uma imagem a um produto através do painel de administração, as informações relevantes são anotadas nas seguintes tabelas:

  • catalog_product_entity_media_gallery: registro de mídia (arquivos de imagens e vídeos);
  • catalog_product_entity_media_gallery_value: vinculação de mídia a produtos e vitrines (localização);
  • catalog_product_entity_media_gallery_value_to_entity: vinculando mídia apenas a produtos (presumivelmente conteúdo de mídia padrão para o produto);
  • catalog_product_entity_varchar: As funções nas quais a imagem é usada são armazenadas aqui;

e as próprias imagens são salvas no diretório ./pub/media/catalog/product/x/y/Onde x и y — a primeira e a segunda letras do nome do arquivo de imagem. Por exemplo, arquivo image.png deve ser salvo como ./pub/media/catalog/product/i/m/image.png, para que a plataforma possa utilizá-lo como imagem na descrição dos produtos do catálogo.

Cadastro postado em ./pub/media/catalog/product/ arquivo de mídia (o processo de colocação do arquivo em si não é discutido neste artigo):

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

Quando registrado, um novo arquivo de mídia recebe um identificador.

Associamos o arquivo de mídia cadastrado ao produto correspondente da vitrine padrão:

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

Associamos o arquivo de mídia cadastrado ao produto correspondente sem estarmos vinculados a nenhuma vitrine. Não está claro onde exatamente esses dados são usados ​​e por que é impossível acessar os dados da tabela anterior, mas esta tabela existe e os dados são gravados nela quando uma imagem é adicionada ao produto. Então é isso.

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

Um arquivo de mídia pode ser usado com diferentes funções (o código do atributo correspondente é indicado entre parênteses):

  • Base(image)
  • Imagem pequena (small_image)
  • Miniatura (thumbnail)
  • Imagem de amostra (swatch_image)

Vincular funções a um arquivo de mídia é exatamente o que acontece em catalog_product_entity_varchar. O código de ligação é semelhante ao código no "Atributos básicos do produto".

Depois de adicionar uma imagem ao produto no painel de administração, ele ficará assim:

Magento 2: Importar produtos diretamente para o banco de dados

categoria

Principais tabelas contendo dados por categoria:

  • catalog_category_entity: cadastro de categorias;
  • catalog_category_product: conexão entre produtos e categorias;
  • catalog_category_entity_*: Valores de atributos EAV;

Inicialmente, no vazio Magento- O registro de categorias do aplicativo contém 2 categorias (abreviei os nomes das colunas: 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|

A categoria com id=1 é a raiz de tudo. Magento-catálogo e não está disponível nem no painel de administração nem na interface pública. Categoria com id=2 (Categoria Padrão) é a categoria raiz da loja principal do site principal (Loja principal do site) criado quando o aplicativo é implantado (consulte. Administrador / Lojas / Todas as lojas). Além disso, a categoria raiz da loja em si também não está disponível na frente, apenas suas subcategorias.

Como o tema deste artigo ainda é a importação de dados de produtos, não utilizarei a entrada direta no banco de dados ao criar as categorias, mas sim as classes fornecidas pelo sistema. Magento (modelos e repositórios). A escrita direta no banco de dados é usada apenas para associar um produto importado a uma categoria (a correspondência de categorias é feita pelo nome e o ID da categoria é obtido durante a correspondência):

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

Depois de adicionar um link de produto às categorias “Categoria 1” e “Categoria 2”, os detalhes do produto no painel de administração são mais ou menos assim:

Magento 2: Importar produtos diretamente para o banco de dados

Ações adicionais

Assim que a importação de dados for concluída, você precisará concluir as seguintes etapas adicionais:

  • indexação de dados: chamada no console ./bin/magento indexer:reindex;
  • regenerando URLs para produtos/categorias: você pode usar a extensão “elgentos/regenerate-catalog-urls«

Produtos no painel de administração após realizar ações adicionais:

Magento 2: Importar produtos diretamente para o banco de dados

e na frente:

Magento 2: Importar produtos diretamente para o banco de dados

Resumo

O mesmo conjunto de produtos (10 itens) do artigo anterior é importado pelo menos dez vezes mais rápido (1 segundo contra 10). Uma estimativa de velocidade mais precisa exigiria um número maior de produtos — centenas, ou melhor ainda, milhares. Mesmo assim, com um conjunto de dados de entrada tão pequeno, pode-se concluir que, utilizando as ferramentas disponíveis, é possível obter um desempenho bastante satisfatório. Magento (modelos e repositórios), significativamente (enfatizo - muito!) acelerar o desenvolvimento da funcionalidade necessária, mas ao mesmo tempo significativamente (enfatizo - muito!) reduz a velocidade com que os dados chegam ao banco de dados.

Como resultado, a água ficou molhada e isso não é uma revelação. No entanto, agora tenho o código para brincar e talvez chegar a algumas conclusões mais interessantes.

Fonte: habr.com

Compre hospedagem confiável para sites com proteção DDoS, servidores VPS VDS 🔥 Compre hospedagem de sites confiável com proteção contra DDoS, servidores VPS/VDS | ProHoster