Magento 2 – Understanding Dependency Injection

dependency-injectionmagento2

I hope this is the correct place to ask the question for this. I read the tutorial about DI for Magento 2, I almost get it the concept. How Magento 2 use di in the framework. After reading I get to know that to use di we need to inject our object into the constructor.

But after reading di.xml files of core magento 2 I am little bit confusion, somewhere I see that,

<type name="Magento\Quote\Model\Quote\Address">
        <arguments>
            <argument name="addressConfig" xsi:type="object">Magento\Customer\Model\Address\Config\Proxy</argument>
            <argument name="secondConfig" xsi:type="object">Magento\Customer\Model\Address\Config\Proxy</argument>
            <argument name="thirdConfig" xsi:type="object">Magento\Customer\Model\Address\Config\Proxy</argument>
            ............................
        </arguments>
    </type>

I am not getting why this is written like this? Also, If initialized the Magento\Quote\Model\Quote\Address object then I have access to 3 objects ?so can anyone explain me about this?

EDIT

This is I am using in my custom extension,

  <type name="Vendor\Module\Model\Mymodel">
        <arguments>
            <argument name="dataSource" xsi:type="array">
                <item name="name1" xsi:type="object">Vendor\Module\Model\ModelClass1</item>
                <item name="name2" xsi:type="object">Vendor\Module\Model\ModelClass2</item>
            </argument>
        </arguments>
    </type>

So If Initialize Vendor\Module\Model\Mymodel in my another class constructor like,

protected $myModel;


public function __construct(
    ...
    \Vendor\Module\Model\Mymodel $myModel,  
    ....
) {
    ....
    $this->myModel = $myModel;      
    ....
}

SO If I use $this->myModel in my method then i can have access of ModelClass1 or ModelClass2 ?

Best Answer

magento2 DI system works in 2 ways.

First way is to define which object should be used when specific namespace is called in the constructor, ie. if you pass Magento\Catalog\Api\ProductInterface in the constructor DI system will instantiate an object that is set as a preference for this interface. Similarly if you define Magento\Catalog\Model\Product you are able to set preference on that as well and exchange default model for any class you want provided it implements required interface or extends original class. To do that you need to use <preference...> tag in di.xml. This method however is global, which means it will use the preference in each instance in the system.

But if you need something smaller, you need to hook into 1 or few models and make some adjustments there then the above approach will give you 2 main problems: (1) you need to make sure your preference work with all usage of the system (2) it may conflict with another module defining preference for the same interface/class which will end up your implementation not functioning. To avoid that you can tell DI system to use a specific class of your choice for a specific constructor. This way you can still change class being passed to constructor but now it is not global, it works for this specific use case. The part of the code you've seen is for that. It is structured as follows

<type name="Magento\Quote\Model\Quote\Address">

This defines a class you to which constructor want to hook.

<arguments>

This node tells config system you will provide a configuration for the constructor. <type...> node ind di.xml may also be used to define plugins so this node is to make sure what are you doing.

<argument name="addressConfig" xsi:type="object">Magento\Customer\Model\Address\Config\Proxy</argument>

Here you define the replacement. name parameter is equal to the variable name in the constructor then xsi:type defines what type of value you will provide, most common used are: object, number, string, array. You can use this way to define not only class variables but any type of variable used in constructor. And finally value of a node defines value to be passed, in this case a class name to be instantiated by DI to inject into constructor.

You do not have to define all arguments used in constructor, you can selectively used the ones you want to change. They do not have to be defined in an order used in constructor.

[EDIT]

When it comes to your edit, whenever Vendor\Module\Model\Mymodel class is instantiated the argument $dataSource in constructor will have at least 2 items with keys name1 and name2 and values of Vendor\Module\Model\ModelClass1 and Vendor\Module\Model\ModelClass2 respectively. When injecting ``Vendor\Module\Model\MyModelto any other class it will be an instance that received those objects. You will be able to access them according to the wayVendor\Module\Model\MyModel` class exposes them.