Em um artigo anterior, descrevi o processo de importação de produtos para o Magento 2 da maneira usual - através de modelos e repositórios. O método usual é caracterizado por uma velocidade de processamento de dados muito baixa. Cerca de um produto por segundo saiu no meu laptop. Nesta continuação, considero uma maneira alternativa de importar um produto - gravando diretamente no banco de dados, ignorando os mecanismos padrão do 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 possa funcionar com o MySQL.
Isenção de responsabilidade : O Magento possui uma funcionalidade pronta para importar dados e, provavelmente, você possui o suficiente. No entanto, se você precisar de um controle mais completo sobre o processo de importação, não se limite a preparar um arquivo CSV para o que é - bem-vindo ao gato.

O código resultante da escrita dos dois artigos pode ser visualizado no módulo Magento flancer32 / mage2_ext_demo_import . Aqui estão algumas das restrições que eu segui para simplificar o código do módulo de demonstração:
- Os produtos estão sendo criados apenas, 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 de importação
- registro do produto
- link do produto e site
- atributos básicos do produto (EAV)
- dados de inventário (quantidade de produto em estoque)
- mídia (fotos)
- link para categorias de catálogo
Registro de Produto
As informações básicas do produto estão 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`) )
Informações mínimas necessárias para criar uma entrada no registro do produto:
adicionais:
type_id
- se não for type_id
, será usado 'simple'
Para gravação direta no banco de dados, eu uso o adaptador DB do próprio 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; }
Após registrar um produto em catalog_product_entity
ele fica visível no painel de administração, na grade do produto ( Catálogo / Produtos ).

Link do produto e site
O relacionamento do produto com o site determina em quais lojas e em quais casos de exibição o produto estará disponível na frente.
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); }

Atributos básicos do produto
Um produto recém-registrado ainda não possui um nome ou descrição. Tudo isso é feito através dos atributos do EAV . Aqui está uma lista dos atributos básicos do produto que são necessários para garantir que o produto seja exibido corretamente na frente:
name
price
description
short_description
status
tax_class_id
url_key
visibility
Um atributo separado para o produto é adicionado assim (os detalhes de obtenção do identificador e do tipo de atributo por seu código são omitidos):
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); } }
Usando o código do atributo, determine seu ID e tipo de dados ( datetime
, decimal
, int
, text
, varchar
) e, na tabela correspondente, store_id = 0
os dados para a montra administrativa ( store_id = 0
).
Depois de adicionar os atributos acima ao produto, obtemos esta imagem no painel de administração:

Dados de inventário
A partir da versão 2.3, o Magento possui simultaneamente dois conjuntos de tabelas que fornecem armazenamento de informações de inventário (quantidade do produto):
cataloginventory_*
: estrutura antiga;inventory_*
: nova estrutura (MSI - Multi Source Inventory);
Você precisa adicionar dados de inventário às duas estruturas, porque a nova estrutura ainda não é completamente independente da antiga (parece que a tabela cataloginventory_stock_status
é cataloginventory_stock_status
como inventory_stock_1
para o armazém default
na nova estrutura).
cataloginventory_
Ao implantar o Magneto 2.3, inicialmente temos 2 entradas no store_website
, o que corresponde a dois sites - o administrativo e o 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|
Na tabela cataloginventory_stock
, temos apenas uma entrada:
stock_id|website_id|stock_name| --------|----------|----------| 1| 0|Default |
Ou seja, em nossa estrutura antiga, existe apenas um "depósito" ( stock
) e está vinculado ao site administrativo. Adicionar novas sources
/ stocks
ao MSI através do stocks
administração (nova estrutura) não leva a novas entradas no cataloginventory_stock
.
Os dados de inventário dos produtos na estrutura antiga são inicialmente escritos nas tabelas:
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); }
inventário_
Inicialmente, a nova estrutura para armazenar dados de inventário contém 1 " origem " ( 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|
Uma " fonte " é uma loja física de produtos (o registro contém coordenadas físicas e um endereço para correspondência). Um " 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 existe um link para o canal de vendas ( inventory_stock_sales_channel
)
type |code|stock_id| -------|----|--------| website|base| 1|
A julgar pela estrutura de dados, vários tipos de canais de vendas são assumidos, mas, por padrão, apenas a conexão " estoque " - " site " é usada (o link para o site é fornecido pela base
códigos do site).
Um " armazém " pode ser vinculado a várias " fontes " e uma " fonte " pode ser vinculada a vários " armazéns " (relacionamento muitos para muitos). Exceções são default'ovye " origem " e " armazém ". Eles não se vinculam a outras entidades (a restrição no nível do código - falha no erro " Não é possível salvar o link relacionado à origem padrão ou ao estoque padrão "). Você pode ler mais sobre a estrutura MSI no Magento 2 no artigo " Sistema de Gerenciamento de Armazém Usando CQRS e Event Sourcing. Design ".
Usarei a configuração padrão e adicionarei todas as informações de inventário à fonte default
, que é usada no canal de vendas associado ao site com o código base
(corresponde à parte do cliente da loja - consulte 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); }
Depois de adicionar dados do inventário ao produto no painel do administrador, obtemos a seguinte imagem:

Ao adicionar "manualmente" imagens ao produto através do painel do administrador, as informações relevantes são registradas nas seguintes tabelas:
catalog_product_entity_media_gallery
: registro de mídia (arquivos de imagens e vídeo);catalog_product_entity_media_gallery_value
: vinculando mídia a produtos e fachadas de lojas (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 que usam a imagem são salvas aqui;
e as próprias imagens são salvas no diretório ./pub/media/catalog/product/x/y/
, em que y
são a primeira e a segunda letras do nome do arquivo de imagem. Por exemplo, o arquivo image.png
deve ser salvo como ./pub/media/catalog/product/i/m/image.png
para que a plataforma possa usá-lo como imagem ao descrever produtos do catálogo.
Registramos o arquivo de mídia localizado em ./pub/media/catalog/product/
(o processo de colocação do arquivo neste artigo não é considerado):
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; }
Ao se registrar, um novo arquivo de mídia recebe um identificador.
Vinculamos o arquivo de mídia registrado ao produto correspondente para a mostra padrão:
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); }
Associamos o arquivo de mídia registrado ao produto correspondente sem referência a nenhuma loja. Não está claro onde exatamente esses dados são usados e por que é impossível acessar os dados da tabela anterior, mas essa tabela existe e os dados são gravados ao adicionar uma imagem ao produto. Portanto, assim.
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
Um arquivo de mídia pode ser usado com funções diferentes (o código do atributo correspondente é indicado entre colchetes):
- Base (
image
) - Imagem pequena (
small_image
) - Miniatura (
thumbnail
) - Imagem de amostra (
swatch_image
)
A ligação de funções a um arquivo de mídia acontece apenas em catalog_product_entity_varchar
. O código de ligação é semelhante ao código na seção " Atributos básicos do produto ".
Depois de adicionar a imagem ao produto no painel de administração, fica assim:

Categorias
Tabelas principais que contêm dados por categoria:
catalog_category_entity
: registro de categorias;catalog_category_product
: associação de produtos e categorias;catalog_category_entity_*
: valores de atributos EAV;
Inicialmente, em um aplicativo Magento vazio, o registro de categoria contém 2 categorias ( upd
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 todo o diretório Magento e não está disponível no painel de administração ou na frente. A categoria com id = 2 ( Categoria Padrão ) é a categoria raiz da loja principal do site principal ( Loja Principal de Sites ) criada quando o aplicativo é implantado (consulte Admin / Lojas / Todas as Lojas ). Além disso, a categoria raiz da loja também não está disponível, apenas suas subcategorias.
Como o tópico deste artigo ainda está importando dados do produto, não usarei a gravação direta no banco de dados ao criar categorias, mas utilizarei as classes fornecidas pelo próprio Magento (modelos e repositórios). A gravação direta no banco de dados é usada apenas para vincular o produto importado à categoria (a categoria é mapeada por seu nome, o ID da categoria é extraído ao corresponder):
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); }
Depois de adicionar um link do produto às categorias "Categoria 1" e "Categoria 2", os detalhes do produto no painel de administração são mais ou menos assim:

Ações adicionais
Após a conclusão da importação de dados, você precisa executar as seguintes etapas adicionais:
- indexação de dados: uma chamada no console
./bin/magento indexer:reindex
; - Regeneração de URL para produtos / categorias: você pode usar a extensão " elgentos / regenerate-catalog-urls "
Produtos no painel do administrador após concluir etapas adicionais:

e na frente:

Sumário
O mesmo conjunto de produtos (10 peças) que no artigo anterior é importado pelo menos uma ordem de magnitude mais rápido (1 segundo versus 10). Para uma estimativa mais precisa da velocidade, você precisa de um número maior de produtos - várias centenas e, de preferência, milhares. No entanto, mesmo com uma quantidade tão pequena de dados de entrada, pode-se concluir que o uso das ferramentas fornecidas pelo Magento (modelos e repositórios) significativamente (enfatiza - significativamente !) Acelera o desenvolvimento da funcionalidade necessária, mas significativamente (enfatiza - significativamente !) Reduza velocidade de entrada de dados no banco de dados.
Como resultado, a água ficou molhada e isso não é uma revelação. No entanto, agora tenho código para jogar e, possivelmente, tirar conclusões mais interessantes.