Magento 2 Virtual Types with namespaces
I've heard it pop up in discussions on best coding practices. Yet, I have also seen the opposite in practical code samples while reviewing extensions: Virtual Types should use namespaces. Here's a small recap on what, why and how.
What are Virtual Types?
Within Magento 2, classes can depend on each other using constructor-based Dependency Injection. And instead of only allowing static dependencies (class A injects class B), Magento offers a configuration system that allows one dependency to be replaced with another (class B is swapped out for class C). One of these configurations is Virtual Types.
Virtual Types are defined in a file
di.xml which might be located in numerous places - for instance, the
etc/ folder of your own module. Virtual Types are in essence new PHP classes (but actually they are not, they are just links), that extend upon their original class while overriding the original class its constructor arguments by adding those in the
Create a new PHP child class or a Virtual Type?
Once you realize that a Virtual Types is nothing more than a new PHP child object (as if there was an actual class generating it), it makes you wonder why you should do this through XML. Maybe it is easier to simply create a new PHP class in your module and modify things there? The end result is the same: There is a new object of a new type. (Note that this new class still needs to be used somewhere else to become useful. Typically this is done by using an XML Type to modify the constructor arguments of yet another class and inject this new virtual class in it.)
I personally favour new PHP classes over new Virtual Types. However, once the original class has a lengthy constructor, a new PHP class would require you to duplicate all parent dependencies in its own constructor and pass them on to its parent - and perhaps all of that trouble is only needed for replacing one of those dependencies. A Virtual Type is quicker: It requires some XML, yes, but it allows you to single out only that dependency that you actually need to be replaced. The more complex the original constructor, the better it is to use a Virtual Type. (That being said, the more complex the original constructor, the more this original constructor needs to be cleaned up - with references to SOLID.)
Virtual Types with namespaces
Now let's go to the main point of this blog: Virtual Types are identical to PHP classes created on the fly by the Object Manager. And just like all PHP classes, we have specific rules to stick to and namespacing is one of them. So why not use namespaces?
Let's take a dummy example without namespaces:
<virtualType name="yireoVirtualSomeClass" type="Yireo\Example\Some\Class"> </virtualType>
And now let's see a namespaced version:
<virtualType name="Yireo\Example\Some\Class\Virtual" type="Yireo\Example\Some\Class"> </virtualType>
To me, the namespaced version looks a lot cleaner. Remember that defining this Virtual Type is only half of the story - if you don't intend to use it elsewhere, it just as well can be removed again. It only becomes useful once it is applied elsewhere, for instance using a Type:
<type name="Magento\Framework\Some\Existing\Class"> <arguments> <argument name="someDep" xsi:type="object">Yireo\Example\Some\Class\Virtual</argument> </arguments> </type>
Once others start debugging the class
Magento\Framework\Some\Existing\Class, they might bump into the
someDep argument and now, thanks to namespaces, the name of this Virtual Type identifies exactly who put that dependency there. This is why we have namespaces.
Virtual in it?
However, this might also become confusing if the Virtual Type actually looks too similar to a PHP class. I always tend to click through my PhpStorm environment with the
generated/ folder excluded from my project. Once in a while, I bump into a class that is not there. And if Magento does not die at that moment, I assume it is something that is generated. Once the class has the word
Proxy in it, this confirms my assumption. Wouldn't it make sense to also include the word
Virtual in the namespaced name of a Virtual Type?
This leads to the following classes that would suggest that the PHP class actually is a VirtualType:
Yireo\Example\Some\Class\Virtual Yireo\Example\Virtual\Some\Class Yireo\Example\Some\ClassVirtual Yireo\Example\VirtualType\Some\Class Yireo\Example\Some\Class\VirtualType
Obviously, there are many more variations. But just make sure to add the word
Virtual in there.
Let's namespace Virtual Types!
Having discussed this already many times with other devs, it makes sense to say that Virtual Types should be namespaced. Hopefully, this blog explained a bit more on the why and how.
Yireo is part of the ExtDN group and as such, we are trying to help the community by improving coding standards for extensions. This specific issue on Virtual Type naming has been dealt with in the following GitHub issue: https://github.com/extdn/extdn-phpcs/issues/9 If you feel to chime in on this issue or other issues, please do.