Magento 2: importez des produits directement dans la base de données

Dans un article précédent, j'ai décrit le processus d'importation de produits dans Magento 2 de la manière habituelle - à travers des modèles et des référentiels. La méthode habituelle se caractérise par une très faible vitesse de traitement des données. Environ un produit par seconde est sorti sur mon ordinateur portable. Dans cette suite, je considère une manière alternative d'importer un produit - par enregistrement direct dans la base de données, en contournant les mécanismes standards de Magento 2 (modèles, usines, référentiels). La séquence d'étapes d'importation de produits peut être adaptée à tout langage de programmation compatible avec MySQL.


Avertissement : Magento a une fonctionnalité prête à l'emploi pour importer des données et, très probablement, vous en avez assez. Cependant, si vous avez besoin d'un contrôle plus complet sur le processus d'importation, non limité à la préparation d'un fichier CSV pour ce qu'il est - bienvenue à cat.


image


Le code résultant de l'écriture des deux articles peut être consulté dans le module Magento flancer32 / mage2_ext_demo_import . Voici quelques-unes des restrictions que j'ai suivies pour simplifier le code du module de démonstration:


  • Les produits sont uniquement créés, pas mis à jour.
  • Un entrepôt
  • Seuls les noms de catégorie sont importés, sans leur structure
  • Les structures de données sont conformes à la version 2.3

JSON pour importer un seul produit:


{ "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" } 

Aperçu des principales étapes d'importation


  • enregistrement du produit
  • lier le produit et le site Web
  • attributs de produit de base (EAV)
  • données d'inventaire (quantité de produit en stock)
  • médias (photos)
  • lien vers les catégories du catalogue

Enregistrement du produit


Les informations de base sur le produit se catalog_product_entity dans 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`) ) 

Informations minimales nécessaires pour créer une entrée dans le registre des produits:


  • attribute_set_id
  • sku

supplémentaire:


  • type_id - s'il n'est pas type_id , alors 'simple' sera utilisé

Pour l'enregistrement direct dans la base de données, j'utilise l'adaptateur DB de Magento lui-même:


 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; } 

Après avoir enregistré un produit dans catalog_product_entity il devient visible dans le panneau d'administration, dans la grille de produits ( Catalogue / Produits ).


image


Lier le produit et le site Web


La relation du produit avec le site détermine dans quels magasins et dans quelles vitrines le produit sera disponible en façade.


 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); } 

image


Attributs de base du produit


Un produit fraîchement enregistré n'a pas encore de nom ni de description. Tout cela se fait via les attributs EAV . Voici une liste des attributs de base du produit qui sont nécessaires pour garantir que le produit est correctement affiché à l'avant:


  • name
  • price
  • description
  • short_description
  • status
  • tax_class_id
  • url_key
  • visibility

Un attribut distinct au produit est ajouté comme ceci (les détails d'obtention de l'identifiant et du type d'attribut par son code sont omis):


 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); } } 

À l'aide du code d'attribut, déterminez son id et son type de données ( datetime , decimal , int , text , varchar ), puis dans le tableau correspondant, nous écrivons les données pour la vitrine administrative ( store_id = 0 ).


Après avoir ajouté les attributs ci-dessus au produit, nous obtenons cette image dans le panneau d'administration:


image


Données d'inventaire


À partir de la version 2.3, Magento dispose simultanément de deux ensembles de tableaux permettant le stockage des informations d'inventaire (quantité de produit):


  • cataloginventory_* : ancienne structure;
  • inventory_* : nouvelle structure (MSI - Multi Source Inventory);

Vous devez ajouter des données d'inventaire aux deux structures, car la nouvelle structure n'est pas encore complètement indépendante de l'ancienne (il semble que la table cataloginventory_stock_status soit cataloginventory_stock_status comme inventory_stock_1 pour l'entrepôt default dans la nouvelle structure).


cataloginventory_


Lors du déploiement de Magneto 2.3, nous avons initialement 2 entrées dans store_website , ce qui correspond à deux sites - le client administratif et le client 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| 

Dans la table cataloginventory_stock , nous n'avons qu'une seule entrée:


 stock_id|website_id|stock_name| --------|----------|----------| 1| 0|Default | 

Autrement dit, dans notre ancienne structure, il n'y a qu'un seul «entrepôt» ( stock ) et il est lié au site Web administratif. L'ajout de nouvelles sources / stocks au MSI via le stocks administration (nouvelle structure) ne conduit pas à de nouvelles entrées dans cataloginventory_stock .


Les données d'inventaire sur les produits de l'ancienne structure sont initialement écrites dans les tableaux:


  • 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); } 

inventaire_


Initialement, la nouvelle structure de stockage des données d'inventaire contient 1 " source " ( inventory_source ):


 source_code|name |enabled|description |latitude|longitude|country_id|...| -----------|--------------|-------|--------------|--------|---------|----------|...| default |Default Source| 1|Default Source|0.000000| 0.000000|US |...| 

et un " entrepôt " ( inventory_stock ):


 stock_id|name | --------|-------------| 1|Default Stock| 

Une « source » est un magasin physique de produits (le dossier contient des coordonnées physiques et une adresse postale). Un " entrepôt " est une union logique de plusieurs "sources" ( inventory_source_stock_link )


 link_id|stock_id|source_code|priority| -------|--------|-----------|--------| 1| 1|default | 1| 

au niveau duquel existe un lien vers le canal de vente ( inventory_stock_sales_channel )


 type |code|stock_id| -------|----|--------| website|base| 1| 

À en juger par la structure des données, différents types de canaux de vente sont supposés, mais par défaut, seule la connexion " stock " - " site Web " est utilisée (le lien vers le site Web est fourni par la base code du site Web).


Un « entrepôt » peut être lié à plusieurs « sources » et une « source » peut être liée à plusieurs « entrepôts » (relation plusieurs-à-plusieurs). Les exceptions sont default'ovye " source " et " entrepôt ". Ils ne se lient pas à d'autres entités (restriction au niveau du code - l'erreur " Impossible d'enregistrer le lien lié à la source par défaut ou au stock par défaut " se bloque). Vous pouvez en savoir plus sur la structure MSI dans Magento 2 dans l'article " Système de gestion d'entrepôt utilisant CQRS et Event Sourcing. Conception ".


J'utiliserai la configuration par défaut et ajouterai toutes les informations d'inventaire à la source default , qui est utilisée dans le canal de vente associé au site Web avec le code de base (correspond à la partie client du magasin - voir 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); } 

Après avoir ajouté des données d'inventaire au produit dans le panneau d'administration, nous obtenons cette image:


image


Médias


Lors de l'ajout "manuel" d'images au produit via le panneau d'administration, les informations pertinentes sont enregistrées dans les tableaux suivants:


  • catalog_product_entity_media_gallery : registre média (images et fichiers vidéo);
  • catalog_product_entity_media_gallery_value : relier les médias aux produits et aux vitrines (localisation);
  • catalog_product_entity_media_gallery_value_to_entity : liaison des médias uniquement aux produits (vraisemblablement le contenu multimédia par défaut du produit);
  • catalog_product_entity_varchar : les rôles qui utilisent l'image sont enregistrés ici;

et les images elles-mêmes sont enregistrées dans le répertoire ./pub/media/catalog/product/x/y/ , où x et y sont les première et deuxième lettres du nom du fichier image. Par exemple, le fichier image.png doit être enregistré sous ./pub/media/catalog/product/i/m/image.png afin que la plateforme puisse l'utiliser comme image lors de la description des produits du catalogue.



Nous enregistrons le fichier multimédia situé dans ./pub/media/catalog/product/ (le processus de placement du fichier dans cet article n'est pas pris en compte):


 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; } 

Lors de l'enregistrement, un nouveau fichier multimédia se voit attribuer un identifiant.



Nous lions le fichier multimédia enregistré avec le produit correspondant pour la vitrine par défaut:


 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); } 


Nous associons le fichier multimédia enregistré au produit correspondant sans référence à une vitrine. On ne sait pas exactement où ces données sont utilisées et pourquoi il est impossible d'accéder aux données de la table précédente, mais cette table existe et les données y sont écrites lors de l'ajout d'une image au produit. Par conséquent, comme ça.


 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


Un fichier multimédia peut être utilisé avec différents rôles (le code de l'attribut correspondant est indiqué entre parenthèses):


  • Base ( image )
  • Petite image ( small_image )
  • Vignette ( thumbnail )
  • Image Swatch ( swatch_image )

La liaison de rôles à un fichier multimédia se produit uniquement dans catalog_product_entity_varchar . Le code de liaison est similaire au code de la section " Attributs de produit de base ".


Après avoir ajouté l'image au produit dans le panneau d'administration, cela se présente comme suit:


image


Les catégories


Tableaux principaux contenant des données par catégorie:


  • catalog_category_entity : registre des catégories;
  • catalog_category_product : association de produits et catégories;
  • catalog_category_entity_* : valeurs des attributs EAV;

Initialement, dans une application Magento vide, le registre des catégories contient 2 catégories (j'ai raccourci les noms des colonnes: 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 catégorie avec id = 1 est la racine de tout le répertoire Magento et n'est pas disponible dans le panneau d'administration ou sur le devant. La catégorie avec id = 2 ( catégorie par défaut ) est la catégorie racine du magasin principal du site Web principal (magasin de site Web principal ) créé lors du déploiement de l'application (voir Admin / Magasins / Tous les magasins ). De plus, la catégorie très racine du magasin à l'avant est également indisponible, uniquement ses sous-catégories.


Étant donné que le sujet de cet article continue d'importer des données produit, je n'utiliserai pas d'enregistrement direct dans la base de données lors de la création de catégories, mais j'utiliserai les classes fournies par Magento lui-même (modèles et référentiels). L'enregistrement direct dans la base de données est utilisé uniquement pour lier le produit importé à la catégorie (la catégorie est mappée par son nom, l'ID de catégorie est extrait lors de la correspondance):


 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); } 

Après avoir ajouté un lien de produit aux catégories "Catégorie 1" et "Catégorie 2", les détails du produit dans le panneau d'administration ressemblent à ceci:


image


Actions supplémentaires


Une fois l'importation des données terminée, vous devez effectuer les étapes supplémentaires suivantes:


  • indexation des données: un appel dans la console ./bin/magento indexer:reindex ;
  • Régénération d'URL pour les produits / catégories: vous pouvez utiliser l'extension " elgentos / régénérer-catalogue-urls "

Produits dans le panneau d'administration après avoir effectué des étapes supplémentaires:


image


et à l'avant:


image


Résumé


Le même ensemble de produits (10 pièces) que dans l'article précédent est importé au moins un ordre de grandeur plus rapidement (1 seconde contre 10). Pour une estimation plus précise de la vitesse, vous avez besoin d'un plus grand nombre de produits - plusieurs centaines, et de préférence des milliers. Néanmoins, même avec une si petite quantité de données d'entrée, on peut conclure que l'utilisation des outils fournis par Magento (modèles et référentiels) de manière significative (souligner - de manière significative !) Accélérer le développement de la fonctionnalité requise, mais de manière significative (souligner - de manière significative !) Réduire la vitesse d'accès des données à la base de données.


En conséquence, l'eau s'est avérée être humide et ce n'est pas une révélation. Cependant, j'ai maintenant du code sur lequel jouer et peut-être tirer des conclusions plus intéressantes.

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


All Articles