(This post is the fourth in the “Upgrade your Magento 1 knowledge to Magento 2”; A series of posts about the major differences between Magento 1 and Magento 2. You can read the other posts in the series here.)
Last time we had a look at what Dependency Injection (DI) is and how it has been implemented in Magento 2. There were a lot of new concepts in that article and a lot of things that needed to be taken on faith. Let’s look a little deeper at how DI works in Magento 2 in order to deepen our understanding.
Recall that in Magento 2, the injected objects are auto-instantiated when they are type-hinted in the parameter list of a method. There’s a lot of magic going on behind the scenes to get this to happen ‘automatically’ and to make it customisable. The Object Manager is the class which underpins the implementation of this ‘automatic’ DI in Magento 2. Not only does it instantiate new objects, but it implements the singleton design pattern (replacing
Mage::getSingleton()), handles the instantiation of parameters (including those defined in all the di.xml defined in each module and those provided directly) and also instantiating the configured instance of an object (because, as we have already seen, preferences can be used to tell Magento 2 which class to use to instantiate an object).
The object manager is an instance of the
Magento\Framework\ObjectManager\ObjectManager class and like most classes in Magento 2, implements and interface, that being
Magento\Framework\ObjectManagerInterface. If we take a look at that interface, we can see that for a class with so many responsibilities, the implementation is actually quite simple. The interface defines three methods:
/** * Create new object instance * * @param string $type * @param array $arguments * @return mixed */ public function create($type, array $arguments = );
The create method will return a new instance of an object. It is analogous to
$type argument is a model, block or helper class name or interface name. The big difference here is that there are no more grouped class names, like
catalog/product, as in Magento 1. Now, you need to specify the full class name when specifying a class in di.xml or anywhere else when requesting a class. The array of
$arguments is data that will be used by the object, in the same way you can pass data as the second parameter of
/** * Retrieve cached object instance * * @param string $type * @return mixed */ public function get($type);
The get method will check to see if an instance of the object of type
$type has already been instantiated and return that. Otherwise it will instantiate a new object of type
$type and return that instead. It is analogous to
Mage::getSingleton(). Again, the
$type argument is a model, block or helper class name or interface name.
If we want to specify that an object we’re injecting should be instantiated as a singleton, then we can specify the
shared="true" attribute in the
di.xml. If we set the same attribute to false, then we are telling Magento 2 that we deliberately don’t want to use the singleton object in this instance.
In this example, you can see that the shared attribute is set on the
type node, but it can also be used on any
arguments node (with an
xsi:type of object) as well:
<type name="Magento\Framework\App\RouterList" shared="true"> <arguments> <argument name="routerList" xsi:type="array"> <item name="standard" xsi:type="array"> <item name="class" xsi:type="string">Magento\Framework\App\Router\Base</item> <item name="disable" xsi:type="boolean">false</item> <item name="sortOrder" xsi:type="string">20</item> </item> <item name="default" xsi:type="array"> <item name="class" xsi:type="string">Magento\Framework\App\Router\DefaultRouter</item> <item name="disable" xsi:type="boolean">false</item> <item name="sortOrder" xsi:type="string">100</item> </item> </argument> </arguments> </type>
/** * Configure object manager * * @param array $configuration * @return void */ public function configure(array $configuration);
The configure method is used to configure the DI instance by passing in an array of configuration parameters.
The classes which actually instantiate the objects – i.e. which contain the PHP
new keyword are
Magento\Framework\ObjectManager\Factory\Dynamic\Developer in developer mode and
Magento\Framework\ObjectManager\Factory\Dynamic\Production in production mode. The main difference between the two classes is that the Developer mode has extra error checking and debugging logic, which is not included in the production class.
Whilst these methods are similar to some of the Magento 1 static methods getModel() and
getSingleton, it is worth noting that you are discouraged from using the object manager directly in your code – these methods are intended for low-level system development, such as the logic that bootstraps Magento. We discuss them here so we can get a better understanding of what is happening under the hood of Magento 2’s DI implementation.
The best practice when it comes to using DI in Magento 2 is to define the dependencies of your object as interfaces in the object class’s constructor. These dependencies expose an API for your class, which means it can then be configured by specifying class names in the di.xml of this module, or any other module. All the di.xml files from all modules are merged together (just like config and layout XML in Magento 1) and the object manager checks this merged XML tree to determine which class should be instantiated for any given class or interface.
If we define a preference using the
preference node, then the configured class will be used globally whenever an instance of the interface is requested. We can also use the
preference node to specify that whenever a particular class is requested, we can specify a different class instead. Our replacement class just needs to extends the substituted class.
If you’re thinking this provides a means of class rewriting, then you’re right – but I’ll expand on this in my next post.
It’s not just objects which can be defined as arguments in the di.xml – other data types like strings and arrays can be defined and the content of these will be merged and passed into the constructor of auto-instantiated objects as well.
The “preferences”, as Magento 2 calls them, are defined in the following files and in the following order, which later files overriding earlier ones:
Next time I’ll look at some of the limitations of the DI system, how class rewrites work and the benefits of object proxying.