Magento – Injecting object through virtual type not working

dimagento2virtualtype

I am using this config in di.xml

 <virtualType name="ourFirstVirtualType" type="Test\Testmod\Model\Newmodelclass">
        <arguments>
            <argument name="test_object" xsi:type="object">Test\Testmod\Model\Example2</argument>
        </arguments>
    </virtualType>

    <type name="Test\Testmod\Model\Modelclass">
        <arguments>
            <argument name="the_object" xsi:type="object">ourFirstVirtualType</argument>
        </arguments>
    </type>

This is the Modelclass

<?php

namespace Test\Testmod\Model;
class Modelclass
{
    public $testProperty;
    public function __construct(Newmodelclass $the_object)
    {
        $this->testProperty = $the_object;
    }
}

and this is Newmodelclass

<?php

namespace Test\Testmod\Model;
class Newmodelclass
{
    public $newTestProperty;
    public function __construct(Againnewmodelclass $test_object)
    {
        $this->newTestProperty = $test_object;
    }
}

Now I am trying to test virtual type with this controller

<?php
namespace Test\Testmod\Controller\Index;

use Test\Testmod\Model\Modelclass;
use Test\Testmod\Model\Newmodelclass;
use Test\Testmod\Model\Againnewmodelclass;

class Index extends \Magento\Framework\App\Action\Action
{
    public function __construct(
        \Magento\Framework\App\Action\Context $context,
        Modelclass $Modelclass,
        Newmodelclass $Newmodelclass,
        Againnewmodelclass $Againnewmodelclass
    ) {
        parent::__construct($context);
        $this->Modelclass = $Modelclass;
        $this->Newmodelclass = $Newmodelclass;
        $this->Againnewmodelclass = $Againnewmodelclass;
    }

    public function execute()
    {  
        echo "<pre>";
        var_dump($this->Modelclass);
        var_dump($this->Newmodelclass);
        var_dump($this->Againnewmodelclass);
        die;
    }

}

I think this should work correct right but giving this error instead

PHP Fatal error: Uncaught TypeError: Argument 1 passed to Test\Testmod\Model\Newmodelclass::__construct() must be an instance of Test\Testmod\Model\Againnewmodelclass,
instance of Test\Testmod\Model\Example2 given, called in /var/www/html/magento226/vendor/magento/framework/ObjectManager/Factory/AbstractFactory.php on line 111
and defined in /var/www/html/magento226/app/code/Test/Testmod/Model/Newmodelclass.php:7

Best Answer

Let's break down the dependency chain going from controller.

First argument is \Magento\Framework\App\Action\Context $context which is core object and will work as expected provided nothing has been altered.

The next argument that the DI will want to inject is Modelclass $Modelclass, which resolves to Test\Testmod\Model\Modelclass as defined in use statement. Now the DI will try to instantiate an object of this class. It will look at its constructor and will see there is 1 argument defined there, namely Newmodelclass $the_object. In regular situation where there is no additional configuration in di.xml this class would be resolved to Test\Testmod\Model\Newmodelclass by using the same namespace as there is no use statement.

However you have prepared a config in di.xml stating that the_object argument will be resolved to ourFirstVirtualType. So in this case DI will try to instantiate that and will resolve this name to Test\Testmod\Model\Newmodelclass as defined in di.xml. So far so good. We have correctly found the class to inject into Test\Testmod\Model\Modelclass.

Now DI will proceed to instantiating Test\Testmod\Model\Newmodelclass and it will read constructor argument of $test_object being locked at Againnewmodelclass (which by namespace is Test\Testmod\Model\Againnewmodelclass). However in di.xml you've defined that when instantiating virtual type of ourFirstVirtualType you want to pass as test_object argument class Test\Testmod\Model\Example2. This will then be used and the DI constructor will create an instance of Test\Testmod\Model\Example2 and inject it to Test\Testmod\Model\Newmodelclass.

But here is a problem. To make it work Test\Testmod\Model\Example2 must be of Test\Testmod\Model\Againnewmodelclass type or a descendant of it and in your case it seems it isn't. Either make Test\Testmod\Model\Example2 a child of Test\Testmod\Model\Againnewmodelclass or change the constructor definition in Test\Testmod\Model\Newmodelclass to use an interface or a class type that will work with using Test\Testmod\Model\Example2 as an argument.

Related Topic