Magento 2: importar produtos diretamente para o banco de dados

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.


imagem


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:


  • attribute_set_id
  • sku

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) { /** @var \Magento\Framework\App\ResourceConnection $this->resource */ /** @var \Magento\Framework\DB\Adapter\Pdo\Mysql $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; } 

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


imagem


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) { /** @var \Magento\Framework\App\ResourceConnection $this->resource */ /** @var \Magento\Framework\DB\Adapter\Pdo\Mysql $conn */ $conn = $this->resource->getConnection(); $table = $this->resource->getTableName('catalog_product_website'); $bind = [ 'product_id' => $prodId, 'website_id' => $websiteId ]; $conn->insert($table, $bind); } 

imagem


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 = /* get attribute ID by attribute code */ $attrType = /* get attribute type [datetime|decimal|int|text|varchar]) by attribute code */ if ($attrId) { /** @var \Magento\Framework\App\ResourceConnection $this->resource */ /** @var \Magento\Framework\DB\Adapter\Pdo\Mysql $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, 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:


imagem


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); /** @var \Magento\Framework\App\ResourceConnection $this->resource */ /** @var \Magento\Framework\DB\Adapter\Pdo\Mysql $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 \Magento\Framework\App\ResourceConnection $this->resource */ /** @var \Magento\Framework\DB\Adapter\Pdo\Mysql $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' => \Magento\CatalogInventory\Api\Data\StockStatusInterface::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 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) { /** @var \Magento\Framework\App\ResourceConnection $this->resource */ /** @var \Magento\Framework\DB\Adapter\Pdo\Mysql $conn */ $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:


imagem


Mídia


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 = /* get attribute ID by attribute code 'media_gallery' */ /** @var \Magento\Framework\App\ResourceConnection $this->resource */ /** @var \Magento\Framework\DB\Adapter\Pdo\Mysql $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; } 

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) { /** @var \Magento\Framework\App\ResourceConnection $this->resource */ /** @var \Magento\Framework\DB\Adapter\Pdo\Mysql $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 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) { /** @var \Magento\Framework\App\ResourceConnection $this->resource */ /** @var \Magento\Framework\DB\Adapter\Pdo\Mysql $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 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:


imagem


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) { /** @var \Magento\Framework\App\ResourceConnection $this->resource */ /** @var \Magento\Framework\DB\Adapter\Pdo\Mysql $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 do produto às categorias "Categoria 1" e "Categoria 2", os detalhes do produto no painel de administração são mais ou menos assim:


imagem


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:


imagem


e na frente:


imagem


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.

Source: https://habr.com/ru/post/pt440262/


All Articles