I don't understand why, in some classes, their dependency injections are declared twice – once in the di.xml
and in the concrete class's constructor.
For instance in Magento\Backend\Model\Url
, its di.xml
has this set of types for DI defined:
<type name="Magento\Backend\Model\Url">
<arguments>
<argument name="scopeResolver" xsi:type="object">
Magento\Backend\Model\Url\ScopeResolver</argument>
<argument name="authSession" xsi:type="object">
Magento\Backend\Model\Auth\Session\Proxy</argument>
<argument name="formKey" xsi:type="object">
Magento\Framework\Data\Form\FormKey\Proxy</argument>
<argument name="scopeType" xsi:type="const">
Magento\Store\Model\ScopeInterface::SCOPE_STORE </argument>
<argument name="backendHelper" xsi:type="object">
Magento\Backend\Helper\Data\Proxy</argument>
</arguments>
</type>
But at the same time, in its concrete class, those classes defined in di.xml required for injection is re-declared again in the constructor:
<?php
public function __construct(
\Magento\Framework\App\Route\ConfigInterface $routeConfig,
\Magento\Framework\App\RequestInterface $request,
\Magento\Framework\Url\SecurityInfoInterface $urlSecurityInfo,
\Magento\Framework\Url\ScopeResolverInterface $scopeResolver,
\Magento\Framework\Session\Generic $session,
\Magento\Framework\Session\SidResolverInterface $sidResolver,
\Magento\Framework\Url\RouteParamsResolverFactory $routeParamsResolverFactory,
\Magento\Framework\Url\QueryParamsResolverInterface $queryParamsResolver,
\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
$scopeType,
\Magento\Backend\Helper\Data $backendHelper,
\Magento\Backend\Model\Menu\Config $menuConfig,
\Magento\Framework\App\CacheInterface $cache,
\Magento\Backend\Model\Auth\Session $authSession,
\Magento\Framework\Encryption\EncryptorInterface $encryptor,
\Magento\Store\Model\StoreFactory $storeFactory,
\Magento\Framework\Data\Form\FormKey $formKey,
array $data = []
) {
//...
}
?>
If we look at its constructor above, \Magento\Framework\App\Route\ConfigInterface $routeConfig
, for example, is not defined in di.xml
. It is only defined in the constructor and Magento will still inject the routeConfig
into the class for use, wouldn't it? Same for \Magento\Framework\Encryption\EncryptorInterface $encryptor
and a few others.
Then, why is there a need to define the other injections in both di.xml
and in the constructor when having those declarations in the constructor is sufficient for Magento to inject those dependencies into the class for use?
Best Answer
As stated in the documentation, in Magento 2, the
di.xml
can be used to do the following:In your case it's slightly complex I'm going to explain each argument one by one:
\Magento\Framework\App\Route\ConfigInterface $routeConfig
: this is an interface so it's not usable directly. The preference for this class is defined inapp/etc/di.xml
and it is theMagento\Framework\App\Route\Config
class\Magento\Framework\App\RequestInterface $request
: same goes for this class, the preference isMagento\Framework\App\Request\Http
\Magento\Framework\Url\SecurityInfoInterface $urlSecurityInfo
: same case here again withMagento\Framework\Url\SecurityInfo\Proxy
as the preference\Magento\Framework\Url\ScopeResolverInterface $scopeResolver
: here we start with the interesting bit. Inapp/etc/di.xml
a preference is defined for this interface and it is theMagento\Framework\Url\ScopeResolver
class. However, for theMagento\Backend\Model\Url
Magento 2 needs to use another class and thus it defines which class in thedi.xml
you posted soMagento\Backend\Model\Url\ScopeResolver
will be used.\Magento\Framework\Session\Generic $session
this is a normal class and thus can be used as it.\Magento\Framework\Session\SidResolverInterface $sidResolver
: back to an interface, the preference is still defined inapp/etc/di.xml
and it isMagento\Framework\Session\SidResolver\Proxy
\Magento\Framework\Url\RouteParamsResolverFactory $routeParamsResolverFactory
: this is a factory class so it can be used as it.\Magento\Framework\Url\QueryParamsResolverInterface $queryParamsResolver
: back to ourapp/etc/di.xml
and the preference isMagento\Framework\Url\QueryParamsResolver
\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
: another case here where **a preference is defined inapp/etc/di.xml
and it isMagento\Framework\App\Config
.$scopeType
: here we only have a variable without any class in front of it. Your moduledi.xml
specifies thatMagento\Store\Model\ScopeInterface::SCOPE_STORE
should be used as the value of this variable.**\Magento\Backend\Helper\Data $backendHelper
: here we could have use that class as it. However here a proxy is used because this class is not necessarily being used (see this post for details about proxy classes: Magento 2: practical explanation of what is a proxy class? )\Magento\Backend\Model\Menu\Config $menuConfig
: we can use this class as it.\Magento\Framework\App\CacheInterface $cache
: another preference defined inapp/etc/di.xml
for this interface which isMagento\Framework\App\Cache\Proxy
\Magento\Backend\Model\Auth\Session $authSession
: same again here we could have used the class as it but we use a proxy class instead for lazy loading.\Magento\Framework\Encryption\EncryptorInterface $encryptor
: jumping to theapp/etc/di.xml
again and we findMagento\Framework\Encryption\Encryptor
as a preference\Magento\Store\Model\StoreFactory $storeFactory
: a factory so we can use it as it.\Magento\Framework\Data\Form\FormKey $formKey
: here we use a theMagento\Framework\Data\Form\FormKey\Proxy
proxy class again for lazy loading.array $data = []
: this one always comes last and is automatically defaulted to an empty array you can find more information here: Magento 2: what is the $data array constructor parameter?To summarize
Globally, classes constructors parameters are interfaces or non instantiable classes. Thus
di.xml
lets you tailor the dependencies you want to use for each class constructor. It's also valid for instantiable classes. For example a class constructor that takes a product class as a constructor argument. It can be tailored in the configurable product module so it takes a configurable product class instead as an argument.