Basics of dependency injection and its usage in Magento 2

Basics of dependency injection and its usage in Magento 2

One of the biggest changes in Magento 2 is the usage of dependency injection design pattern. With this pattern, a lot has been changed inside codebase, and many new things have been introduced. In this article, I will try to explain the very basics of this design pattern, and its implementation in Magento 2 to help those who are beginners in the field of dependency injection.

Let me first start by explaining the basic idea behind this design pattern. By following dependency injection, you should ask for required resources when your object is being created, instead of creating resources when they are required. This will allow class isolation and independent development, as well as unit testing due to ease of mocking required objects. Before continuing, let me show you an example:

class A
{
...
public function read()
{
$dbh = new DatabaseConnection(); // avoid this
$dbh->query('SELECT ...');
...
}
...
}

Previous example demonstrates simple class A with method read(). In this method, we require database connection object which is created in the first line of method. This is something most of you are used to, and it is something similar to what is used in Magento 1.x (with usage of factory pattern). The next example will implement the same logic, but following dependency injection pattern:

class A
{
protected $_dbh;
...
public function __construct(DatabaseConnection $connection)
{
$this->_dbh = $connection;
}
...
public function read()
{
$this->_dbh->query('SELECT ...');
...
}
...
}

In this example, database connection object has been passed to class A through constructor, instead of being created inside read() method. Even though this is not the only way of passing dependencies onto object, this implementation is used in Magento 2. So, what exactly have we achieved by this?

For starters, class A can be developed independently of class DatabaseConnection as we can always fake connection for purpose of development. It is independent of DatabaseConnection implementation, and it is much easier to test our code this way.

One of the downsides you have probably noticed is that the second snippet of the code is larger than the previous one. And that is true, for an example Magento\Catalog\Model\Product has constructor that spreads across 52 lines of code. Also, if you try to debug this code, you will find it is a really difficult task. Luckily, this is where testing comes in hand.

For initialization of such large objects, dependency injection introduces dependency injection container, and in Magento 2 it is called ObjectManager. This container is responsible for resolving and creating dependencies upon object creation. Let’s examine how this works on a simple example:

class A
{
public function __construct(B $b)
{
$this->_b = $b;
}
}

When creating object from class A, the following would happen:

  1. ObjectManager->create(‘A’) is called
  2. Constructor of A is examined
  3. Class B is being created, and used for creating A

In those few steps, ObjectManager has created an object from class. But for the moment, let’s not keep it that simple, and imagine we have multiple implementations of class B. This raises the question, which implementation will be used? And the answer to that question can be found in one of the di.xml files, which are located in the following locations:

  • {moduleDir}/etc/{areaCode}/di.xml
  • {moduleDir}/etc/di.xml
  • app/etc/di/*.xml

Within those files, you can find/specify these settings:

Class definitions: Type and number of dependencies. Fortunately, Magento 2 uses constructor signature to compile this automatically.
Instance configuration: Definition on how to instantiate objects. This involves setting of types and virtualTypes which are out of scope of this article.
Abstraction-implementation mappings: These mappings allow you to choose preferred interface implementation. This is something similar to rewrite in Magento 1.x.

One more thing that is left here to explain is type of objects that are being injected. In Magento 2, they are separated into two groups: injectable, and non-injectable. Let me first start by explaining what non-injectable objects are. After that, injectables will be self-explanatory.

For an example, let’s examine the product page, where you wish to display a current product, and some related products. If you pass product model to your controller, first you will have to call load on that product to get the current product. But once that object is used, where will you get info about other products? So you are guessing that product model is one of the non-injectable objects. Actually, all objects that have some kind of identity, would be non-injectables: products, orders, cart items, users, …

In order to use non-injectable objects in your code, you have to request their factory. For example, if you are trying to load multiple products, your code will depend on product factory, and through that object you would call create() method with identity of the products you are trying to load. Proxies are primarily used for lazy loading of optional or expensive dependencies.

In order to have things a bit more complex, sometimes you really wish to depend upon non-injectable object itself, as they behave like singletons. Good example for this would be a CMS page.

To sum up, we have covered the very basics of dependency injection design pattern. There is a lot more to cover, but I hope that I managed to speed up this process for you. There are many materials on this subject out there, but if you are interested in the Magento specifics, a good starting point would be the official documentation.

In case you feel you need some extra help, we can offer you a detailed custom report based on our technical audit – feel free to get in touch and see what we can do for you!

You made it all the way down here so you must have enjoyed this post! You may also like:

3 best open-source eCommerce platforms in 2021 Zrinka Antolovic
Zrinka Antolovic, | 8

3 best open-source eCommerce platforms in 2021

Moving the validation error message, this time globally Danijel Vrgoc
, | 0

Moving the validation error message, this time globally

Overriding classes in Magento 2 Damir Korpar
Damir Korpar, | 13

Overriding classes in Magento 2

12 comments

  1. I really like how you explained it thoroughly. This is very helpful in sharing the knowledge with other Magento Devs.

    Big thanks to you!

  2. Thanks for the article. It gave me the much needed boost to start working with DI in Magento 2. I’ll try to give me 5 cents thought.

    The “a-ha” moment for me was when I realized that when I define a dependency in my class constructor Magento will try to give it me automatically (out of the box). That is why it is better to use an interface for the type of the object, instead of the actual class that you need. After I realized that then the di.xml file started to make more sense. So in di.xml you can specificy which implementation of the dependancy I want. As long as the class inside the tag implements the interface – it is all good and peachy.

    After that the factories/injectable and non-injectables become more clear as well. General rule of thumb – injectables are all objects that do not interact with the database. Non injectables are database objects (i.e. have identity) and thus you need to use a Factory to create them.

    I’m starting to like the Magento 2 more and more 🙂

  3. It’s fantastic that author/devs write explanations like this. But when you use passive voice you take away the reader’s ability to learn which code is performing the action under discussion. This is all the more critical when discussing DI where the entire topic concerns activities being done by code that you would not normally expect.
    Example:
    “When creating object from class A, the following would happen:” create(‘A’) is called” <– By what code?
    "Constructor of A is examined" <– By what? And what does "is examined" mean?
    "Class B is being created, and used for creating A" <– Skipping the grammar issue, again what code is doing this?

    Thanks,

  4. I’ve been working through the Magento 2 fundementals course and was struggling with their explanation of DI . Your version is much clearer and understandable, thanks for writing it!

  5. Thanks for the info. I’m really looking forward to more Magento 2 tutorials and videos explaining the internals of Magento 2 from a developers perspective.

  6. Magento 2 is going to flourish the e-Commerce market by speeding up the websites. Provides an excellent shopping experience for the customers.

  7. Great..!

    I love to see the Magento in action soon.

    Also the idea of lazy loading over expensive dependencies will create a significant performance improvements in Magento which is a headache since day 1.

    Will love to see the new release soon. Any idea on when we can expect its release?

    Your documentation sounds to me that its going to launch soon. May be January 2015?

Leave a Reply

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

You may use these HTML tags and attributes: <a href="" title=""> <blockquote cite=""> <code> <del datetime=""> <em> <s> <strike> <strong>. You may use following syntax for source code: <pre><code>$current = "Inchoo";</code></pre>.

Tell us about your project

Drop us a line. We'd love to know more about your project.