Magento 2: importar productos de fuentes externas

Magento es una solución de comercio electrónico, es decir. está más orientado a la venta de productos que al almacenamiento, la logística o la contabilidad financiera que acompaña a las ventas. Otras aplicaciones (por ejemplo, los sistemas ERP) son más adecuadas para aplicaciones complementarias. Por lo tanto, muy a menudo en la práctica de utilizar Magento surge la tarea de integrar una tienda con estos otros sistemas (por ejemplo, 1C).

En general, la integración se puede reducir a la replicación de datos mediante:

  • catálogo (productos, categorías);
  • datos de inventario (saldos de productos en almacenes y precios);
  • clientela;
  • pedidos;

Magento ofrece una clase separada de objetos para manipular datos en la base de datos: repositorios. Debido a las características específicas de Magento, agregar datos a la base de datos a través de repositorios es fácil de codificar, pero es, digamos, lento. En esta publicación, considero las etapas principales para agregar programáticamente un producto a Magento 2 de la manera "clásica": utilizando clases de repositorio.

Los clientes y los pedidos normalmente se replican en la otra dirección: desde Magento a sistemas ERP externos. Por lo tanto, es más sencillo con ellos, en el lado de Magento solo necesitas seleccionar los datos apropiados y luego "Las balas salieron volando de nuestro lado.«.

Principios para registrar datos en una base de datos.

Por el momento, la creación de objetos guardados en la base de datos mediante programación en Magento se realiza a través de Fábrica:

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

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

y la escritura en la base de datos se realiza a través de Repositorio:

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

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

El enfoque "Fábrica" ​​y "Repositorio" se puede utilizar para todos los modelos principales en el dominio Magento 2.

Información básica del producto

Estoy viendo una estructura de datos que coincide con la versión Magento 2.3. La información más básica sobre el producto se encuentra en la tabla. catalog_product_entity (registro de producto):

entity_id
attribute_set_id
type_id
sku
has_options
required_options
created_at
updated_at

Estoy limitado a un tipo de producto (type_id='simple'), un conjunto de atributos predeterminados (attribute_set_id=4) e ignorar los atributos has_options и required_options. Dado que los atributos entity_id, created_at и updated_at se generan automáticamente, entonces, de hecho, para agregar un nuevo producto, solo necesitamos configurar sku. Hago esto:

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

y me sale una excepción:

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

Agrego el nombre del producto a la solicitud y aparece un mensaje que indica que falta el atributo Price. Luego de agregar el precio, el producto se agrega a la base de datos:

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

El nombre del producto se almacena en la tabla de atributos varchar del producto (catalog_product_entity_varchar), precio - en la tabla catalog_product_entity_decimal. Antes de agregar un producto, es recomendable indicar explícitamente que estamos utilizando el escaparate administrativo para importar datos:

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

Atributos adicionales

Procesar atributos adicionales del producto usando Magento es un placer. Modelo de datos EAV para entidades principales (ver tabla eav_entity_type) es una de las características clave de esta plataforma. Simplemente agregamos los atributos apropiados al modelo de producto:

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

y al guardar el modelo a través del objeto de repositorio:

$repoProd->save($prod);

Los atributos adicionales también se almacenarán en las tablas de la base de datos correspondientes.

datos de inventario

En términos simples: la cantidad de producto en stock. En Magento 2.3, las estructuras en la base de datos que describen el formato para almacenar datos de inventario son significativamente diferente de lo que pasó antes. Sin embargo, agregar la cantidad de un producto en stock mediante el modelo de producto no es mucho más difícil que agregar otros atributos:

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

Medios de comunicación

Como regla general, el soporte mediático de un producto para un cliente en una tienda (comercio electrónico) difiere del soporte mediático del mismo producto para un empleado en un sistema de contabilidad interno (ERP). En el primer caso es recomendable mostrar el producto cara a cara, en el segundo basta con dar una idea general del producto. Sin embargo, transferir al menos la imagen principal de un producto es bastante común. case al importar datos.

Al agregar una imagen a través del panel de administración, la imagen primero se guarda en un directorio temporal (./pub/media/tmp/catalog/product) y solo cuando se guarda el producto se mueve al directorio de medios (./pub/media/catalog/product). Además, cuando se agrega a través del panel de administración, la imagen se etiqueta 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 alguna razón, los medios se vinculan solo después de guardar el producto y recuperarlo nuevamente del repositorio. Y necesitas especificar el atributo. label al agregar una entrada a la galería de medios del producto (de lo contrario, obtenemos una excepción Undefined index: label in .../module-catalog/Model/Product/Gallery/CreateHandler.php on line 516).

categoría

A menudo, la estructura de categorías de la tienda y la aplicación backend o la ubicación de los productos en ellas pueden diferir significativamente. Las estrategias para migrar datos sobre categorías y productos dentro de ellas dependen de muchos factores. En este ejemplo me quedo con lo siguiente:

  • las categorías de backend y tienda se comparan por nombre;
  • si se importa una categoría que no está en la tienda, se crea en la categoría raíz (Default Category) y su posterior posicionamiento en el catálogo de la tienda se asume manualmente;
  • un producto se asigna a una categoría solo cuando se crea en la tienda (primera importación);

La información básica sobre la categoría está en la tabla. catalog_category_entity (catálogo de categorías). Creando una categoría en Magento:

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

La vinculación de un producto a una categoría se realiza mediante el ID de categoría y el SKU del producto:

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

En total

Escribir código para agregar un producto mediante programación a Magento 2 es bastante fácil. He combinado todo lo indicado anteriormente en un módulo de demostración "flancer32/mage2_ext_demo_import". Solo hay un comando de consola en el módulo fl32:import:prod, que importa los productos descritos en el archivo JSON "./etc/data/products.json":

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

Las fotos para importar están en el catálogo. ./etc/data/img.

El tiempo para importar 10 productos usando este método es de aproximadamente 10 segundos en mi computadora portátil. Si desarrollamos más esta idea, es fácil llegar a la conclusión de que se pueden importar alrededor de 3600 productos por hora, y que se pueden necesitar unas 100 horas para importar 30 productos. Reemplazar una computadora portátil con un servidor le permite suavizar un poco la situación. Quizás incluso varias veces. Pero no por órdenes de magnitud. Quizás esta velocidad y lentitud sea, en cierta medida, una de las razones del surgimiento del proyecto. magento/async-importación.

Una solución radical para aumentar la velocidad de importación podría ser la entrada directa a la base de datos, pero en este caso se pierden todos los "beneficios" relacionados con la extensibilidad de Magento: todo lo "avanzado" tendrá que hacerlo usted mismo. Sin embargo, vale la pena. Si funciona, consideraré el enfoque con escritura directa en la base de datos en el próximo artículo.

Fuente: habr.com

Añadir un comentario