Magento 2: Importando Produtos de Fontes Externas

Magento é uma solução de e-commerce, ou seja, visa mais a venda de produtos do que a armazenagem, a logística ou a contabilidade financeira que acompanha as vendas. Outras aplicações (por exemplo, sistemas ERP) são mais adequadas para acompanhar aplicações. Portanto, muitas vezes na prática de utilização do Magento surge a tarefa de integrar uma loja com esses outros sistemas (por exemplo, 1C).

Em geral, a integração pode ser reduzida à replicação de dados:

  • catálogo (produtos, categorias);
  • dados de estoque (saldos de produtos em armazéns e preços);
  • clientes;
  • pedidos;

Magento oferece uma classe separada de objetos para manipulação de dados no banco de dados - repositórios. Devido às especificidades do Magento, adicionar dados ao banco de dados através de repositórios é fácil de codificar, mas é, digamos, lento. Nesta publicação, considero os principais estágios da adição programática de um produto ao Magento 2 da maneira “clássica” - usando classes repo.

Clientes e pedidos geralmente são replicados na outra direção – do Magento para sistemas ERP externos. Portanto, é mais simples com eles, no lado do Magento você só precisa selecionar os dados apropriados e depois “balas voaram do nosso lado".

Princípios de registro de dados em um banco de dados

No momento, a criação de objetos salvos no banco de dados programaticamente no Magento é feita através Fábrica:

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

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

e a gravação no banco de dados é feita através Repositório:

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

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

A abordagem “Fábrica” e “Repositório” pode ser usada para todos os principais modelos no domínio Magento 2.

Informações básicas do produto

Estou olhando para uma estrutura de dados que corresponde à versão Magento 2.3. As informações mais básicas sobre o produto estão na tabela catalog_product_entity (cadastro do produto):

entity_id
attribute_set_id
type_id
sku
has_options
required_options
created_at
updated_at

Estou limitado a um tipo de produto (type_id='simple'), um conjunto de atributos padrão (attribute_set_id=4) e ignorar atributos has_options и required_options. Já que os atributos entity_id, created_at и updated_at são gerados automaticamente, então, na verdade, para adicionar um novo produto, basta definir sku. Eu faço isso:

/** @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:

The "Product Name" attribute value is empty. Set the attribute and try again.

Eu adiciono o nome do produto à solicitação e recebo uma mensagem informando que o atributo está faltando Price. Após adicionar o preço, o produto é adicionado ao banco de dados:

$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 é armazenado na tabela de atributos varchar do produto (catalog_product_entity_varchar), preço - na tabela catalog_product_entity_decimal. Antes de adicionar um produto, é aconselhável indicar explicitamente que estamos utilizando a vitrine administrativa para importar dados:

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

Atributos Adicionais

Processar atributos adicionais do produto usando Magento é um prazer. Modelo de dados EAV para entidades principais (ver tabela eav_entity_type) é um dos principais recursos desta plataforma. Simplesmente adicionamos os atributos apropriados ao modelo do produto:

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

e ao salvar o modelo através do objeto repo:

$repoProd->save($prod);

atributos adicionais também serão armazenados nas tabelas de banco de dados correspondentes.

Dados de inventário

Em termos simples - a quantidade de produto em estoque. No Magento 2.3, as estruturas do banco de dados que descrevem o formato para armazenar dados de inventário são significativamente diferente do que aconteceu antes. Porém, adicionar a quantidade de um produto em estoque através do modelo do produto não é muito mais difícil 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

Via de regra, o suporte de mídia de um produto para um cliente em uma loja (e-commerce) difere do suporte de mídia do mesmo produto para um funcionário em um sistema de contabilidade interna (ERP). No primeiro caso é aconselhável mostrar o produto cara a cara, no segundo basta dar uma ideia geral do produto. Porém, transferir pelo menos a imagem primária de um produto é bastante comum. case ao importar dados.

Ao adicionar uma imagem através do painel de administração, a imagem é primeiro salva em um diretório temporário (./pub/media/tmp/catalog/product) e somente ao salvar o produto é movido para o diretório de mídia (./pub/media/catalog/product). Além disso, quando adicionada através do painel de administração, a imagem é marcada 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 alguma razão, a mídia é vinculada somente após salvar o produto e recuperá-lo novamente do repositório. E você precisa especificar o atributo label ao adicionar uma entrada à galeria de mídia do produto (caso contrário, receberemos uma exceção Undefined index: label in .../module-catalog/Model/Product/Gallery/CreateHandler.php on line 516).

categoria

Freqüentemente, a estrutura de categorias da loja e do aplicativo de back-end ou a colocação de produtos neles podem diferir significativamente. As estratégias para migrar dados sobre categorias e produtos dentro delas dependem de muitos fatores. Neste exemplo, mantenho o seguinte:

  • as categorias de backend e loja são comparadas por nome;
  • se for importada uma categoria que não esteja na loja, ela será criada na categoria raiz (Default Category) e seu posterior posicionamento no catálogo da loja é assumido manualmente;
  • um produto é atribuído a uma categoria somente quando é criado na loja (primeira importação);

Informações básicas sobre a categoria estão na tabela catalog_category_entity (catálogo de categorias). Criando 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 de um produto a uma categoria é realizada através do ID da categoria e do SKU do produto:

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

No total

Escrever código para adicionar um produto programaticamente ao Magento 2 é bastante fácil. Combinei tudo o que foi declarado acima em um módulo de demonstração “flancer32/mage2_ext_demo_import". Existe apenas um comando de console no módulo fl32:import:prod, que importa os produtos descritos no arquivo JSON "./etc/data/products.json":

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

As fotos para importação estão no catálogo ./etc/data/img.

O tempo para importar 10 produtos usando esse método é de cerca de 10 segundos no meu laptop. Se desenvolvermos mais esta ideia, é fácil chegar à conclusão de que cerca de 3600 produtos podem ser importados por hora, e pode levar cerca de 100 horas para importar 30 mil produtos. Substituir um laptop por um servidor permite amenizar um pouco a situação. Talvez até várias vezes. Mas não por ordens de grandeza. Talvez esta velocidade e lentidão seja, em certa medida, uma das razões para o surgimento do projeto magento/importação assíncrona.

Uma solução radical para aumentar a velocidade de importação poderia ser a entrada direta no banco de dados, mas neste caso todas as “guloseimas” relativas à extensibilidade do Magento são perdidas - você terá que fazer tudo “avançado” sozinho. No entanto, vale a pena. Se der certo, considerarei a abordagem com gravação direta no banco de dados no próximo artigo.

Fonte: habr.com

Adicionar um comentário