How to create a new product type in Magento 2?

By default, Magento 2 provides 6 product types including simple, grouped, configurable, virtual, bundled and downloadable having unique behaviours and attributes. These default product types cover the majority of the E-commerce needs by supporting the wide range of requirements.

There may arise circumstances when you may be not satisfied with the available types and your products might not be suitable for those default product types.

In general, when a product class has distinct behaviour or attributes, it should be represented by its own custom product type. This allows the product type to have complex and custom logic and presentation, with no impact on other product types, ensuring that default product types can continue to function as intended.

In this post, I will be guiding you on how to create a new Magento 2 product type.

Module Registration File

Create the app/code/Aureatelabs/NewProductType/registration.php file

<?php

\Magento\Framework\Component\ComponentRegistrar::register(
   \Magento\Framework\Component\ComponentRegistrar::MODULE,
   'Aureatelabs_NewProductType',
   __DIR__
);

Config XML

Create the app/code/Aureatelabs/NewProductType/etc/module.xml file

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
   <module name="Aureatelabs_NewProductType" setup_version="1.0.0" />
</config>

Defining a custom product type involves an XML declaration of product_types.xml.

Create the app/code/Aureatelabs/NewProductType/etc/product_types.xml file

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Catalog:etc/product_types.xsd">
   <type name="new_product_type" label="New Product Type" modelInstance="Aureatelabs\NewProductType\Model\Product\Type\NewProductType" indexPriority="60" sortOrder="80" isQty="true">
   </type>
</config>

In type, the node has three required attributes.

  • name: new product type code
  • label: new product type name
  • modelInstance: new product type model

You can also specify the below attributes

  • indexPriority: priority index
  • isQty: whether it has a quantity
  • sortOrder: position number in the sort list

Although there are many other nodes and attributes that can influence the product type definition, this is an illustration of the simplest case.

Also, we are capable to override such internal models:

  • priceModel: the model of a price
  • indexerModel: the model of indexing logic
  • stockIndexerModel: the model of stock indexing

Product Type Model

Each product type instance is associated with an instance of the corresponding product type model. This model has the opportunity to modify product type behaviour and attributes and is called during several product manipulation processes. In short, you can apply your custom logic in the product type model.

Create the app/code/Aureatelabs/NewProductType/Model/Product/Type/NewProductType.php file

<?php

namespace Aureatelabs\NewProductType\Model\Product\Type;

class NewProductType extends \Magento\Catalog\Model\Product\Type\AbstractType {

   /**
    * Delete data specifically for new product type
    *
    * @param \Magento\Catalog\Model\Product $product
    * @return void
    */
   public function deleteTypeSpecificData(\Magento\Catalog\Model\Product $product)
   {
        // method intentionally empty
   }

}

A product model must inherit from the \Magento\Catalog\Model\Product\Type\AbstractType class. This class has only one abstract method and it is deleteTypeSpecificData. This specific method is called while saving a product instance if its type has changed, and gives the original product type the opportunity to clean up any type-specific data before the type change is finalized.

Unless the new custom product type has such type-specific data, the method can be overridden with an empty method.

Core Product Types

It’s often the case that a custom product type behaves like and has similar requirements for a core product type. In that case, it will be useful to extend one of the core product types instead of the abstract type directly.

The following product type classes are available in the core Magento.

  • Simple: \Magento\Catalog\Model\Product\Type\Simple
  • Virtual: \Magento\Catalog\Model\Product\Type\Virtual
  • Configurable: \Magento\ConfigurableProduct\Model\Product\Type\Configurable
  • Grouped: \Magento\GroupedProduct\Model\Product\Type\Grouped
  • Downloadable: \Magento\Downloadable\Model\Product\Type
  • Bundle: \Magento\Bundle\Model\Product\Type
  • Giftcard (Enterprise Edition Only): \Magento\GiftCard\Model\Catalog\Product\Type\Giftcard

Result

Once the product type is declared in XML and its associated product type model is created, products of the new type can be shown in the admin.

Additional Steps

Having successfully created a new product type, now there are some general steps (optional) to utilize it.

Associate With Common Attributes

Since product attributes can be scoped to relevant product types, any core attributes which are already scoped will not apply to the new product type. Thus attributes that are indeed relevant will need to be associated with the new product type. Hence you have to create one Install data script in order to make these changes.

Create the app/code/Aureatelabs/NewProductType/Setup/InstallData.php file

<?php
namespace Aureatelabs\NewProductType\Setup;

use Magento\Eav\Setup\EavSetup;
use Magento\Eav\Setup\EavSetupFactory;
use Magento\Framework\Setup\InstallDataInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;


class InstallData implements InstallDataInterface
{
   private $eavSetupFactory;

   public function __construct(EavSetupFactory $eavSetupFactory)
   {
       $this->eavSetupFactory = $eavSetupFactory;
   }

   public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
   {
       /** @var EavSetup $eavSetup */
       $eavSetup = $this->eavSetupFactory->create(['setup' => $setup]);

       //associate these attributes with new product type
       $fieldList = [
           'price',
           'special_price',
           'special_from_date',
           'special_to_date',
           'minimal_price',
           'cost',
           'tier_price',
           'weight',
       ];
       // make these attributes applicable to new product type
       foreach ($fieldList as $field) {
           $applyTo = explode(
               ',',
               $eavSetup->getAttribute(\Magento\Catalog\Model\Product::ENTITY, $field, 'apply_to')
           );
           if (!in_array('new_product_type', $applyTo)) {
               $applyTo[] = 'new_product_type';
               $eavSetup->updateAttribute(
                   \Magento\Catalog\Model\Product::ENTITY,
                   $field,
                   'apply_to',
                   implode(',', $applyTo)
               );
           }
       }
   }
}

Composite Products

When you are adding child products to a composite product type like configurable, grouped or bundle, it will only show products where the associated product type has explicitly declared its eligibility to be the child of a composite product.

So new product types can allow themselves to be children of composite product types by adding a composableTypes node in product_types.xml

Create app/code/Aureatelabs/NewProductType/etc/product_types.xml file.

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Catalog:etc/product_types.xsd">
   <type name="new_product_type" label="New Product Type" modelInstance="Aureatelabs\NewProductType\Model\Product\Type\NewProductType" indexPriority="60" sortOrder="80" isQty="true">
   </type>
   <composableTypes>
       <type name="new_product_type" />
   </composableTypes>
</config>

Custom Attributes Node

The customAttributes nodes can be used when Magento wants to get a list of product types that comply with the condition.

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Catalog:etc/product_types.xsd">
   <type name="new_product_type" label="New Product Type" modelInstance="Aureatelabs\NewProductType\Model\Product\Type\NewProductType" indexPriority="60" sortOrder="80" isQty="true">
       <customAttributes>
           <attribute name="refundable" value="true"/>
       </customAttributes>
   </type>
</config>

Inside this node, we can declare some attributes nodes:

  • refundable: It will check the product type, it can be refunded or not. By default Magento CE, the downloadable and virtual product cannot be refunded (In Magento EE added Gift product). The product type in sales order item table will be used for checking the refundable product.
  • taxable: It will check the product type, it is taxable or not.
  • Is_real_product: It is used for checking "it is a real product or not". Group product, Downloadable Product, Virtual Product are not real products.
  • is_product_set: checks whether product type is a set of products. In group product is_product_set value is set to true.

Customize Product type models

Creating a new product type is only the beginning. Now that it’s defined, the new product type provides an enormous capacity for scoped customization.

Product Type Model Methods

The behaviour of products can be significantly influenced by overriding methods in the product type model. This allows custom logic to be applied within the product type. So you don’t have to create any observer or plugin to do that.

Some examples of such methods:

  • beforeSave() : This method runs during the beforeSave() products of the given type.
  • save(): This method runs during the afterSave() products of the given type.
  • isSalable(): This method allows a custom product type to customize product saleability.
  • _prepareProduct(): This method provides a hook to interact with the information by request when a product is initially added to the cart.
  • isVirtual(): By default it is false. If your product type is like virtual type then you can use this function and return true.

Price Model

In addition to the product type model, a module can specify a price model in the product type definition XML.

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Catalog:etc/product_types.xsd">
   <type name="new_product_type" label="New Product Type" modelInstance="Aureatelabs\NewProductType\Model\Product\Type\NewProductType" indexPriority="60" sortOrder="80" isQty="true">
       <priceModel instance="Aureatelabs\NewProductType\Model\Product\Price" />
   </type>
</config>

The price model should extend \Magento\Catalog\Model\Product\Type\Price. This class has no abstract methods which must be implemented but allows extending classes to interact with nearly every aspect of price calculation.

<?php

namespace Aureatelabs\NewProductType\Model\Product;

class Price extends \Magento\Catalog\Model\Product\Type\Price {
    //your custom code here
}

You can also add indexerModel and stockIndexerModel like priceModel in the product type definition XML.

  • Share :