How di.xml Controls Dependency Injection in Magento 2

How di.xml Controls Dependency Injection in Magento 2

Magento 2 uses dependency injection (DI) to manage how classes receive their required dependencies. The di.xml file sits at the center of this system, letting you configure which classes get injected where and how they behave.

Where di.xml Files Live

Place di.xml in your module's etc directory. The location determines scope:

  • module/etc/di.xml - Applies globally across storefront, admin, and API
  • module/etc/adminhtml/di.xml - Admin area only
  • module/etc/frontend/di.xml - Storefront only
  • module/etc/webapi_rest/di.xml - REST API only

Tip

To enhance your eCommerce store’s performance with Magento, focus on optimizing site speed by utilizing Emmo themes and extensions. These tools are designed for efficiency, ensuring your website loads quickly and provides a smooth user experience. Start leveraging Emmo's powerful solutions today to boost customer satisfaction and drive sales!

Four Ways to Configure Dependencies

1. Class Preferences

Preferences map interfaces to concrete implementation classes. Use this when you need to override protected or private methods that plugins can't touch.

Avoid preferences for public methods. Plugins work better for those.

<?xml version="1.0" encoding="UTF-8"?>

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">

<preference for="Vendor\Module\Api\DataInterface"

type="Vendor\Module\Model\DataImplementation" />

</config>

When Magento needs DataInterface, it instantiates DataImplementation instead.

2. Virtual Types

Virtual types create reusable configuration blueprints without generating actual PHP classes. They reduce code duplication.

<?xml version="1.0" encoding="UTF-8"?>

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">

    <virtualType name="CustomLogger" type="Psr\Log\LoggerInterface">

        <arguments>

            <argument name="name" xsi:type="string">custom_logger</argument>

            <argument name="handlers" xsi:type="array">

                <item name="system" xsi:type="object">Magento\Framework\Logger\Handler\System</item>

            </argument>

        </arguments>

    </virtualType>

</config>

3. Type Configuration

Type configurations let you set constructor arguments, method calls, and other class-specific settings.

<?xml version="1.0" encoding="UTF-8"?>

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">

<type name="Magento\Catalog\Model\Product">

<arguments>

<argument name="customAttribute" xsi:type="string">custom_value</argument>

</arguments>

</type>

</config>

4. Plugins (Interceptors)

Plugins let you modify public method behavior without altering core code. They intercept method calls and run your custom code before, after, or around the original method.

<?xml version="1.0" encoding="UTF-8"?>

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">

<type name="Magento\Catalog\Model\Product">

<arguments>

<argument name="customAttribute" xsi:type="string">custom_value</argument>

</arguments>

</type>

</config>

Plugin Types Explained

Before Plugins

Before plugins change method arguments or add logic before the original method executes. Name your method with the before prefix.

<?php

namespace Vendor\Module\Plugin;

class ProductPlugin

{

    public function beforeSetName($subject, $name)

    {

        // Modify the name argument

        $name = strtoupper($name);

        return [$name];

    }

}

Return an array of modified arguments. Return null if you don't change anything.

After Plugins

After plugins modify return values. Since Magento 2.2, after plugins can access input arguments from the original method. Use the after prefix.

<?php

namespace Vendor\Module\Plugin;

class ProductPlugin

{

    public function afterGetPrice($subject, $result)

    {

        // Add 10% to the price

        return $result * 1.10;

    }

}

The $result parameter holds the original return value.

Around Plugins

Around plugins wrap the entire method execution. They can run code before and after the original method. Use around plugins sparingly - they can prevent execution of other plugins if not implemented correctly.

<?php

namespace Vendor\Module\Plugin;

class ProductPlugin

{

public function aroundSave($subject, callable $proceed)

{

// Code before original method

$result = $proceed(); // Call original method

// Code after original method

return $result;

}

}

If you don't call $proceed(), the original method and other plugins won't execute.

Plugin Execution Order

The sortOrder attribute controls when plugins run. Lower numbers execute first for before and around plugins. Higher numbers execute first for after plugins.

Plugin Type Execution Order Sort Order
Before sortOrder 1 → 100 (ascending) -
Around (before callable) sortOrder 1 → 100 (ascending) -
Original Method - -
Around (after callable) sortOrder 100 → 1 (descending) -
After sortOrder 100 → 1 (descending) -

Example with three plugins (sortOrder 10, 20, 30):

1. Plugin10::beforeMethod

2. Plugin10::aroundMethod (before)

3. Plugin20::beforeMethod

4. Plugin20::aroundMethod (before)

5. Plugin30::beforeMethod

6. Plugin30::aroundMethod (before)

7. Original::method

8. Plugin30::aroundMethod (after)

9. Plugin30::afterMethod

10. Plugin20::aroundMethod (after)

11. Plugin20::afterMethod

12. Plugin10::aroundMethod (after)

13. Plugin10::afterMethod

Plugin Limitations

Plugins cannot modify final methods, final classes, non-public methods, static methods, constructors, virtual types, or objects created before the interception framework loads.

You also can't use plugins on objects instantiated with the new keyword directly.

Injectable vs Non-Injectable Objects

Injectable objects are singletons created through dependency injection using di.xml configuration. Examples: repositories, services, helpers.

Non-injectable (newable) objects require external input like database data or user input. Examples: products, orders, customers. Use factories to create these objects.

Best Practices

  • Use constructor injection - Pass dependencies through class constructors instead of creating them inside methods.
  • Avoid ObjectManager - Direct ObjectManager calls create hidden dependencies and bypass DI benefits. Let the DI system resolve dependencies automatically.
  • Prefer plugins over preferences - Plugins keep customizations modular and reduce upgrade conflicts. Use preferences only for private/protected methods.
  • Set unique sortOrder values - Prevents unpredictable behavior when multiple plugins target the same method.
  • Keep classes focused - Too many constructor dependencies signals a class does too much. Break large classes into smaller, single-purpose ones.

Configuration Scope Strategy

Structure your di.xml files by scope for better performance:

  • Global configurations in etc/di.xml
  • Admin-specific customizations in etc/adminhtml/di.xml
  • Frontend optimizations in etc/frontend/di.xml

Magento loads configurations dynamically based on active area, modules, and theme. Area-specific files override global settings.

Summary Table

Feature Purpose When to Use
Preferences Map interfaces to classes Override private/protected methods
Virtual Types Reusable configuration Multiple classes need same setup
Type Config Constructor arguments Pass values to class constructors
Before Plugin Modify input Change arguments before method runs
After Plugin Modify output Change return values
Around Plugin Full control Wrap entire method execution

Conclusion

Factory classes form a core component of Magento 2 architecture. They handle non-injectable object creation, support dependency injection, and maintain code quality. Understanding when and how to use factories separates proficient Magento developers from beginners.

FAQs

What is the purpose of di.xml in Magento 2?

The di.xml (Dependency Injection configuration file) defines how classes and interfaces are wired together in Magento 2. It tells Magento which classes to instantiate, how to pass dependencies, and what preferences, plugins, or virtual types to apply.

Where is di.xml located?

You can find di.xml files in several scopes within a Magento module:

  • app/code/Vendor/Module/etc/di.xml — for global configuration
  • app/code/Vendor/Module/etc/frontend/di.xml — for frontend area only
  • app/code/Vendor/Module/etc/adminhtml/di.xml — for admin area only

Magento merges these configurations automatically during compilation.

How does di.xml define class preferences?

Preferences in di.xml map an interface or abstract class to a concrete implementation. This allows Magento to decide which class to instantiate when an interface is requested:

<preference for="Vendor\Module\Api\Data\ProductInterface"
            type="Vendor\Module\Model\Product" />

What are constructor arguments in di.xml?

Constructor arguments let you inject specific values or class instances into objects. For example:

<type name="Vendor\Module\Model\Example">
    <arguments>
        <argument name="limit" xsi:type="number">10</argument>
    </arguments>
</type>

This sets the $limit parameter when Example is instantiated.

How are plugins defined in di.xml?

Plugins allow you to intercept method calls before, after, or around the original method. You define them in di.xml like this:

<type name="Magento\Catalog\Model\Product">
    <plugin name="custom_plugin"
            type="Vendor\Module\Plugin\ProductPlugin" />
</type>

What are Virtual Types in di.xml?

Virtual Types let you reuse existing class configurations with different constructor arguments, without creating new PHP classes. Example:

<virtualType name="CustomProduct" type="Magento\Catalog\Model\Product">
    <arguments>
        <argument name="custom" xsi:type="boolean">true</argument>
    </arguments>
</virtualType>

Can I override another module’s di.xml configuration?

Yes. Magento merges all di.xml files during compilation. If two modules define the same preference or type, the module loaded later (based on module.xml sequence) takes precedence.

How does di.xml improve Magento’s flexibility?

It decouples class dependencies, allowing developers to swap, extend, or decorate functionality without modifying core code. This supports better testability, modularity, and extensibility across the system.

Is it mandatory to use di.xml for Dependency Injection?

No. Most dependencies are automatically resolved by Magento’s Object Manager through type hinting. You only need di.xml when you want to customize or override default dependencies.

When should I modify di.xml?

Modify di.xml when you need to:

  • Replace an interface implementation (using preference).
  • Add or modify a plugin.
  • Inject specific arguments or values into constructors.
  • Create virtual types for reusable configurations.