Type vs VirtualType

Type

La dependencia que inyectamos en un objeto a través de su constructor o setters, es un sólo objeto que puede ser usado por diferentes módulos. La etiqueta type del di.xml tiene una función similar. Es un sólo objeto al que pasaremos argumentos en uno o varios módulos a través del di.xml Es importante aclarar la diferencia entre argumento y parámetro. El argumento es un valor concreto que pasamos a los constructores del objeto y los parámetros son variables definidas en el constructor de la clase. Un ejemplo muy bueno es los módulos Elasticsearch. Elasticsearch tiene varias versiones. Entonces Magento ha creado un módulo para cada versión. En el Backoffice hay un seleccionable de la versión de elasticsearch. Entonces mediante el fichero di.xml Magento añade la versión como opción a ese seleccionable.

    
        // del módulo Magento_Elasticsearch7, etc/di.xml. En cada módulo de elasticsearch le pasan un argumento Elasticsearch n.  
        <type name="Magento\Search\Model\Adminhtml\System\Config\Source\Engine">
            <arguments>
                <argument name="engines" xsi:type="array"> // nombre del parámetro del constructor
                    <item sortOrder="30" name="elasticsearch7" xsi:type="string">Elasticsearch 7</item>
                </argument>
            </arguments>
        </type>
    

Esta es una manera muy buena para juntar datos de distintos módulos en un sólo objeto.

VirtualType

La etiqueta Virtualtype funciona muy similar al type pero con la matiz de que cuando defines un virtualtype, le asignas un nombre al objeto creado y luego usar este nombre para pasarlo a la etiqueta type. Por ejemplo: puedes crear varios virtualtypes (varios objetos), asigas un nombre a cada objeto y luego pasas estos argumentos a alguna etiqueta type.

Un ejemplo muy bueno es el módulo cms. En los editores de los cms 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 (objeto), le asignaremos el nombre DefaultWYSIWYGValidator y luego pasaremos este nombre como argumento del constructor del objeto Magento\Framework\Validator\HTML\ConfigurableWYSIWYGValidator

    // Magento_Cms, etc/di.xml 
    
        <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>
    
    
        // del módulo Magento_Cms, etc/di.xml
        <type name="Magento\Cms\Model\Wysiwyg\Validator">
            <arguments>
                <argument name="validator" xsi:type="object">DefaultWYSIWYGValidator</argument>
            </arguments>
        </type>
    
    
        // en app/etc/di.xml
        <preference for="Magento\Framework\Validator\HTML\WYSIWYGValidatorInterface" type="DefaultWYSIWYGValidator" />
        // en el módulo Magento_Cms, etc/di.xml
        <preference for="Magento\Framework\Validator\HTML\WYSIWYGValidatorInterface" type="Magento\Cms\Model\Wysiwyg\Validator" />
    

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                          |                                     |
+----------------+------------------------------------------------------------+-------------------------------------+

En resumen:

  1. Se crea un objeto del tipo ConfigurableWYSIWYGValidator a través de la etiqueta virtualtype
  2. Luego lo pasa como argumento al constructor del modelo Magento\Cms\Model\Wysiwyg\Validator (objeto) a través de la etiqueta type
  3. En el fichero app/etc/di.xml hay un mapeo del WYSIWYGValidatorInterface con el nombre del virtualType DefaultWYSIWYGValidator y en el módulo Magento_Cms, en etc/di.xml también está el mapeo. Esto quiere decir que el segundo mapeo está sobreescribiendo el primer mapeo si inyectas la interface WYSIWYGValidatorInterface estarás usando Magento\Cms\Model\Wysiwyg\Validator. Lo que no sé es por qué Magento declara un mapeo y luego otro mapeo para sobreescribirlo. Si eliminamos el mapeo en el módulo Magento_Cms, veremos que tomará el mapeo en el app/etc/di.xml tomando el objeto instanciado por el DefaultWYSIWYGValidator.
  4. Tanto la clase Magento\Cms\Model\Wysiwyg\Validator como ConfigurableWYSIWYGValidator implementan la interface WYSIWYGValidatorInterface
  5. Si debugamos, por ejemplo, inyectando WYSIWYGValidatorInterface en alguna clase a través de su constructor y declaramos el atributo wysiwygValidator para esta interface, veremos que el atributo $this->wysiwygValidator , en este caso es del tipo objeto Magento\Cms\Model\Wysiwyg\Validator, por el mapeo en el módulo Magento_Cms en el etc/di.xml. Tiene un atributo privado llamado validator del tipo objeto Magento\Framework\Validator\HTML\ConfigurableWYSIWYGValidator. Este es el objeto instanciado por la etiqueta virtualtype con nombre DefaultWYSIWYGValidator. Entonces el método público validate() del ConfigurableWYAIWYGValidator es llamado en el método público validate() del objeto Validator. En el objeto ConfigurableWYSIWYGValidator tiene atributos privados, que son : allowedTags, allowedAttributes... etc. y son usados en el método público validate(). Los dos objetos tienen el método público validate() porque implementan la interface WYSIWYGValidatorInterface.