Back to Top

Virtual Types in Magento 2

Updated 19 July 2024

Hello Friends,
In this blog, we will learn about the Virtual Types concept in Magento 2.

Along side, you can also have a look at our Magento 2 extensions already crafted by certified Adobe Commerce Developers.

What are Virtual Types in Magento 2?

In Magento 2, Virtual Types allows us to modify existing classes without affecting the other classes and without having to create a new class file(it isn’t necessary.

But if we want then we can create it as well), we can use inject these Virtual Types where we need them.

Searching for an experienced
Magento 2 Company ?
Find out More

In Magento 2, Virtual Types are like a sub-class for an existing class.

Moreover, there is no need to seek elsewhere; avail yourself of the chance to commence your projects with an endorsed Magento 2 development company.

How to use Virtual Types?

In Magento 2, we can mention Virtual Types in the di.xml file inside the <magento-root-dir.>/app/code/Vendor/Module/etc/ directory OR /vendor/VendorName/src/module-name/etc/ directory

OR

<magento-root-dir.>/app/code/Vendor/Module/etc/<frontend Or adminhtml> directory OR /vendor/VendorName/src/module-name/etc/<frontend Or adminhtml> directory.

In the di.xml file, we use the tag to add our custom Virtual Type class.
<virtualType/> accepts two attributes:

[A] name: In this attribute, we set virtualType’s name as value.
[B] type: This attribute contains the existing class name which we use to create the virtualType.

For better understanding, here I have created a di.xml file and added a Virtual Type class using the <virtualType/> tag.
The di.xml file is placed inside the <magento-root-directory>/app/code/Webkul/Module/etc/ directory.

<?xml version="1.0"?>
<!--
/**
 * Webkul Software.
 *
 * @category  Webkul
 * @package   Webkul_Module
 * @author    Webkul Software Private Limited
 * @copyright Webkul Software Private Limited (https://webkul.com)
 * @license   https://store.webkul.com/license.html
 */
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <!--Employee Class-->
    <type name="Webkul\Module\Model\Employee">
        <arguments>
            <argument name="empattribute" xsi:type="array">
                <item name="name" xsi:type="string">Name</item>
                <item name="id" xsi:type="string">Id</item>
            </argument>
        </arguments>
    </type>

    <!--CustomVirtualEmployee virtualType class for Employee class-->
    <virtualType name="CustomVirtualEmployee" type="Webkul\Module\Model\Employee">
    </virtualType>

    <!--FirstEmployee virtualType class for EmpRecord class-->
    <virtualType name="FirstEmployee" type="Webkul\Module\Model\EmpRecord">
        <arguments>
            <argument name="record" xsi:type="array">
                <item name="name_1" xsi:type="string">Jack</item>
                <item name="id_1" xsi:type="string">101</item>
            </argument>
        </arguments>
    </virtualType>

    <!--SecondEmployee virtualType class for EmpRecord class-->
    <virtualType name="SecondEmployee" type="Webkul\Module\Model\EmpRecord">
        <arguments>
            <argument name="record" xsi:type="array">
                <item name="name_2" xsi:type="string">Lusi</item>
                <item name="id_1" xsi:type="string">140</item>
            </argument>
        </arguments>
    </virtualType>

    <!--using virtualType classes FirstEmployee and SecondEmployee within di.xml file-->
    <type name="Webkul\Module\Model\AllRecords">
        <arguments>
            <argument name="allRecords" xsi:type="array">
                <item name="firstEmployee" xsi:type="object">FirstEmployee</item>
                <item name="secondEmployee" xsi:type="object">SecondEmployee</item>
            </argument>
        </arguments>
    </type>
</config>

In the above di.xml, I have used Virtual Types in two ways:

  1. Mentioned the FirstEmployee and SecondEmployee classes, these classes don’t exist in the Module but they will work virtually.

    If we use this class in code then it will not work. We can use these virtualType classes within the di.xml.
  2. Mentioned the CustomVirtualEmployee class and also created this PHP class file in the module.

    If we will create the class, then we can do some other stuff like modifying methods or simply using the class inside PHP code.

Here, I have created the Employee.php class file placed inside the <magento-root-dir.>/app/code/Vendor/Module/Model/ directory. I have created CustomVirtualEmployee virtualType for the Employee class.

<?php
/**
 * Webkul Software.
 *
 * @category  Webkul
 * @package   Webkul_Module
 * @author    Webkul Software Private Limited
 * @copyright Webkul Software Private Limited (https://webkul.com)
 * @license   https://store.webkul.com/license.html
 */
namespace Webkul\Module\Model;

class Employee
{
    /**
     * @var array
     */
    private $empattribute;
    
    /**
     * Construct method
     * 
     * @param array $empattribute
     * @return void
     */
    public function __construct(array $empattribute = [])
    {
        $this->empattribute = $empattribute;
    }

    /**
     * Get Employee Data
     * 
     * @return array
     */
    public function getEmployeeData()
    {
        return $this->empattribute;
    }
}

Now, I have created the CustomVirtualEmployee.php class file placed inside the <magento-root-dir.>/app/code/Vendor/Module/Model/ directory.

<?php
/**
 * Webkul Software.
 *
 * @category  Webkul
 * @package   Webkul_Module
 * @author    Webkul Software Private Limited
 * @copyright Webkul Software Private Limited (https://webkul.com)
 * @license   https://store.webkul.com/license.html
 */
namespace Webkul\Module\Model;

class CustomVirtualEmployee extends \Webkul\Module\Model\Employee
{
    /**
     * @var array
     */
    private $empattribute;
    
    /**
     * Construct method
     * 
     * @param array $empattribute
     * @return void
     */
    public function __construct(array $empattribute = [])
    {
        parent::__construct($empattribute);
        $this->empattribute = $this->getEmployeeData();
        $this->empattribute["addedNewOne"] = $this->getNewAttribute();
    }

    /**
     * Get New Attribute Label
     * 
     * @return string
     */
    public function getNewAttribute()
    {
        return "Salary";
    }

    /**
     * Get All Data
     * 
     * @return array
     */
    public function getAllData()
    {
        return $this->empattribute;
    }
}

Now, I have created the EmpRecord.php class file placed inside the <magento-root-dir.>/app/code/Vendor/Module/Model/ directory. For this class, I have created two virtualTypes FirstEmployee and SecondEmployee.

<?php
/**
 * Webkul Software.
 *
 * @category  Webkul
 * @package   Webkul_Module
 * @author    Webkul Software Private Limited
 * @copyright Webkul Software Private Limited (https://webkul.com)
 * @license   https://store.webkul.com/license.html
 */
namespace Webkul\Module\Model;

class EmpRecord
{
    /**
     * @var array
     */
    private $record;
    
    /**
     * Construct method
     * 
     * @param array $record
     * @return void
     */
    public function __construct(array $record = [])
    {
        $this->record = $record;    
    }

    /**
     * Get Record Data
     * 
     * @return array
     */
    public function getRecord()
    {
        return $this->record;
    }
}

Now, I have created the AllRecords.php class file placed inside the <magento-root-dir.>/app/code/Vendor/Module/Model/ directory. In this class, I have used virtualTypes FirstEmployee and SecondEmployee.

<?php
/**
 * Webkul Software.
 *
 * @category  Webkul
 * @package   Webkul_Module
 * @author    Webkul Software Private Limited
 * @copyright Webkul Software Private Limited (https://webkul.com)
 * @license   https://store.webkul.com/license.html
 */
namespace Webkul\Module\Model;

class AllRecords
{
    /**
     * @var array
     */
    private $allRecords;

    /**
     * Construct method
     * 
     * @param array $allRecords
     * @return void
     */
    public function __construct(array $allRecords = [])
    {
        $this->allRecords = $allRecords;
    }

    /**
     * Get All Records
     * 
     * @return array
     */
    public function getRecords()
    {
        return $this->allRecords;
    }
}

After adding the virtualType in your code, please execute the following command to see the result.

php bin/magento setup:di:compile

See the Result:
To display the result, I created the Controller file Index.php inside the <magento-root-dir.>app/code/Webkul/Module/Controller/Index/ directory and added the following code:

<?php
/**
 * Webkul Software.
 *
 * @category  Webkul
 * @package   Webkul_Module
 * @author    Webkul Software Private Limited
 * @copyright Webkul Software Private Limited (https://webkul.com)
 * @license   https://store.webkul.com/license.html
 */

namespace Webkul\Module\Controller\Index;

use Magento\Framework\App\Action\Action;
use Magento\Framework\App\Action\Context;

class Index extends Action
{
    /**
     * @var \Magento\Framework\Controller\Result\Raw
     */
    protected $rawResultFactory;

    /**
     * @var \Webkul\Module\Model\AllRecords
     */
    protected $empAllRecords;

    /**
     * @var \Webkul\Module\Model\CustomVirtualEmployee
     */
    protected $virEmp;

    /**
     * Initialize dependencies
     * 
     * @param Context $context
     * @param \Webkul\Module\Model\AllRecords $empAllRecords
     * @param \Webkul\Module\Model\CustomVirtualEmployee $virEmp
     * @param \Magento\Framework\Controller\Result\Raw $rawResultFactory
     * @return void
     */
    public function __construct(
        Context $context,
        \Webkul\Module\Model\AllRecords $empAllRecords,
        \Webkul\Module\Model\CustomVirtualEmployee $virEmp,
        \Magento\Framework\Controller\Result\Raw $rawResultFactory
    ) {
        $this->virEmp   = $virEmp;
        $this->rawResultFactory = $rawResultFactory;
        $this->empAllRecords = $empAllRecords;
        parent::__construct($context);
    }

    /**
     * Execute method to print data
     *
     * @return \Magento\Framework\Controller\Result\Raw
     */
    public function execute()
    {
        $result = $this->rawResultFactory;
        $result->setHeader('Content-Type', 'text/html');

        $empRecords = $this->empAllRecords->getRecords();

        $dataStr = "";

        $dataStr .= "<h3>Result for Case 1: Used &lt;virtualType&gt; within di.xml file</h3>";

        foreach ($empRecords as $index=>$each) {
            $dataStr .= $index."::".json_encode($each->getRecord())."<br>";
        }

        $employeeAttrs = $this->virEmp->getAllData();
    
        $dataStr .= "<h3>Result for Case 2: Created &lt;virtualType&gt; for class Webkul\Module\Model\Employee and created CustomVirtualEmployee class</h3>";

        foreach ($employeeAttrs as $key=>$value) {
            $dataStr .= $key."::".$value."<br>";
        }

        $result->setContents( $dataStr);

        return $result;
    }
}

When you will run the controller on browser, you will see the result as below image:

VirtualTypeResult

Virtual types can be used to modify or replace the constructor argument of an existing class.


In <virtualType/>, in the name attribute, we can pass the ClassName or complete path of the virtual class as the following example in <magento-root-dir.>/vendor/magento/module-catalog/adminhtml/etc/di.xml file.

<virtualType name="Magento\Catalog\Ui\DataProvider\Product\ProductCollectionFactory" type="Magento\Catalog\Model\ResourceModel\Product\CollectionFactory">
    <arguments>
        <argument name="instanceName" xsi:type="string">\Magento\Catalog\Ui\DataProvider\Product\ProductCollection</argument>
    </arguments>
</virtualType>

Download the Full Code.

For a personalized touch, engage and hire Magento developers who are focused on delivering exceptional results for your custom e-commerce projects.

Hope this will be helpful. Thanks 🙂

. . .

Leave a Comment

Your email address will not be published. Required fields are marked*


Be the first to comment.

Back to Top

Message Sent!

If you have more details or questions, you can reply to the received confirmation email.

Back to Home