Magento is an e-commerce solution, meaning it focuses more on product sales than on related sales inventory, logistics, or financial accounting. For those supporting functions, other applications (like ERP systems) are more suitable. Therefore, it is quite common in the practice of using Magento to face the task of integrating the store with these other systems (for example, with 1C).
In general, integration can be boiled down to the replication of data regarding:
- catalog (products, categories);
- inventory data (product quantities in warehouses and prices);
- customers;
- orders;
Magento offers a separate class of objects for manipulating data in the database — . Due to the specifics of Magento, adding data to the database through repositories is easily coded but, shall we say, not done quickly. In this publication, I will examine the main stages of programmatically adding a product in Magento 2 using the 'classical' method — with the use of repo classes.
Customers and orders are usually replicated in the other direction — from Magento to external ERP systems. So it's simpler with them; on the Magento side, you just need to select the relevant data, and then — 'the bullets have left our side«.
Principles of writing data to the database
Currently, creating objects saved in the database programmatically in Magento is done through :
function __construct (MagentoCmsModelBlockFactory $blockFactory) {
$this->blockFactory = $blockFactory;
}
/** @var MagentoCmsModelBlock $block * */
$block = $this->blockFactory->create();while writing to the database is done through :
function __construct (MagentoCmsApiBlockRepositoryInterface $blockRepo) {
$this->blockRepo = $blockRepo;
}
$this->blockRepo->save($block);The approach using 'Factory' and 'Repository' can be applied to all the main models in the Magento 2 domain.
Basic product information
I am examining the data structure corresponding to version Magento 2.3. The most basic product information is found in the catalog_product_entity (product registry):
entity_id
attribute_set_id
type_id
sku
has_options
required_options
created_at
updated_atI will limit myself to one product type (type_id='simple'), a set of default attributes (attribute_set_id=4), and ignore the attributes has_options and required_options. Since the attributes entity_id, created_at and updated_at are generated automatically, in essence, we only need to provide sku. I do it like this:
/** @var MagentoCatalogApiDataProductInterfaceFactory $factProd */
/** @var MagentoCatalogApiProductRepositoryInterface $repoProd */
/** @var MagentoCatalogApiDataProductInterface $prod */
$prod = $factProd->create();
$prod->setAttributeSetId(4);
$prod->setTypeId('simple');
$prod->setSku($sku);
$repoProd->save($prod);and receive an exception:
The "Product Name" attribute value is empty. Set the attribute and try again.I am adding the product name to the request and receiving a message that the attribute is missing. Price. After adding the price, the product is saved in the database:
$prod = $factProd->create();
$prod->setAttributeSetId(4);
$prod->setTypeId('simple');
$prod->setSku($sku);
$prod->setName($name);
$prod->setPrice($price);
$repoProd->save($prod);The product name is stored in the varchar attributes table (catalog_product_entity_varchar), the price is in the table catalog_product_entity_decimal. Before adding the product, it is advisable to explicitly indicate that we are using the admin panel for data import:
/** @var MagentoStoreModelStoreManagerInterface $manStore */
$manStore->setCurrentStore(0);Additional attributes
Handling additional product attributes with Magento is a pleasure. The EAV data model for core entities (see table eav_entity_type) is one of the key features of this platform. Simply add the corresponding attributes to the product model:
$prodEntity->setData('description', $desc);
$prodEntity->setData('short_description', $desc_short);
// or
$prodEntity->setDescription($desc);
$prodEntity->setShortDescription($desc_short);and when saving the model through the repo object:
$repoProd->save($prod);additional attributes will also be saved in the corresponding database tables.
Inventory data
Simply put — the quantity of the product in stock. In Magento 2.3, the database structures describing the format of inventory data storage from what it was before. Nevertheless, adding the quantity of the product in stock through the product model is not much more complicated than adding other attributes:
/** @var MagentoCatalogModelProduct $prodEntity */
/** @var MagentoCatalogApiProductRepositoryInterface $repoProd */
$inventory = [
'is_in_stock' => true,
'qty' => 1234
];
$prodEntity->setData('quantity_and_stock_status', $inventory);
$repoProd->save($prodEntity);Media
Typically, the media accompanying a product for the customer in a store (e-commerce) differs from the media for the employee in the internal accounting system (ERP). In the first case, it's advisable to show the 'product face', while in the second, simply giving a general overview of the product is sufficient. However, transferring at least the primary image of the product is a fairly common case when importing data.
When adding an image through the admin panel, the image is first saved in a temporary directory (./pub/media/tmp/catalog/product) and only upon saving the product is it moved to the media catalog (./pub/media/catalog/product). Also, when adding through the admin panel, the image is assigned tags 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);For some reason, the media is tied only after the product is initially saved and retrieved from the repository again. The attribute label When adding a record to the product media gallery (otherwise we receive an exception Undefined index: label in .../module-catalog/Model/Product/Gallery/CreateHandler.php on line 516).
Categories
Often, the structure of store categories and the backend application or the placement of products within them can differ significantly. The strategies for transferring data about categories and the products within them depend on numerous factors. In this example, I adhere to the following:
- The backend categories and the store are matched by name;
- if a category is imported that does not exist in the store, it is created under the root category (
Default Category) and its further positioning in the store catalog is expected to be done manually; - The binding of a product to a category occurs only when it is created in the store (during the first import);
The main information about the category is located in the table catalog_category_entity (category catalog). Creating a category in Magento:
/** @var MagentoCatalogApiDataCategoryInterfaceFactory $factCat */
/** @var MagentoCatalogApiCategoryRepositoryInterface $repoCat */
$cat = $factCat->create();
$cat->setName($name);
$cat->setIsActive(true);
$repoCat->save($cat);The binding of a product to a category is done by the category ID and product SKU:
/** @var MagentoCatalogModelCategoryProductLinkFactory $factCatProdLink */
/** @var MagentoCatalogApiCategoryLinkRepositoryInterface $repoCatLink */
$link = $factCatProdLink->create();
$link->setCategoryId($catMageId);
$link->setSku($prodSku);
$repoCatLink->save($link);Total
Writing code to add a product to Magento 2 programmatically is quite straightforward. I have consolidated everything mentioned above into a demo module "". The module has only one console command fl32:import:prod, which imports products described in the JSON file "«:
[
{
"sku": "...",
"name": "...",
"desc": "...",
"desc_short": "...",
"price": ...,
"qty": ...,
"categories": ["..."],
"image_path": "..."
}
]The images for import are located in the directory ./etc/data/img.
The time to import 10 products this way is approximately 10 seconds on my laptop. If we develop this thought further, it’s not hard to conclude that about 3600 products can be imported per hour, and importing 100K products might take around 30 hours. Replacing the laptop with a server can somewhat alleviate the situation. It can even improve performance substantially. But not exponentially. Perhaps this relatively slow speed is one of the reasons for the emergence of the project .
A radical solution to increase import speed could be direct writing to the database, but in this case, all the "goodies" related to Magento's extensibility are lost—you have to implement everything "extended" yourself. However, it's worth it. If possible, I will consider the direct database writing approach in the next article.
Source: habr.com
