Un “type virtuel” est un mécanisme de l’injection de dépendances de Magento 2. Il permet de changer les arguments d’un constructeur d’un objet spécifique, donc de gérer ses dépendances, sans affecter les autres classes qui ont elles-même une dépendance à cet objet. Dans les faits un virtual type n’est rien d’autre qu’une classe étendant une autre classe.

Il ne faut cependant pas interpréter un virtual type comme étant un moyen de surcharger une classe en modifiant l’une ou l’autre fonction de la classe étendue. Le virtual type n’est pas voué à changer le comportement d’un objet, mais seulement ses dépendances.

Surcharge de classe

La surcharge d’une classe permet de modifier le comportement de l’objet. Par exemple, déclarons la surcharge de classe suivante à l’aide du fichier di.xml de notre module :

<!-- MyVendor/MyModule/etc/di.xml -->
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <preference for="Magento\Customer\Model\Session"          
        type="MyVendor\MyModule\Model\Session" />
</config>
<?php
// MyVendor/MyModule/Model/Session.php
namespace MyVendor\MyModule\Model;

class Session extends \Magento\Customer\Model\Session
{
    // Modification du comportement de la méthode logout
    public function logout() {}
}

La surcharge d’une classe passe également par le pattern d’injection de dépendances de Magento 2. Nous avons déclaré ici une préférence d’injection de dépendances pour la classe Magento\Customer\Model\Session. Cette configuration XML informe Magento que lorsque la classe Magento\Customer\Model\Session est requise en dépendance d’un objet, la classe MyVendor\MyModule\Model\Session de notre module sera injectée à sa place. Cette configuration est globale et s’applique donc à l’ensemble des classes de tous les modules ayant cette dépendance.

Virtual types

Les virtual types permettent de modifier les dépendances d’un objet. Admettons la configuration XML suivante :

<!-- MyVendor/MyModule/etc/di.xml -->
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <virtualType name="mySessionStorage" type="Magento\Customer\Model\Session\Storage">
        <arguments>
            <argument name="namespace"  xsi:type="string">my_customer_namespace</argument>
        </arguments>
    </virtualType>

    <virtualType name="mySessionModel" type="Magento\Customer\Model\Session">
        <arguments>
            <argument name="storage"  xsi:type="object">mySessionStorage</argument>
        </arguments>
    </virtualType>

    <type name="MyVendor\MyModule\Helper\Data">
        <arguments>
            <argument name="session" xsi:type="object">mySessionModel</argument>
        </arguments>
    </type>
</config>
 
<?php
//MyVendorMyModuleHelperData.php
namespace MyVendor\MyModule\Helper;
class Data
{
    public function __construct(
        \Magento\Customer\Model\Session $session
    ) {}
}

Nous avons déclaré ici deux virtuals types. Le premier s’appelle mySessionStorage et déclare un type virtuel sur la classe Magento\Customer\Model\Session\Storage. Cette déclaration permet de modifier la valeur de l’argument $namespace par ‘my_customer_namespace’.

Le deuxième s’appelle mySessionModel et déclare un type virtuel sur la classe Magento\Customer\Model\Session. Cette déclaration permet de modifier sa dépendance (l’argument $storage du constructeur) en lui passant à présent notre type virtuel mySessionStorage.

Enfin, la troisième déclaration change la dépendance à la Session (l’argument $session du constructeur) de notre classe Helper\Data par le type virtuel mySessionModel.

En procédant ainsi, nous nous assurons que seule notre classe Helper\Data verra sa dépendance Customer\Session modifiée. Toutes les autres classes de tous les autres modules ne seront jamais impactées par cette préférence d’injection de dépendances.