Magento 2: importación de productos desde fuentes externas

Magento es una solución de comercio electrónico, es decir, está más enfocada en la venta de productos que en la contabilidad de inventario, logística o finanzas que la acompaña. Otras aplicaciones (como los sistemas ERP) son más adecuadas para esto. Por lo tanto, en la práctica de uso de Magento, a menudo surge la necesidad de integrar la tienda con estos otros sistemas (por ejemplo, con 1C).

En gran medida, la integración se puede reducir a la replicación de datos por:

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

Magento ofrece una clase de objetos separada para manipular datos en la base de datos — el repositorio. Debido a la especificidad de Magento, agregar datos a la base de datos a través de repositorios se codifica fácilmente, pero se realiza, digamos, no de forma rápida. En esta publicación, reviso las etapas principales para agregar un producto a Magento 2 de forma "clásica" — utilizando clases de repositorio.

Los clientes y pedidos son replicados, generalmente, en la dirección opuesta — desde Magento hacia sistemas ERP externos. Por lo tanto, es más sencillo, desde el lado de Magento sólo necesitas seleccionar los datos correspondientes, y después — "nuestras balas han salido«.

Principios de escritura de datos en la base de datos

Actualmente, la creación de objetos que se guardan en la base de datos de forma programática en Magento se realiza a través de Factory:

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

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

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

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

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

El enfoque que utiliza "Factory" y "Repository" se puede aplicar a todos los modelos principales en el dominio de Magento 2.

Información básica sobre el producto

Reviso la estructura de datos correspondiente a 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 productos):

entity_id
attribute_set_id
type_id
sku
has_options
required_options
created_at
updated_at

Me limito a un tipo de producto (type_id='simple'), un conjunto de atributos por defecto (attribute_set_id=4) e ignoro los atributos has_options y required_options. Dado que los atributos entity_id, created_at y updated_at se generan automáticamente, en esencia, para agregar un nuevo producto, sólo necesitamos establecer sku. Así lo hago:

/** @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 obtengo una excepción:

El valor del atributo "Nombre del Producto" está vacío. Establezca el atributo e inténtelo de nuevo.

Agrego en la solicitud el nombre del producto y recibo un mensaje de que falta el atributo Precio. Después de añadir el precio, el producto se guarda en 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 guarda en la tabla de atributos varchar del producto (catalog_product_entity_varchar), el precio se guarda en la tabla catalog_product_entity_decimal. Antes de añadir el producto, es recomendable indicar explícitamente que estamos utilizando la interfaz administrativa para importar datos:

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

Atributos adicionales

El manejo de atributos adicionales de productos con Magento es un placer. El modelo de datos EAV para entidades principales (vea la tabla eav_entity_type) es una de las características clave de esta plataforma. Simplemente añadimos los atributos correspondientes al modelo de producto:

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

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

$repoProd->save($prod);

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

Datos de inventario

En términos simples: la cantidad del producto en el inventario. En Magento 2.3, las estructuras en la base de datos que describen el formato de almacenamiento de los datos de inventario diferencias significativas de lo que había antes. Sin embargo, añadir la cantidad del producto en el inventario a través del modelo de producto no es mucho más complicado que añadir 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

Generalmente, el acompañamiento multimedia del producto para el cliente en la tienda (comercio electrónico) es diferente del acompañamiento multimedia del mismo producto para el empleado en el sistema interno de contabilidad (ERP). En el primer caso, es recomendable mostrar el "producto de frente"; en el segundo, basta con dar una visión general del producto. Sin embargo, transferir al menos la imagen principal del producto es bastante común case durante la importación de datos.

Al añadir una imagen a través del panel de administración, la imagen se guarda primero en un directorio temporal (./pub/media/tmp/catalog/product) y solo al guardar el producto se mueve al directorio multimedia (./pub/media/catalog/product). Además, al añadir a través del panel de administración, se asignan etiquetas a la imagen 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 solo se vinculan después de guardar previamente el producto y obtenerlo de nuevo del repositorio. Y es necesario especificar el atributo label al añadir un registro a la galería multimedia del producto (de lo contrario, obtenemos una excepción Índice no definido: etiqueta en .../module-catalog/Model/Product/Gallery/CreateHandler.php en la línea 516).

Categorías

A menudo, la estructura de categorías de la tienda y la aplicación backend o la disposición de los productos en ellas pueden variar significativamente. Las estrategias para transferir datos sobre las categorías y los productos en ellas dependen de muchos factores. En este ejemplo, seguiré la siguiente:

  • las categorías del backend y de la tienda se emparejan por nombre;
  • si se importa una categoría que no existe en la tienda, se crea bajo la categoría raíz (Default Category) y su posterior posicionamiento en el catálogo de la tienda se asume manualmente;
  • la vinculación de un producto a una categoría se lleva a cabo solo al crearlo en la tienda (primera importación);

La información principal sobre la categoría se encuentra en la tabla catalog_category_entity (catálogo de categorías). Crear 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 por ID de categoría y SKU del producto:

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

Total

Es bastante sencillo escribir código para agregar un producto a Magento 2 programáticamente. Todo lo expuesto anteriormente lo he condensado en el módulo de demostración «flancer32/mage2_ext_demo_import« En el módulo, solo hay un comando de consola fl32:import:prod, que importa productos descritos en el archivo JSON «./etc/data/products.json«:

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

Las imágenes para importación se encuentran en el directorio ./etc/data/img.

El tiempo de importación de 10 productos de esta manera es de aproximadamente 10 segundos en mi portátil. Si se desarrolla más este pensamiento, no es difícil llegar a la conclusión de que se pueden importar alrededor de 3600 productos por hora, y la importación de 100K productos podría llevar aproximadamente 30 horas. Cambiar el portátil por un servidor puede suavizar un poco la situación. Podría ser incluso varias veces. Pero no por órdenes de magnitud. Quizás esta velocidad lenta es, en cierta medida, una de las razones de la aparición del proyecto magento/async-import.

Una solución radical para aumentar la velocidad de importación puede ser la escritura directa en la base de datos, pero en este caso se pierden todas las 'ventajas' relacionadas con la extensibilidad de Magento: todo lo 'extendido' deberá hacerse uno mismo. Sin embargo, vale la pena. Si es posible, consideraré el enfoque de escritura directa en la base de datos en el siguiente artículo.

Fuente: habr.com

Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS 🔥 Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS | ProHoster