Magento 2: importe productos directamente a la base de datos

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.


imagen


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:


  • attribute_set_id
  • sku

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) { /** @var \Magento\Framework\App\ResourceConnection $this->resource */ /** @var \Magento\Framework\DB\Adapter\Pdo\Mysql $conn */ $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 ).


imagen


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) { /** @var \Magento\Framework\App\ResourceConnection $this->resource */ /** @var \Magento\Framework\DB\Adapter\Pdo\Mysql $conn */ $conn = $this->resource->getConnection(); $table = $this->resource->getTableName('catalog_product_website'); $bind = [ 'product_id' => $prodId, 'website_id' => $websiteId ]; $conn->insert($table, $bind); } 

imagen


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 = /* get attribute ID by attribute code */ $attrType = /* get attribute type [datetime|decimal|int|text|varchar]) by attribute code */ if ($attrId) { /** @var \Magento\Framework\App\ResourceConnection $this->resource */ /** @var \Magento\Framework\DB\Adapter\Pdo\Mysql $conn */ $conn = $this->resource->getConnection(); $tblName = 'catalog_product_entity_' . $attrType; $table = $this->resource->getTableName($tblName); $bind = [ 'attribute_id' => $attrId, 'entity_id' => $prodId, /* put all attributes to default store view with id=0 (admin) */ '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:


imagen


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); /** @var \Magento\Framework\App\ResourceConnection $this->resource */ /** @var \Magento\Framework\DB\Adapter\Pdo\Mysql $conn */ $conn = $this->resource->getConnection(); $table = $this->resource->getTableName('cataloginventory_stock_item'); $bind = [ 'product_id' => $prodId, /* we use one only stock in 'cataloginventory' structure by default */ 'stock_id' => 1, 'qty' => $qty, 'is_qty_decimal' => $isQtyDecimal, 'is_in_stock' => $isInStock, /* default stock is bound to admin website (see `cataloginventory_stock`) */ 'website_id' => 0 ]; $conn->insert($table, $bind); } 

cataloginventory_stock_status


 function createOldStatus($prodId, $qty) { $isInStock = ($qty > 0); /** @var \Magento\Framework\App\ResourceConnection $this->resource */ /** @var \Magento\Framework\DB\Adapter\Pdo\Mysql $conn */ $conn = $this->resource->getConnection(); $table = $this->resource->getTableName('cataloginventory_stock_status'); $bind = [ 'product_id' => $prodId, /* we use one only stock in 'cataloginventory' structure by default */ 'stock_id' => 1, 'qty' => $qty, 'stock_status' => \Magento\CatalogInventory\Api\Data\StockStatusInterface::STATUS_IN_STOCK, /* default stock is bound to admin website (see `cataloginventory_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) { /** @var \Magento\Framework\App\ResourceConnection $this->resource */ /** @var \Magento\Framework\DB\Adapter\Pdo\Mysql $conn */ $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:


imagen


Medios de comunicación


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 = /* get attribute ID by attribute code 'media_gallery' */ /** @var \Magento\Framework\App\ResourceConnection $this->resource */ /** @var \Magento\Framework\DB\Adapter\Pdo\Mysql $conn */ $conn = $this->resource->getConnection(); $table = $this->resource->getTableName('catalog_product_entity_media_gallery'); $bind = [ 'attribute_id' => $attrId, 'value' => $imgPathPrefixed, /* 'image' or 'video' */ '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) { /** @var \Magento\Framework\App\ResourceConnection $this->resource */ /** @var \Magento\Framework\DB\Adapter\Pdo\Mysql $conn */ $conn = $this->resource->getConnection(); $table = $this->resource->getTableName('catalog_product_entity_media_gallery_value'); $bind = [ 'value_id' => $mediaId, /* use admin store view by default */ 'store_id' => 0, 'entity_id' => $prodId, 'label' => null, /* we have one only image */ '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) { /** @var \Magento\Framework\App\ResourceConnection $this->resource */ /** @var \Magento\Framework\DB\Adapter\Pdo\Mysql $conn */ $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í:


imagen


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) { /** @var \Magento\Framework\App\ResourceConnection $this->resource */ /** @var \Magento\Framework\DB\Adapter\Pdo\Mysql $conn */ $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í:


imagen


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:


imagen


y al frente:


imagen


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.

Source: https://habr.com/ru/post/440262/


All Articles