Dependency injection
- Overview of dependency injection
- Preview of constructor injection
- Configuration overview
- Class definitions
- Type configurations
- Lifecycle management
- Compiler tool
Overview of dependency injection
The Magento software now uses dependency injection as an alternative to the Magento 1.x Mage
class. Dependency injection means that all object dependencies are passed (that is, injected) into an object instead of being pulled by the object from the environment.
A dependency (sometimes referred to as coupling) implies the degree that one component relies on another component to perform a function. A large amount of dependency limits code reuse and makes moving components to new projects difficult.
In simple terms, if ModuleA needs to access some functionality in ModuleB, ModuleA depends on ModuleB. ModuleA consumes the service offered by ModuleB, so ModuleA is the consumer and ModuleB is the dependent.
In addition, we use dependency inversion, a coding principle that stipulates you use abstractions to reduce code dependencies. Dependency inversion means:
- High-level modules should not depend on low-level modules. Both should depend on abstractions.
- Abstractions should not depend upon details. Details should depend on abstractions.
For more information, see this article by Robert C. Martin.
The object manager specifies the dependency environment for constructor injection. The object manager must be present only when composing code. In larger applications, composing code is performed early in the bootstrapping process.
This topic uses the following terms:
- Constructor injection
-
Type of dependency injection used for implementation dependencies (that is, dependencies that fulfill a business task of an object)
- Factory
-
Object that creates the objects of a specific type. Unlike business objects, a factory can be dependent on the object manager.
- Proxy
-
Auto-generated object that implements the same interface as the original object, but unlike this original object has only one dependency鈥攖he object manager. A proxy is used for lazy loading of optional dependencies. A proxy can be used to break cyclical dependencies. For more information about proxies, see Preview of constructor injection.
- Lifecycle
-
An object鈥檚 lifecycle determines in what scope instances are reused, and when to release them.
Preview of constructor injection
Constructor injection must be used for all optional and required service dependencies of an object. Service dependencies fulfill business functions of your object.
<?php
class Test
{
protected $class;
public function __construct(SomeClass $class)
{
$this->class = $class;
}
public function execute()
{
//some code
$this->class->execute();
//some code
}
}
$test->execute();
?>
Use a proxy for expensive optional dependencies; proxies are auto-generated, no coding is required.
A sample proxy (which you declare in di.xml
) follows:
<type name="Magento\Backend\Model\Config\Structure\Element\Iterator\Field" shared="false">
<arguments>
<argument name="groupFlyweight" xsi:type="object">Magento\Backend\Model\Config\Structure\Element\Group\Proxy</argument>
</arguments>
</type>
Configuration overview
The object manager needs the following configurations:
- Class definitions for retrieving the types and numbers of class dependencies
- Instance configurations for retrieving how the objects are instantiated and for defining their lifecycle
- Abstraction-implementation mappings (that is, interface preferences) for defining what implementation is to be used upon request to an interface
To define the interface preferences for the object manager, use app/etc/di/*.xml
, <your module dir>/etc/di.xml
, and <your module dir>/etc/<areaname>/di.xml
files depending on the scope it belongs in.
For example, to set the interface preferences for the Magento Admin, use app/code/core/Magento/Backend/etc/adminhtml/di.xml
as follows:
You can also specify whether or not the object is shareable in its di.xml
as follows:
Dependency injection is configuration-based; configurations are validated by config.xsd.
Object manager configurations can be specified at any of the following scopes:
- Primary for bootstrapping (
app/etc/di/*.xml
) - Global across all of Magento (
<your module directory>/etc/di.xml
) -
Area-specific configuration (
<your module directory>/etc/<areaname>/di.xml
)Area-specific means specific a Magento area (
frontend
,adminhtml
, and so on). For example, here is the Magento Customer module鈥檚 adminhtml di.xml.
Each scope overrides any previously existing config when it is loaded.
Configurations for each scope are merged across modules, so there is no way to create a configuration that is only seen by a single module.
Class definitions
Magento uses class constructor signatures, not doc-block annotations, to retrieve information about class dependencies; that is, to define what dependencies are to be passed to an object.
Magento reads constructors using reflection. We recommend you use the single-store compiler tool or the multi-store compiler tool to pre-compile class definitions for better performance.
The parameters specified for a class type are inherited by its descendant classes.
Type configurations
By type, we mean basically the scope of the dependency (all of Magento, module, module area). For a review, see Configuration overview.
See one of the following sections for more information:
Specify types
Sample dependency injection by type:
The preceding sample declares the following types:
Magento\Core\Model\Session
(if the type is not set explicitly, it is taken from the name)config
virtual type that extendsMagento\Core\Model\Config
moduleConfig
virtual type that extends typeMagento\Core\Model\Config
Magento\Core\Model\App
type. All instances of this type retrieve and instance ofmoduleConfig
as a dependency
Arguments
Arguments are injected into a class instance during its creation. Argument names must correspond to constructor parameters of the configured class.
Sample argument that creates instances of Magento\Core\Model\Session
with the argument $sessionName
set to a value of adminhtml
:
Argument definitions
The following tables discuss the meanings of argument definitions.
Node format | Description | Possible values |
---|---|---|
Object with default lifecycle <argument xsi:type="object"> {Type_Name}</argument> Object with specified lifecycle <argument xsi:type="object" shared="{shared}">{Type_Name}</argument> |
Creates an instance of Type_Name type and passed as argument. Any class name, interface name, or virtual type name can be passed as Type_Name . shared defines the lifecycle of a created instance. |
n/a |
Node format | Description | Possible values |
---|---|---|
Regular string <argument xsi:type="string"> {someValue}</argument> Translated string <argument xsi:type="string" translate="true">{someValue}</argument> |
someValue is passed as string. |
Any value is passed as a string. |
Node format | Description | Possible values |
---|---|---|
<argument xsi:type="boolean"> {boolValue}</argument> |
boolValue value is converted to bool |
Truth value discussed in the following table. |
Input type | Input data | Interpreted Boolean type |
---|---|---|
Boolean | true |
true |
Boolean | false |
false |
String | "true" |
true |
String | "false" |
false |
String | "1" |
true |
String | "0" |
false |
Integer | 1 |
true |
Integer | 0 |
false |
String literals are case-sensitive.
Node format | Description | Possible values |
---|---|---|
<argument xsi:type="number"> {numericValue}</argument> |
numericValue as-is |
Integer, float, or numeric string. |
Node format | Description | Possible values |
---|---|---|
<argument xsi:type="init_parameter"> {Constant::NAME}</argument> |
Global application argument represented by
Constant::NAME looked up and passed as argument. |
Constant the containing name of a global argument. |
Node format | Description | Possible values |
---|---|---|
<argument xsi:type="const"> {Constant::NAME}</argument> |
Constant::NAME passed as argument. |
Any constant name. |
Node format | Description | Possible values |
---|---|---|
<argument xsi:type="null"/> |
Pass null as argument. | n/a |
Node format | Description | Possible values |
---|---|---|
<argument xsi:type="array"> <item key="someItem" xsi:type="string">someVal</item> </argument> |
Array with elements corresponding to the items passed as argument. Array can contain an infinite number of items. Each item can be any type as argument, including an array itself, or an object type. | n/a |
When the configuration files for a given scope are merged, array arguments with the same name are merged into a new array. If a new configuration is loaded at a later time, either a more specific scope or through code, then any array definitions in the new configuration will completely replace the previously loaded config instead of being merged.
Parameter configuration inheritance
The preceding example configures all instances of Magento\Framework\View\Element\Context and its children to retrieve and instance of Magento\Framework\Url, but Magento\Backend\Block\Context overrides this and retrieves Magento\Backend\Model\Url.
Lifecycle management
An object鈥檚 lifecycle determines in what scope instances are reused, and when to release them.
The object manager creates objects and manages the lifecycle of the following types of objects:
singleton
鈥擟reate one class instance at the first request and subsequently reuse that instance. Release the instance when the container with which it鈥檚 registered is disposed. This is the default.transient
鈥擟reate a new class instance every time the class is requested.
The preceding lifecycle can be configured as:
argument
鈥擠efines the lifecycle for the argument only.type
鈥擜 convenience configuration that defines the lifecycle for all instances of the specified type.
Injectables and non-injectables
We use the following terms to describe objects that can or cannot be instantiated by the object manager:
- Injectable
-
Object (typically a singleton) that can be instantiated by the object manager.
- Non-injectable
-
Object that cannot be instantiated by the object manager. Typically, this object:
- Has a transient lifecycle
- Requires external input (such as data user input or data from database) to be properly created
Most models are non-injectable (for example, Magento\Catalog\Model\Product or Magento\User\Model\User).
You must observe the following rules:
- Injectables can request other injectables in the constructor, but non-injectables cannot request other objects in a constructor
- If a business function of an injectable object is to produce non-injectables, the injectable must ask for a factory in its constructor (due to the fact that factories are injectables)
- If a business function of an injectable object is to perform some actions on a non-injectable, it must receive the non-injectable as a method argument
You can create non-injectables in services with object factories or you can pass them in as method parameters.
Do not push injectables to non-injectables because it violates the Law of Demeter and requires additional lookup during object unserialization.
Factories
Factories are special objects that have only one purpose: to create an instance of one non-injectable class or interface. Unlike other objects, factories are allowed to depend on the object manager. Factories are used to isolate object manager from business code:
<?php
class Magento\Core\Model\Config\BaseFactory
{
protected $_objectManager;
public function __construct(Magento\Framework\ObjectManager $objectManager)
{
$this->_objectManager = $objectManager;
}
public function create($sourceData = null)
{
return $this->_objectManager->create('Magento\Core\Model\Config\Base', array('sourceData' => $sourceData));
}
} ?>
Most factories are simple, so developers do not have to bother with writing them. If a non-existent factory is encountered by object manager in runtime mode or compiler, the object manager generates the factory.
Compiler tool
To compile all non-existent proxies and factories; and to pre-compile class definitions, inheritance information, and plugin definitions for multiple stores or websites, see one of the following topics:
- If you have one website and one store, see Single-store compiler
- If you have multiple websites and stores, see Multi-store compiler
Find us on