Type vs VirtualType

Magento usa a menudo ficheros di.xml para crear instancia de Clases de php. Primero junta todos los ficheros de esta naturaleza mezclando los nodos correspondientes y luego se crea una instancia de la Clase con sus argumentos correspondientes. A esta manera de crear instancia le han puesto el nombre de Inyección de dependencias. Las clases definidas en el fichero di.xml tienen que ser inyectables porque los objetos creados son del tipo Singleton. Hay que usar el patrón Factory para crear objeto para las clases NO inyectables, tales como las clases relacionadas con la base de datos.

Type

La etiqueta type del di.xml define cuáles son los argumentos de los parámetros del constructor de una clase. Es importante aclarar la diferencia entre argumento y parámetro. El argumento es un valor concreto que pasamos al constructor del objeto y los parámetros son variables definidas en el constructor de la clase. Un ejemplo muy bueno son las opciones de Elasticsearch a elegir para el motor de indexación en el Backoffice, en Stores > Settings > Configuration > Catalog > Catalog > Catalog Search, en la sección Search Engine. Elasticsearch tiene varias versiones. Para crear este seleccionable, se pasa el nombre de cada versión a través del fichero di.xml de cada módulo de Elasticsearch.

    
        # En cada módulo de elasticsearch le pasan un argumento Elasticsearch n.
        # Ruta: app/code/Magento/Elasticsearch7/etc/di.xml

        <type name="Magento\Search\Model\Adminhtml\System\Config\Source\Engine">
            <arguments>
                <argument name="engines" xsi:type="array">
                    <item sortOrder="30" name="elasticsearch7" xsi:type="string">Elasticsearch 7</item>
                </argument>
            </arguments>
        </type>

        # Ruta: app/code/Magento/Elasticsearch6/etc/di.xml

        <type name="Magento\Search\Model\Adminhtml\System\Config\Source\Engine">
            <arguments>
                <argument name="engines" xsi:type="array">
                    <item sortOrder="20" name="elasticsearch6" xsi:type="string">Elasticsearch 6.x (Deprecated)</item>
                </argument>
            </arguments>
        </type>

        # Ruta: app/code/Magento/Elasticsearch/etc/di.xml

        <type name="Magento\Search\Model\Adminhtml\System\Config\Source\Engine">
            <arguments>
                <argument name="engines" xsi:type="array">
                    <item sortOrder="10" name="elasticsearch5" xsi:type="string">Elasticsearch 5.0+ (Deprecated)</item>
                </argument>
            </arguments>
        </type>
    

Los argumentos definidos en el fichero di.xml de los diferentes módulos de Elasticsearch pasarán al objeto de la clase Engine.

    
        # Según el orden definido en los archivos di.xml, la instancia será como la siguiente

        $diferentesVersionesDeElasticsearch = [
                    'Elasticsearch 5.0+ (Deprecated)',
                    'Elasticsearch 6.x (Deprecated)',
                    'Elasticsearch 7'
        ];

        $engine = new Engine($diferentesVersionesDeElasticsearch);
        
        # Este objeto es usado en el source_model siguiente
        # Ruta: app/code/Magento/Search/etc/adminhtml/system.xml

        <?xml version="1.0"?>
        <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
            <system>
                <section id="catalog">
                    <group id="search">
                        <field id="engine" translate="label" type="select" sortOrder="20" showInDefault="1">
                            <label>Search Engine</label>
                            <source_model>Magento\Search\Model\Adminhtml\System\Config\Source\Engine</source_model>
                        </field>
                    </group>
                </section>
            </system>
        </config>
    

VirtualType

La etiqueta Virtualtype funciona de forma muy similar al Type. Cuando definimos Virtualtype, le asignas un nombre al objeto y luego usar este nombre en la etiqueta Type. Por ejemplo: podemos crear varios virtualtypes, asignamos un nombre a cada y luego los pasamos como argumentos a una etiqueta Type. Esto es pasar objeto como argumento a un constructor de una clase.

Un ejemplo muy bueno es el módulo Magento_Cms. En el WYSIWYG podemos poner etiquetas y atributos de HTML, pero algunas etiquetas o atributos no están permitidos. Si queremos añadir etiquetas de html, tendremos que crear un Virtualtype, le asignaremos el nombre DefaultWYSIWYGValidator y luego pasaremos el nombre como argumento del constructor del objeto Magento\Framework\Validator\HTML\ConfigurableWYSIWYGValidator.

    
        # Magento_Cms, etc/di.xml
        # Definimos una instancia de la clase Magento\Framework\Validator\HTML\ConfigurableWYSIWYGValidator
        # Esta instancia tendrá el nombre DefaultWYSIWYGValidator

        <virtualType name="DefaultWYSIWYGValidator" type="Magento\Framework\Validator\HTML\ConfigurableWYSIWYGValidator">
            <arguments>
                <argument name="allowedTags" xsi:type="array">
                    <item name="div" xsi:type="string">div</item>
                    <item name="a" xsi:type="string">a</item>
                    <item name="p" xsi:type="string">p</item>
                    <item name="span" xsi:type="string">span</item>
                    <item name="em" xsi:type="string">em</item>
                    <item name="strong" xsi:type="string">strong</item>
                    <item name="ul" xsi:type="string">ul</item>
                    <item name="li" xsi:type="string">li</item>
                    <item name="ol" xsi:type="string">ol</item>
                    <item name="h5" xsi:type="string">h5</item>
                    <item name="h4" xsi:type="string">h4</item>
                    <item name="h3" xsi:type="string">h3</item>
                    <item name="h2" xsi:type="string">h2</item>
                    <item name="h1" xsi:type="string">h1</item>
                    <item name="table" xsi:type="string">table</item>
                    <item name="tbody" xsi:type="string">tbody</item>
                    <item name="tr" xsi:type="string">tr</item>
                    <item name="td" xsi:type="string">td</item>
                    <item name="th" xsi:type="string">th</item>
                    <item name="tfoot" xsi:type="string">tfoot</item>
                    <item name="img" xsi:type="string">img</item>
                    <item name="hr" xsi:type="string">hr</item>
                    <item name="figure" xsi:type="string">figure</item>
                    <item name="button" xsi:type="string">button</item>
                    <item name="i" xsi:type="string">i</item>
                    <item name="u" xsi:type="string">u</item>
                    <item name="br" xsi:type="string">br</item>
                    <item name="b" xsi:type="string">b</item>
                </argument>
                <argument name="allowedAttributes" xsi:type="array">
                    <item name="class" xsi:type="string">class</item>
                    <item name="width" xsi:type="string">width</item>
                    <item name="height" xsi:type="string">height</item>
                    <item name="style" xsi:type="string">style</item>
                    <item name="alt" xsi:type="string">alt</item>
                    <item name="title" xsi:type="string">title</item>
                    <item name="border" xsi:type="string">border</item>
                    <item name="id" xsi:type="string">id</item>
                </argument>
                <argument name="attributesAllowedByTags" xsi:type="array">
                    <item name="a" xsi:type="array">
                        <item name="href" xsi:type="string">href</item>
                    </item>
                    <item name="img" xsi:type="array">
                        <item name="src" xsi:type="string">src</item>
                    </item>
                    <item name="button" xsi:type="array">
                        <item name="type" xsi:type="string">type</item>
                    </item>
                </argument>
                <argument name="attributeValidators" xsi:type="array">
                    <item name="style" xsi:type="object">Magento\Framework\Validator\HTML\StyleAttributeValidator</item>
                </argument>
            </arguments>
        </virtualType>

        # Pasamos el objeto DefaultWYSIWYGValidator como argumento al constructor de la clase Magento\Cms\Model\Wysiwyg\Validator
        
        <type name="Magento\Cms\Model\Wysiwyg\Validator">
            <arguments>
                <argument name="validator" xsi:type="object">DefaultWYSIWYGValidator</argument>
            </arguments>
        </type>
        
        # Mapeamos la interface al objeto DefaultWYSIWYGValidator
        
        <preference for="Magento\Framework\Validator\HTML\WYSIWYGValidatorInterface" type="DefaultWYSIWYGValidator" />

    

Nota: cuando no sabemos qué valores tiene configurados en el constructor de una clase, podemos averiguarlo con el comando:

    
    bin/magento dev:di:info "Magento\Search\Model\Adminhtml\System\Config\Source\Engine"
    Constructor Parameters:
    +---------+----------------+-----------------------------------------------------------------+
    | Name    | Requested Type | Configured Value                                                |
    +---------+----------------+-----------------------------------------------------------------+
    | engines |                | {                                                               |
    |         |                |     "elasticsearch5": "string Elasticsearch 5.0+ (Deprecated)", |
    |         |                |     "elasticsearch7": "string Elasticsearch 7"                  |
    |         |                | }                                                               |
    +---------+----------------+-----------------------------------------------------------------+

    bin/magento dev:di:info "Magento\Cms\Model\Wysiwyg\Validator"

    Constructor Parameters:
    +----------------+------------------------------------------------------------+-------------------------------------+
    | Name           | Requested Type                                             | Configured Value                    |
    +----------------+------------------------------------------------------------+-------------------------------------+
    | validator      | Magento\Framework\Validator\HTML\WYSIWYGValidatorInterface | instance of DefaultWYSIWYGValidator |
    | messages       | Magento\Framework\Message\ManagerInterface                 |                                     |
    | config         | Magento\Framework\App\Config\ScopeConfigInterface          |                                     |
    | logger         | Psr\Log\LoggerInterface                                    |                                     |
    | messageFactory | Magento\Framework\Message\Factory                          |                                     |
    +----------------+------------------------------------------------------------+-------------------------------------+

        *** El configured value, son los argumentos del fichero di.xml

    

Referencias