Automatically change product types in Magento 2

  • Tutorial
Sometimes for one reason or another, content managers need to convert a particular product from Simple to Virtual, or vice versa, change the set attribute to it, and so on ... Most often, this falls on the shoulders of developers. And here I would like to describe 1 new and very interesting feature of Magento 2, which is not described in the official documentation.

To change the type of product in Magento 1. * content managers would have to create new products manually, make copies of the original products, and so on, or ask developers to do this through code. Magento 2. * introduces a new “feature” to achieve this goal. Before you deal with it (I will call it “auto conversion”), you should keep in mind a few points to get the right results.

Auto conversion from a user perspective


Let's look in the admin panel, or rather how it works from the point of view of the content manager. As before, you can choose the type of new product before you create it.

In Magento 1, this is displayed on Admin> Catalog> Manage Products> Page for selecting the attribute of the set and type of product after clicking on “Add Product”.


In Magento 2, for this, on the Admin> Products> Catalog page, click on the arrow to the right of the "Add Product" button.


After selecting the type you need, Magento will show you the edit page for a new product with predefined options and / or new sections for the selected type. If you select the Simple product product type or simply click on “Add Product” you will see the Simple product editing page, but pay attention to the following 3 things:

  1. Weight Attribute
  2. Section Configurations
  3. Downloadable Information Section

These 3 sections determine the type of product.

Magento 2 can automatically (upon saving) change product types for both new and existing products of the following products:

Consider what needs to be done for this.

Simple and Virtual products

If you set the weight to “Weight” = “The item has no weight” (namely, there is no weight, and do not just remove the value from the input field), the product will be saved as a Virtual product, but as soon as you return to the product editing page and indicate that the product has weight, it will be converted to a Simple product immediately after saving.

Downloadable product

In order to turn a Simple product into a Downloadable product you need to set “Weight” = “The item has no weight” and add 1 or more links in the “Downloadable Information” section below. To turn this product back into a Simple product, just set “Weight” = “The item has weight” and save the product.

Configurable Product

To turn a Simple product into a Configurable product, just add any subsidiary product to it as described above. And in order to make it Simple again, just delete all the configurations and click save. As you already understood, this means that in Magento 2 there cannot be a Configurable product without children (as it was in Magento 1).

That's all.

On a note


Before using auto conversion to a configurable product, you need to keep in mind that in Magento 2 the principle of obtaining prices from a Configurable product has been changed. In Magento 1 Configurable, products had their own price and completely ignored the price of related products. Magento 2 takes a different approach. Now Configurable products use a calculated price from attached children. The flip side of the coin is that when you remove all attached products, the product is converted to a Simple product, but if you remove all products associated with it from the Grid page in the admin panel, the product will not be converted (why this is so, I will describe later). Given the new logic, the fact that the product is Configurable only if it has children, otherwise it is "converted" into a Simple product,
* In Magento 2.1, some of these errors have been fixed, and when you directly call the $ product-> getFinalPrice () method, an exception is now thrown
Fatal error:  Uncaught exception 'Magento\Framework\Exception\LocalizedException' with message 'Configurable product "…sku…" does not have sub-products' in vendor\magento\module-configurable-product\Pricing\Price\ConfigurablePriceResolver.php:52


For developers


Now let's talk about how exactly it all works. I would like to start with the most important thing , with the class Magento \ Catalog \ Model \ Product \ TypeTransitionManager .
...
public function __construct(
        \Magento\Catalog\Model\Product\Edit\WeightResolver $weightResolver,
        array $compatibleTypes
    ) {
        $this->compatibleTypes = $compatibleTypes;
        $this->weightResolver = $weightResolver;
    }
    public function processProduct(Product $product)
    {
        if (in_array($product->getTypeId(), $this->compatibleTypes)) {
            $product->setTypeInstance(null);
            $productTypeId = $this->weightResolver->resolveProductHasWeight($product)
                ? Type::TYPE_SIMPLE
                : Type::TYPE_VIRTUAL;
            $product->setTypeId($productTypeId);
        }
    }
...

This is a fairly simple class that has 2 methods __construct and processProduct. All the magic is in the second method, processProduct ($ product). As you can see, it checks whether the type of the transferred product is in an array of compatible types and if the product has weight, then it is a Simple product, if not, then a Virtual product.

In Magento 2.1. you guessed it, the following types are compatible:
  • Simple
  • Virtual
  • Downloadable
  • Configurable

All of them are transmitted through di.xml
...
Magento\Catalog\Model\Product\Type::TYPE_SIMPLEMagento\Catalog\Model\Product\Type::TYPE_VIRTUAL
...

This means that only these types can be converted to Simple or Virtual products.

As mentioned above, a Simple product can be converted to a configurable and downloadable product in the same way. All magic in the following classes (plugins):

Magento \ Downloadable \ Model \ Product \ TypeTransitionManager \ Plugin \ Downloadable :: aroundProcessProduct () verifies that the product type is Simple, Virtual, Downloadable, data with the downloadable key has been sent from the frontend and that this product has no weight. If so, then it is Downloadable, otherwise the original method is launched.
...
public function aroundProcessProduct(
        \Magento\Catalog\Model\Product\TypeTransitionManager $subject,
        Closure $proceed,
        \Magento\Catalog\Model\Product $product
    ) {
        $isTypeCompatible = in_array(
            $product->getTypeId(),
            [
                \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE,
                \Magento\Catalog\Model\Product\Type::TYPE_VIRTUAL,
                \Magento\Downloadable\Model\Product\Type::TYPE_DOWNLOADABLE
            ]
        );
        $downloadableData = $this->request->getPost('downloadable');
        $hasDownloadableData = false;
        if (isset($downloadableData)) {
            foreach ($downloadableData as $data) {
                foreach ($data as $rowData) {
                    if (empty($rowData['is_delete'])) {
                        $hasDownloadableData = true;
                        break 2;
                    }
                }
            }
        }
        if ($isTypeCompatible && $hasDownloadableData && !$this->weightResolver->resolveProductHasWeight($product)) {
            $product->setTypeId(\Magento\Downloadable\Model\Product\Type::TYPE_DOWNLOADABLE);
            return;
        }
        $proceed($product);
    }
...


Magento \ ConfigurableProduct \ Model \ Product \ TypeTransitionManager \ Plugin \ Configurable :: aroundProcessProduct () the plugin changes the product type to “configurable” if the request contains data with the “attributes” key, otherwise the original method starts.
...
public function aroundProcessProduct(
        \Magento\Catalog\Model\Product\TypeTransitionManager $subject,
        Closure $proceed,
        \Magento\Catalog\Model\Product $product
    ) {
        $attributes = $this->request->getParam('attributes');
        if (!empty($attributes)) {
            $product->setTypeId(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE);
            return;
        }
        $proceed($product);
    }
...

How does it all work


If you try to save the product from the admin panel, as described above, it will change its type to 1 of the above thanks to this code, but if you just try to save the product model through the save () method, nothing will change. The point is the controller that handles the storage of goods.
Magento \ Catalog \ Controller \ Adminhtml \ Product \ Save :: execute () uses an instance of the class Magento \ Catalog \ Model \ Product \ TypeTransitionManager

...
$product = $this->initializationHelper->initialize($this->productBuilder->build($this->getRequest()));
$this->productTypeManager->processProduct($product);
…
$product->save();
...

As you can see, product conversion is not tied to the save () method in any way and is called immediately before creating the product.

Instead of a conclusion


For users and developers, I think it’s a little clearer how exactly the new feature of Magento 2 works. It amazes me that for the entire time Magento 2 has existed, they have not written about it anywhere.
I would also like to add that in Magento 2 it’s quite simple to add new types of goods and if you may need to allow users to convert them among themselves, then you just need to add a couple of lines to the di.xml of your module and create your own plugin.

Also popular now: