Discover Shopify App Store – A Comprehensive Handbook 2024
Explore the Shopify App Store for tailored solutions to grow your business. Discover your perfect app today!
Vinh Jacker | 11-11-2024
Dependency Injection is a unique design pattern that implements control inversion and provides the ability to follow the principle of dependency inversion. It is a technique that enables loose coupling. A number of the latest software application frameworks support Dependency Injection including TypeScript, Spring, Google Guice, Microsoft Managed Extensibility Framework (MEF), etc.
In Magento 2, one of the most significant changes is the use of Dependency Injection. With this design pattern, the codebase of Magento 2 has changed a lot, and many new things have been introduced. And now it has the same functionality as provided by the Mage class in Magento 1.
In this article, I will explain the basics of Magento 2 Dependency Injection in Magento 2 to help those who are new to this topic.
Dependency is the object which is required by the class from an external source and injection is the method of passing that particular dependency to the dependent class. For example, if you have a class that fetches some data by using Magento 2 Observer Class, we can say that your class has a dependency on that Magento 2 Observer Object.
Still confused? Take a look at this fantastic example by John Munsch, who has explained dependency injection to a 5-year-old:
“When you go and get things out of the refrigerator for yourself, you can cause problems. You might leave the door open or get something Mommy or Daddy doesn’t want you to have. You might even be looking for something we don’t even have or which has expired. What you should be doing is stating a need, “I need something to drink with lunch,” and then we will make sure you have something when you sit down to eat.”
It means that the collaborating classes (5-year-old) should rely on the foundation classes (parents) to provide the desired results. Dependency Injection is a win-win situation for everyone, it passes the object to the dependent class, rather than allowing the dependent class to build or find the object from scratch.
Constructor Injection: This is the most common form of dependency injection. Dependencies are passed into a class via its constructor.
Example:
class MyClass
{
protected $dependency;
public function __construct(\Namespace\Module\ClassName $dependency)
{
$this->dependency = $dependency;
}
}
Example (in di.xml
):
<preference for="Magento\Catalog\Api\ProductRepositoryInterface" type="Vendor\Module\Model\CustomProductRepository" />
Example:
<virtualType name="Vendor\Module\Model\CustomModel" type="Magento\Framework\Model\AbstractModel">
<arguments>
<argument name="data" xsi:type="array">
<item name="customData" xsi:type="string">Some Value</item>
</argument>
</arguments>
</virtualType>
Example:
php Copy this code:
protected $objectFactory;
public function __construct(\Namespace\Module\Model\MyClassFactory $objectFactory)
{
$this->objectFactory = $objectFactory;
}
public function createObject()
{
$object = $this->objectFactory->create();
return $object;
}
Setter injection involves injecting dependencies through setter methods. In this approach, the class provides setter methods that allow you to set the dependencies after the object has been created. Here’s an example:
class MyClass
{
private $dependency;
public function setDependency(Dependency $dependency)
{
$this->dependency = $dependency;
}
public function someMethod()
{
// Use the dependency
$this->dependency->doSomething();
}
}
The di.xml
file is used to configure DI and contains definitions for preferences, virtual types, and other DI-related configurations. For example:
xml Copy this code:
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<preference for="Magento\SomeModule\Api\SomeInterface" type="Vendor\Module\Model\CustomClass" />
</config>
In summary, Magento 2’s DI framework promotes loose coupling by handling class dependencies through constructors and configuration files like di.xml.
In order to reduce code dependencies, it’s highly recommended to follow the dependency inversion principle as well as use abstractions in your site code. It means that you shouldn’t let your high-level classes work directly with low-level classes. Instead, just use their interfaces.
By using your code interfaces, you can minimize the incompatibility risks when Magento modifies the underlying implementation of these interfaces. This method also enables you to focus on what the class does rather than how it is implemented.
As the Magento codebase follows the dependency inversion principle, you could use the di.xml file to connect your custom implementation of a Magento interface to a related class or service.
In general, Magento promotes Dependency Injection as a preferred approach for better code modularity, testability, reusability, flexibility, and so on. It helps developers build robust and maintainable applications while ensuring a high level of code quality and performance:
Modular and Testable Code: Dependency Injection helps in creating modular and loosely coupled code. By injecting dependencies instead of directly instantiating the within a class, it becomes easier to replace or mock dependencies during testing. This improves code maintainability and enables more efficient unit testing.
Code Reusability: With Magento 2 Dependency Injection, dependencies can be shared and reused across multiple classes. Instead of duplicating code to create instances of dependencies within different classes, dependencies can be injected and shared, reducing redundancy and improving efficiency.
Flexibility and Scalability: Dependency Injection allows for flexibility in managing dependencies. It becomes easier to switch or modify dependencies without making significant changes to the code. This promotes scalability as the system can easily accommodate changes or additions to the dependencies as the application evolves.
Decoupling of Components: Magento 2 Dependency Injection helps in decoupling components by removing direct dependencies between classes. This promotes the Single Responsibility Principle (SRP) and allows each class to focus on its own functionality, making the codebase more modular and easier to understand and maintain.
Magento encourages the use of Dependency Injection over direct use of Object Manager: In fact, Object Manager also offers quite same service to Dependency Injection. However, in spide of the benefit in less codes to write, Object Manager doesn’t follow the Develop Processes of Magento 2, and it even creats the unrequired hidden dependencies. Knowing what the code depends on is a better approach than facing hidden deprendencies in the code. That is the reason why the structure prefers Magento 2 Dependency Injection
Although the create () method of Object Manager can create your class’s objects, get, and pass the value for constructor parameter from di.xml files, we don’t recommend using it.
Object Manager goes against the objective of Dependency Injection. Even though Object Manager requires less code, it doesn’t follow the M2 Development Processes. Moreover, it creates hidden dependencies that are not required.
On the other hand, Dependency Injection has no hidden dependencies in the code. That’s why it’s a better choice.
1. Define the dependency in the class constructor
namespace VendorModuleModel;
class MyClass
{
protected $_dependency;
public function __construct(
VendorModuleDependencyMyDependency $dependency
) {
$this->_dependency = $dependency;
}
}
2. Inject the dependency into the class
namespace VendorModuleDependency;
class MyDependency
{
public function doSomething()
{
// Do something
}
}
3. Use the injected dependency in your class
namespace VendorModuleModel;
class MyClass
{
protected $_dependency;
public function __construct(
VendorModuleDependencyMyDependency $dependency
) {
$this->_dependency = $dependency;
}
public function doSomething()
{
$this->_dependency->doSomething();
}
}
1. Define the setter method in the class
namespace VendorModuleModel;
class MyClass
{
protected $_dependency;
public function setDependency(VendorModuleDependencyMyDependency $dependency)
{
$this->_dependency = $dependency;
}
}
2. Inject the dependency into the class
namespace VendorModuleDependency;
class MyDependency
{
public function doSomething()
{
// Do something
}
}
3. Use the injected dependency in your class
namespace VendorModuleModel;
class MyClass
{
protected $_dependency;
public function setDependency(VendorModuleDependencyMyDependency $dependency)
{
$this->_dependency = $dependency;
}
public function doSomething()
{
$this->_dependency->doSomething();
}
}
1. Define the interface
namespace VendorModuleDependency;
interface MyDependencyInterface
{
public function doSomething();
}
2. Implement the interface
namespace VendorModuleDependency;
class MyDependency implements MyDependencyInterface
{
public function doSomething()
{
// Do something
}
}
3. Inject the dependency into the class
namespace VendorModuleModel;
class MyClass
{
protected $_dependency;
public function __construct(MyDependencyInterface $dependency)
{
$this->_dependency = $dependency;
}
public function doSomething()
{
$this->_dependency->doSomething();
}
}
Virtual types in Magento 2 allow configuring concrete classes. Defining them is possible without modifying their original implementations. They act as placeholders for actual class configurations. Such enables flexibility and customization without altering core code.
Besides the types we mentioned earlier, understanding virtual types in Magento 2 would benefit you too. They allow configuring concrete classes, and it’s possible to define them without changing their initial implementations. These virtual types act as placeholders for the actual class configurations. It offers you the flexibility to customize without interfering with the core code.
Configuration in di.xml:
Specify virtual types in the di.xml configuration file. Provide the virtual type’s name and configuration.
Use the <virtualType>
tag to find a virtual type and define its parameters.
<virtualType name="VirtualTypeExample" type="ConcreteClass">
<arguments>
<argument name="argumentName" xsi:type="string">argumentValue</argument>
</arguments>
</virtualType>
Usage in Class Constructor
Add virtual types into the class constructors like other dependencies.
Magento 2 will instantiate and inject the configured actual class for the virtual type.
class MyClass {
public function __construct(VirtualTypeExample $virtualType) {
// Virtual type automatically resolves to the configured concrete class
}
}
Plugins (aka interceptors) help adjust the behavior of public class functions without changing the core code. They allow: before, after, and around method interception, which is flexible to extend and customize functions.
Configuration in di.xml:
<type name="TargetClass">
<plugin name="PluginName" type="PluginClass" sortOrder="10" disabled="false"/>
</type>
Install the plugin class that using methods specific to the interception type. Utilize the method parameters to retrieve the original method arguments and return values.
class PluginClass {
public function beforeMethod($subject, $arg1, $arg2) {
// Modify arguments or perform actions before the original method is called
}
public function afterMethod($subject, $result) {
// Modify the result or perform actions after the original method is called
}
public function aroundMethod($subject, $proceed, $arg1, $arg2) {
// Execute custom logic before and after the original method
$result = $proceed($arg1, $arg2);
return $result;
}
}
Magento 2 Dependency Injection is a great way to reduce tight coupling between application codebases. Rather than hard-coding dependencies, you can inject the list of object that a class may need. Dependency Injection also enables you to manage future changes and other complexity in your codes.
Also, it is essential to follow the design patterns and coding standard while developing Magento 2. Although there’s more to learn about this topic, these were only the basics to get you started with Dependency Injection in Magento 2.