En un artículo anterior, describí el proceso de importación de productos en Magento 2 de la forma habitual: a través de modelos y repositorios. El método habitual se caracteriza por una velocidad de procesamiento de datos muy baja. Aproximadamente un producto por segundo salió en mi computadora portátil. En esta continuación, considero una forma alternativa de importar un producto: mediante el registro directo en la base de datos, evitando los mecanismos estándar de Magento 2 (modelos, fábricas, repositorios). La secuencia de pasos para importar productos se puede adaptar a cualquier lenguaje de programación que pueda funcionar con MySQL.
Descargo de responsabilidad : Magento tiene una funcionalidad lista para importar datos y, lo más probable, ya tiene suficiente. Sin embargo, si necesita un control más completo sobre el proceso de importación, no se limita a preparar un archivo CSV para lo que es, bienvenido a cat.

El código resultante de la escritura de ambos artículos se puede ver en el módulo Magento flancer32 / mage2_ext_demo_import . Estas son algunas de las restricciones que seguí para simplificar el código del módulo de demostración:
- Los productos solo se crean, no se actualizan.
- Un almacén
- Solo se importan los nombres de categoría, sin su estructura
- Las estructuras de datos se ajustan a la versión 2.3
JSON para importar un solo producto:
{ "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" }
Resumen de los principales pasos de importación
- registro de producto
- enlace de producto y sitio web
- atributos básicos del producto (EAV)
- datos de inventario (cantidad de producto en stock)
- medios (fotos)
- enlace a categorías de catálogo
Registro de producto
La información básica del producto está en 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`) )
Información mínima necesaria para crear una entrada en el registro del producto:
adicional:
type_id
: si no se type_id
, se usará 'simple'
Para la grabación directa en la base de datos, uso el adaptador DB de Magento:
function create($sku, $typeId, $attrSetId) { $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; }
Después de registrar un producto en catalog_product_entity
se hace visible en el panel de administración, en la cuadrícula del producto ( Catálogo / Productos ).

Enlace de producto y sitio web
La relación del producto con el sitio determina en qué tiendas y en qué vitrinas estará disponible el producto en el frente.
function linkToWebsite($prodId, $websiteId) { $conn = $this->resource->getConnection(); $table = $this->resource->getTableName('catalog_product_website'); $bind = [ 'product_id' => $prodId, 'website_id' => $websiteId ]; $conn->insert($table, $bind); }

Atributos básicos del producto
Un producto recién registrado aún no tiene un nombre o descripción. Todo esto se hace a través de los atributos EAV . Aquí hay una lista de los atributos básicos del producto que se necesitan para garantizar que el producto se muestre correctamente en el frente:
name
price
description
short_description
status
tax_class_id
url_key
visibility
Se agrega un atributo separado al producto de esta manera (se omiten los detalles de obtener el identificador y el tipo de atributo por su código):
public function create($prodId, $attrCode, $attrValue) { $attrId = $attrType = if ($attrId) { $conn = $this->resource->getConnection(); $tblName = 'catalog_product_entity_' . $attrType; $table = $this->resource->getTableName($tblName); $bind = [ 'attribute_id' => $attrId, 'entity_id' => $prodId, 'store_id' => 0, 'value' => $attrValue ]; $conn->insert($table, $bind); } }
Usando el código de atributo, determine su id y tipo de datos ( datetime
y datetime
, decimal
, int
, text
, varchar
), luego en la tabla correspondiente escribimos los datos para el escaparate administrativo ( store_id = 0
).
Después de agregar los atributos anteriores al producto, obtenemos esta imagen en el panel de administración:

Datos de inventario
A partir de la versión 2.3, Magento tiene simultáneamente dos conjuntos de tablas que proporcionan almacenamiento de información de inventario (cantidad de producto):
cataloginventory_*
: estructura antigua;inventory_*
: nueva estructura (MSI - Inventario de múltiples fuentes);
Necesita agregar datos de inventario a ambas estructuras, porque la nueva estructura aún no es completamente independiente de la anterior (parece que la tabla cataloginventory_stock_status
se cataloginventory_stock_status
como inventory_stock_1
para el almacén default
en la nueva estructura).
cataloginventory_
Al implementar Magneto 2.3, inicialmente tenemos 2 entradas en store_website
, que corresponde a dos sitios: el cliente administrativo y el 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|
En la tabla cataloginventory_stock
, solo tenemos una entrada:
stock_id|website_id|stock_name| --------|----------|----------| 1| 0|Default |
Es decir, en nuestra antigua estructura solo hay un "almacén" ( stock
) y está vinculado al sitio web administrativo. Agregar nuevas sources
/ stocks
al MSI a través del stocks
administración (nueva estructura) no conduce a nuevas entradas en cataloginventory_stock
.
Los datos de inventario de productos en la estructura anterior se escriben inicialmente en las tablas:
cataloginventory_stock_item
cataloginventory_stock_status
cataloginventory_stock_item
function createOldItem($prodId, $qty) { $isQtyDecimal = (((int)$qty) != $qty); $isInStock = ($qty > 0); $conn = $this->resource->getConnection(); $table = $this->resource->getTableName('cataloginventory_stock_item'); $bind = [ 'product_id' => $prodId, 'stock_id' => 1, 'qty' => $qty, 'is_qty_decimal' => $isQtyDecimal, 'is_in_stock' => $isInStock, 'website_id' => 0 ]; $conn->insert($table, $bind); }
cataloginventory_stock_status
function createOldStatus($prodId, $qty) { $isInStock = ($qty > 0); $conn = $this->resource->getConnection(); $table = $this->resource->getTableName('cataloginventory_stock_status'); $bind = [ 'product_id' => $prodId, 'stock_id' => 1, 'qty' => $qty, 'stock_status' => \Magento\CatalogInventory\Api\Data\StockStatusInterface::STATUS_IN_STOCK, 'website_id' => 0 ]; $conn->insert($table, $bind); }
inventario_
Inicialmente, la nueva estructura para almacenar datos de inventario contiene 1 " fuente " ( inventory_source
):
source_code|name |enabled|description |latitude|longitude|country_id|...| -----------|--------------|-------|--------------|--------|---------|----------|...| default |Default Source| 1|Default Source|0.000000| 0.000000|US |...|
y un " almacén " ( inventory_stock
):
stock_id|name | --------|-------------| 1|Default Stock|
Una " fuente " es una tienda física de productos (el registro contiene coordenadas físicas y una dirección de correo). Un " almacén " es una unión lógica de varias "fuentes" ( inventory_source_stock_link
)
link_id|stock_id|source_code|priority| -------|--------|-----------|--------| 1| 1|default | 1|
en el nivel del cual hay un enlace al canal de ventas ( inventory_stock_sales_channel
)
type |code|stock_id| -------|----|--------| website|base| 1|
A juzgar por la estructura de datos, se suponen varios tipos de canales de venta, pero de forma predeterminada solo se utiliza la conexión " stock " - " sitio web " (el enlace al sitio web está dado por el código del sitio web - base
).
Un " almacén " puede estar vinculado a varias " fuentes ", y una " fuente " puede estar vinculada a varios " almacenes " (relación de muchos a muchos). Las excepciones son default'ovye " fuente " y " almacén ". No se unen a otras entidades (restricción en el nivel del código: el error " No se puede guardar el enlace relacionado con el origen predeterminado o el stock predeterminado " se bloquea). Puede leer más sobre la estructura de MSI en Magento 2 en el artículo " Sistema de gestión de almacenes mediante CQRS y búsqueda de eventos. Diseño ".
Usaré la configuración predeterminada y agregaré toda la información de inventario a la fuente default
, que se usa en el canal de ventas asociado con el sitio web base
(corresponde a la parte del cliente de la tienda - ver store_website
):
function createNewItem($sku, $qty) { $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); }
Después de agregar datos de inventario al producto en el panel de administración, obtenemos esta imagen:

Cuando se agregan "manualmente" imágenes al producto a través del panel de administración, la información relevante se registra en las siguientes tablas:
catalog_product_entity_media_gallery
: registro de medios (imágenes y archivos de video);catalog_product_entity_media_gallery_value
: enlace de medios a productos y escaparates (localización);catalog_product_entity_media_gallery_value_to_entity
: enlace de medios solo a productos (presumiblemente contenido de medios predeterminado para el producto);catalog_product_entity_varchar
: los roles que usan la imagen se guardan aquí;
y las imágenes mismas se guardan en el directorio ./pub/media/catalog/product/x/y/
, donde x
e y
son la primera y la segunda letra del nombre del archivo de imagen. Por ejemplo, el archivo image.png
debe guardarse como ./pub/media/catalog/product/i/m/image.png
para que la plataforma pueda usarlo como una imagen cuando describa productos del catálogo.
Registramos el archivo multimedia ubicado en ./pub/media/catalog/product/
(no se considera el proceso de colocar el archivo en este artículo):
function createMediaGallery($imgPathPrefixed) { $attrId = $conn = $this->resource->getConnection(); $table = $this->resource->getTableName('catalog_product_entity_media_gallery'); $bind = [ 'attribute_id' => $attrId, 'value' => $imgPathPrefixed, 'media_type' => 'image', 'disabled' => false ]; $conn->insert($table, $bind); $result = $conn->lastInsertId($table); return $result; }
Al registrarse, a un nuevo archivo multimedia se le asigna un identificador.
Vinculamos el archivo multimedia registrado con el producto correspondiente para el escaparate predeterminado:
function createGalleryValue($mediaId, $prodId) { $conn = $this->resource->getConnection(); $table = $this->resource->getTableName('catalog_product_entity_media_gallery_value'); $bind = [ 'value_id' => $mediaId, 'store_id' => 0, 'entity_id' => $prodId, 'label' => null, 'position' => 1, 'disabled' => false ]; $conn->insert($table, $bind); }
Asociamos el archivo multimedia registrado con el producto correspondiente sin referencia a ningún escaparate. No está claro dónde se usan exactamente estos datos y por qué es imposible acceder a los datos de la tabla anterior, pero esta tabla existe y los datos se escriben al agregar una imagen al producto. Por lo tanto, así.
function createGalleryValueToEntity($mediaId, $prodId) { $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
Se puede usar un archivo multimedia con diferentes roles (el código del atributo correspondiente se indica entre paréntesis):
- Base (
image
) - Imagen pequeña (
small_image
) - Miniatura (
thumbnail
) - Imagen de
swatch_image
( swatch_image
)
La vinculación de roles a un archivo multimedia simplemente ocurre en catalog_product_entity_varchar
. El código de enlace es similar al código en la sección " Atributos básicos del producto ".
Después de agregar la imagen al producto en el panel de administración, resulta así:

Categorias
Tablas principales que contienen datos por categoría:
catalog_category_entity
: registro de categorías;catalog_category_product
: asociación de productos y categorías;catalog_category_entity_*
: valores de atributos EAV;
Inicialmente, en una aplicación Magento vacía, el registro de categorías contiene 2 categorías (acorté los nombres de columna: 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|
La categoría con id = 1 es la raíz de todo el directorio de Magento y no está disponible en el panel de administración o en el frente. La categoría con id = 2 ( Categoría predeterminada ) es la categoría raíz de la tienda principal del sitio web principal ( Tienda del sitio web principal ) creada cuando se implementa la aplicación (consulte Admin / Tiendas / Todas las tiendas ). Además, la categoría raíz de la tienda en el frente tampoco está disponible, solo sus subcategorías.
Como el tema de este artículo sigue importando datos de productos, no utilizaré la grabación directa en la base de datos al crear categorías, pero utilizaré las clases proporcionadas por Magento (modelos y repositorios). La grabación directa en la base de datos se usa solo para vincular el producto importado con la categoría (la categoría se asigna por su nombre, la identificación de la categoría se extrae cuando coincide):
function create($prodId, $catId) { $conn = $this->resource->getConnection(); $table = $this->resource->getTableName('catalog_category_product'); $bind = [ 'category_id' => $catId, 'product_id' => $prodId, ]; $conn->insert($table, $bind); }
Después de agregar un enlace de producto a las categorías "Categoría 1" y "Categoría 2", los detalles del producto en el panel de administración se ven así:

Acciones adicionales
Una vez completada la importación de datos, se deben realizar los siguientes pasos adicionales:
- indexación de datos: una llamada en la consola
./bin/magento indexer:reindex
; - Regeneración de URL para productos / categorías: puede usar la extensión " elgentos / regenerate-catalog-urls "
Productos en el panel de administración después de completar pasos adicionales:

y al frente:

Resumen
El mismo conjunto de productos (10 piezas) que en el artículo anterior se importa al menos un orden de magnitud más rápido (1 segundo frente a 10). Para una estimación más precisa de la velocidad, necesita una mayor cantidad de productos: varios cientos, y preferiblemente miles. Sin embargo, incluso con una cantidad tan pequeña de datos de entrada, se puede concluir que el uso de las herramientas proporcionadas por Magento (modelos y repositorios) significativamente (enfatizar - ¡ significativamente !) Acelerar el desarrollo de la funcionalidad requerida, pero significativamente (enfatizar - ¡ significativamente !) Reducir velocidad de entrada de datos en la base de datos.
Como resultado, el agua resultó estar húmeda y esto no es una revelación. Sin embargo, ahora tengo código para jugar y posiblemente sacar conclusiones más interesantes.