Magento 2: importação de produtos de fontes externas

Magento é uma solução de e-commerce, ou seja, está mais voltada para a venda de produtos do que para a contabilidade de estoque, logística ou finanças associadas às vendas. Para isso, outros aplicativos (como sistemas ERP) são mais adequados. Portanto, frequentemente, no uso do Magento, surge a necessidade de integrar a loja com esses outros sistemas (por exemplo, com 1C).

Em termos gerais, a integração pode ser reduzida à replicação de dados em:

  • catálogo (produtos, categorias);
  • dados de inventário (estoques de produtos e preços);
  • clientes;
  • pedidos;

O Magento oferece uma classe de objetos separada para manipulação de dados no banco — repositório. Devido à natureza específica do Magento, adicionar dados ao banco através dos repositórios é fácil de codificar, mas ocorre, digamos, de forma lenta. Neste artigo, vou abordar as etapas principais para adicionar programaticamente um produto ao Magento 2 de forma "clássica" — usando classes de repositório.

Clientes e pedidos são replicados, geralmente, na outra direção — do Magento para sistemas ERP externos. Portanto, isso é mais simples, do lado do Magento, só é necessário selecionar os dados correspondentes, e depois — "as balas saíram do nosso lado«.

Princípios para gravar dados no banco

Atualmente, a criação de objetos que são salvos no banco de forma programática no Magento é feita através de Factory:

function __construct (MagentoCmsModelBlockFactory $blockFactory) {
    $this->blockFactory = $blockFactory;
}

/** @var MagentoCmsModelBlock $block * / 
$block = $this->blockFactory->create();

e a gravação no banco — através de Repository:

function __construct (MagentoCmsApiBlockRepositoryInterface $blockRepo) {
    $this->blockRepo = $blockRepo;
}

$this->blockRepo->save($block);

A abordagem utilizando "Factory" e "Repository" pode ser aplicada a todos os principais modelos no domínio do Magento 2.

Informações básicas sobre o produto

Estou analisando a estrutura de dados correspondente à versão Magento 2.3. A informação básica do produto está na tabela catalog_product_entity (registro de produtos):

entity_id
attribute_set_id
type_id
sku
has_options
required_options
created_at
updated_at

Estou me limitando a um tipo de produto (type_id='simple'), um conjunto de atributos padrão (attribute_set_id=4) e ignorando os atributos has_options e required_options. Como os atributos entity_id, SELECT now() as created_at, cpu.* FROM (SELECT DISTINCT tags_id from cpu) base ASOF LEFT JOIN cpu USING (tags_id, created_at) e updated_at são gerados automaticamente, na verdade, para adicionar um novo produto, é suficiente fornecer o sku. Faço assim:

/** @var MagentoCatalogApiDataProductInterfaceFactory $factProd */
/** @var MagentoCatalogApiProductRepositoryInterface $repoProd */
/** @var MagentoCatalogApiDataProductInterface $prod */
$prod = $factProd->create();
$prod->setAttributeSetId(4);
$prod->setTypeId('simple');
$prod->setSku($sku);
$repoProd->save($prod);

e recebo uma exceção:

O valor do atributo "Nome do Produto" está vazio. Defina o atributo e tente novamente.

Adiciono ao pedido o nome do produto e recebo uma mensagem de que falta um atributo Preço. Após adicionar o preço, o produto é salvo na base:

$prod = $factProd->create();
$prod->setAttributeSetId(4);
$prod->setTypeId('simple');
$prod->setSku($sku);
$prod->setName($name);
$prod->setPrice($price);
$repoProd->save($prod);

O nome do produto é salvo na tabela de atributos varchar do produto (catalog_product_entity_varchar), o preço — na tabela catalog_product_entity_decimal. Antes de adicionar o produto, é recomendável indicar explicitamente que estamos usando a vitrine administrativa para importar dados:

/** @var MagentoStoreModelStoreManagerInterface $manStore */
$manStore->setCurrentStore(0);

Atributos adicionais

Trabalhar com atributos adicionais de produtos no Magento é um prazer. O modelo de dados EAV para entidades principais (veja a tabela eav_entity_type) — é uma das características principais dessa plataforma. Basta adicionar os atributos correspondentes ao modelo de produto:

$prodEntity->setData('description', $desc);
$prodEntity->setData('short_description', $desc_short);
// ou
$prodEntity->setDescription($desc);
$prodEntity->setShortDescription($desc_short);

e ao salvar o modelo através do repositório:

$repoProd->save($prod);

os atributos adicionais também serão salvos nas tabelas correspondentes do banco de dados.

Dados de inventário

De forma simples — a quantidade do produto em estoque. No Magento 2.3, as estruturas no banco de dados que descrevem o formato de armazenamento dos dados de inventário, diferem significativamente do que era anteriormente. No entanto, adicionar a quantidade do produto em estoque através do modelo de produto não é muito mais complicado do que adicionar outros atributos:

/** @var MagentoCatalogModelProduct $prodEntity */
/** @var MagentoCatalogApiProductRepositoryInterface $repoProd */
$inventory = [
    'is_in_stock' => true,
    'qty' => 1234
];
$prodEntity->setData('quantity_and_stock_status', $inventory);
$repoProd->save($prodEntity);

Mídia

Geralmente, o acompanhamento de mídia do produto para o cliente na loja (e-commerce) é diferente do acompanhamento de mídia do mesmo produto para um funcionário no sistema de contabilidade interno (ERP). No primeiro caso, é desejável mostrar o "produto de frente", no segundo — é suficiente fornecer uma visão geral do produto. No entanto, transferir ao menos a imagem primária do produto — é algo bastante comum caso durante a importação de dados.

Ao adicionar uma imagem através do painel administrativo, a imagem é primeiro salva em um diretório temporário (./pub/media/tmp/catalog/product) e só ao salvar o produto é movida para o diretório de mídia (./pub/media/catalog/product). Além disso, ao adicionar através do painel administrativo, são atribuídos os rótulos image, small_image, thumbnail, swatch_image.

/** @var MagentoCatalogApiProductRepositoryInterface $repoProd */
/** @var MagentoCatalogModelProductGalleryCreateHandler $hndlGalleryCreate */
/* $imagePath = '/path/to/file.png';  $imagePathRelative = '/f/i/file.png' */
$imagePathRelative = $this->imagePlaceToTmpMedia($imagePath);
/* reload product with gallery data */
$product = $repoProd->get($sku);
/* add image to product's gallery */
$gallery['images'][] = [
    'file' => $imagePathRelative,
    'media_type' => 'image'
    'label' => ''
];
$product->setData('media_gallery', $gallery);
/* set usage areas */
$product->setData('image', $imagePathRelative);
$product->setData('small_image', $imagePathRelative);
$product->setData('thumbnail', $imagePathRelative);
$product->setData('swatch_image', $imagePathRelative);
/* create product's gallery */
$hndlGalleryCreate->execute($product);

Por algum motivo, a mídia só é vinculada após a prévia gravação do produto e a recuperação dele novamente do repositório. E é necessário especificar o atributo label ao adicionar um registro à galeria de mídia do produto (caso contrário, recebemos uma exceção Índice indefinido: label em .../module-catalog/Model/Product/Gallery/CreateHandler.php na linha 516).

Categorias

Frequentemente, a estrutura das categorias da loja e do aplicativo backend ou a disposição dos produtos dentro delas pode variar significativamente. As estratégias de migração de dados sobre categorias e produtos dependem de muitos fatores. Neste exemplo, sigo o seguinte:

  • as categorias do backend e da loja são correspondidas pelo nome;
  • se uma categoria que não existe na loja for importada, ela será criada sob a categoria raiz (Categoria Padrão) e sua posterior posicionamento no catálogo da loja deve ser feito manualmente;
  • a vinculação do produto à categoria ocorre apenas no momento de sua criação na loja (primeira importação);

As principais informações sobre a categoria estão na tabela catalog_category_entity (catálogo de categorias). A criação de uma categoria no Magento:

/** @var MagentoCatalogApiDataCategoryInterfaceFactory $factCat */
/** @var MagentoCatalogApiCategoryRepositoryInterface $repoCat */
$cat = $factCat->create();
$cat->setName($name);
$cat->setIsActive(true);
$repoCat->save($cat);

A vinculação do produto à categoria é feita pelo ID da categoria e SKU do produto:

/** @var MagentoCatalogModelCategoryProductLinkFactory $factCatProdLink */
/** @var MagentoCatalogApiCategoryLinkRepositoryInterface $repoCatLink */
$link = $factCatProdLink->create();
$link->setCategoryId($catMageId);
$link->setSku($prodSku);
$repoCatLink->save($link);

Total

Escrever um código para adicionar um produto ao Magento 2 programaticamente não é muito difícil. Tudo que foi mencionado acima foi consolidado em um módulo de demonstração "flancer32/mage2_ext_demo_import". O módulo contém apenas um comando de console fl32:import:prod, que importa produtos descritos no arquivo JSON "./etc/data/products.json«:

[
  {
    "sku": "...",
    "name": "...",
    "desc": "...",
    "desc_short": "...",
    "price": ...,
    "qty": ...,
    "categories": ["..."],
    "image_path": "..."
  }
]

As imagens para importação estão no diretório ./etc/data/img.

O tempo de importação de 10 produtos dessa forma leva cerca de 10 segundos no meu notebook. Se desenvolver esse pensamento, não é difícil chegar à conclusão de que, em uma hora, é possível importar cerca de 3600 produtos, e a importação de 100K produtos pode levar cerca de 30 horas. Trocar o notebook por um servidor pode suavizar a situação, talvez até várias vezes. Mas não em ordens de magnitude. Essa lentidão pode ser parcialmente uma das razões para o surgimento do projeto magento/async-import.

Uma solução radical para aumentar a velocidade de importação pode ser a gravação direta no banco de dados, mas nesse caso, todas as "vantagens" relacionadas à extensibilidade do Magento se perdem — será necessário fazer tudo "estendido" por conta própria. No entanto, vale a pena. Se possível, considerarei a abordagem com gravação direta no BD no próximo artigo.

Fonte: habr.com

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