Product Types: Handling Automatic Changes in Magento ® 2

img_Magneto_2_tips

Sometimes for whatever reason, content managers of Magento ® stores face the problem of converting products from Simple to Virtual or vice versa, changing their attribute sets and so on. Most often, this task is addressed to the developers of Magento ®. But in Magento ® 2, a very interesting and useful feature was added and we’d like to review it here.

Product Types in Magento ® 2: Automatic Change

In Magento ® 1 the content manager would have to create new products manually, to make them copies of the original products, and so on, or another variant would be asking the developers to make the necessary changes in the code. In Magento ® 2 this can be done in a much easier way.

How to convert a product. Information for users

In Magento ® 2 you need to go to Admin > Products > Catalog and click on the arrow on the right side of the “Add Product” button.

2_Magneto2_add_products

After selecting the necessary product type, Magento ® shows you the page where you can edit the new product with predefined options and/or new sections of the selected type. If you select the Simple Product types, or simply click on the "Add Product" you will see a Simple product edit page. Pay attention to the following 3 things here:

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

3_item_has_waight

These three sections determine the type of product.

Magento ® 2 can automatically (when you save the product) change both new and existing types of products, so you can change the following types:

  • Simple
  • Virtual
  • Downloadable
  • Configurable

Let’s review in details what you should do for this.

Simple and Virtual product types

If you set the weight as “The item has no weight” (it means choosing the value “no weight”, and not removing the value from the input field) then the product will be saved as Virtual product, but as soon as you go back to the product edit page and specify that the product has the weight, it will be converted to Simple product after it is saved.

Downloadable products

4_add_link

To convert Simple Product into Downloadable Product, you need to set “Weight” as “The item has no weight” and then, in the Downloadable Information section, you need to add one or more links. To convert this product back into a Simple product you should set “Weight” as “The item has weight” and save your data.

Configurable products

To convert the Simple product into a Configurable product, you need to add any child item as it is described above. And to convert it again into a Simple product you need to delete all configurations and click on “Save” button. As you understand, this means that in Magento ® 2 there cannot be a Configurable product without child items (as it was in Magento ® 1).

Please keep in mind that this logic does not work correctly for now. By content manager’s mistake, there can be Configurable product without child items in Magento ® 2. It is not a standard situation, and if you need more technical advice on this issue, please contact us by e-mail at support@gomage.com.

Before you use this conversion feature for configurable items you need to keep in mind that the price getting principle has been changed for Configurable products in Magento ® 2. In Magento ® 1 Configurable products have their own price, and completely ignore the price of related products.

Magento ® 2 uses a different approach. Configurable products use the calculated price of the linked child products. So, when you delete all linked items, a Configurable product is converted into a Simple product, but if you remove all associated products from the Grid page in the admin panel, then the product is not converted (we will explain why this is so later).

In other words, the product is Configurable only if it has children, otherwise, it is "converted" into a Simple product and that can lead to some unexpected issues in the code, especially if you work with the import products.

In Magento ® 2.1, some of these errors were fixed and indirect reference to the method of $ product-> getFinalPrice () there is an exception.

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

Information for developers

Now let's review how it works. We would like to start with 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 simple class that has 2 methods: __construct 2 and processProduct. The important thing is the second method - processProduct($product). As you can see, it checks whether there is a type of transferred product in the array of compatible types and, if the product has the weight - it is the Simple product if not, it is a Virtual product.

As you can guess, in Magento ® 2.1. the following types are compatible: Simple, Virtual, Downloadable, and Configurable.

They all pass through di.xml.

...
<type name="Magento\Catalog\Model\Product\TypeTransitionManager">
<arguments>
<argument name="compatibleTypes" xsi:type="array">
<item name="simple" xsi:type="const">Magento\Catalog\Model\Product\Type::TYPE_SIMPLE</item>
<item name="virtual" xsi:type="const">Magento\Catalog\Model\Product\Type::TYPE_VIRTUAL</item>
</argument>
</arguments>
</type>
...

That means that only these types can be converted into Simple or Virtual products.

As mentioned above, a Simple product can be converted into a Configurable one and into a Downloadable product as well. The most important things are in the following classes (plugins):

  • Magento\Downloadable\Model\Product\TypeTransitionManager\Plugin\Downloadable
  • Magento\ConfigurableProduct\Model\Product\TypeTransitionManager\Plugin\Configurable

Magento\Downloadable\Model\Product\TypeTransitionManager\ Plugin\Downloadable::arounProcessProduct verifies that the product types are Simple, Virtual, Downloadable, the product has no weight and downloadable data exists in POST request. If so, it is a Downloadable product, otherwise, the original method is run.

...
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 – this plugin changes the type of product into "configurable" if there is data with a key "attributes" in the request, otherwise the original method is run.

...
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 it works

If you try to save the items in the admin panel, as described above, it will change its type to one of those mentioned above because of this code, but if you just try to save the product model via the save() method that will not change anything. The point is the controller which processes the product saving operation.

Magento\Catalog\Controller\Adminhtml\Product\Save::execute() uses a class instance Magento\Catalog\Model\Product\TypeTransitionManager.

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

$product->save();
...

As you can see, the product conversion is not linked to saving() method and it is called immediately before the creation of the product.

We hope the new Magento ® 2 feature and its principle of work have become a little clearer for both users and developers. You can simply add new product types, if you ever need to allow users the ability to convert from one type to another, the developers will only have to add a few lines in your di.xml file and create your own plugin.