What, on earth, are virtual types?
I find the best way to think of virtual types is that they are an efficient way of making multiple flavours of a single class type without needing to declare them explicitly in your module code.
The example above is a good starting point found in the Magento wish-list module. Notice the making of a virtual type of the default Session Storage class. The difference here is Magento is creating a new flavour of the same class, but this time in a different namespace. There is a lot more going on in that file than the snippet I’ve shown above, but it’s a good starting point. Rather than having to declare an entire php class, extend the parent and give different constructor arguments, the same can be achieved by declaring it with a couple of lines in xml. During compilation, Magento will whip-up a fresh version of the parent class, but with your constructor arguments, name, namespace, the works! This unique feature of Magento 2 has become quite useful in our own projects at C3, when we wanted to create different log files in our custom modules.
As a fun project, let’s say we want to create a custom logger for each custom module we create for our Magento project. This custom logger will log to a custom log file for each module.
There were two ways to go about this.
- The more traditional approach; create a single parent custom logger class, then declare separate child classes in each of our modules, ensuring to extend the parent, and declare a different log file location for each module.
- Virtual types! Like approach 1, we can create a single parent custom logger class, however in each of our modules, simply declare a virtual type of the original, passing in the log file name to each, and let Magento compilation handle the rest.
Given that as developers, we like the most efficient (lazy) way of doing things, let’s opt with option 2.
Here are a couple of screenshots of us utilizing this approach. As you can see we’re creating virtual types of the Magento default Monolog class and filesystem handler, but creating different log file location for our new logger called \A\New\Virtual\Logger
Now Magento will perform its magic by automatically creating these two new ‘virtual’ classes for us, which inherit from the ‘type’ of class specified in the xml declaration of the virtual type.
Great, I have a virtual type. How do I use it?
Well, here it’s a little confusing, and quite rightly can be seen as a reason not to use virtual types. We need to pass in the virtual type via dependency injection in our di.xml to any class in which we are intending to use it. See the screenshot below:
As you can see we’re referencing `A\New\CustomClass’ in our di.xml, but this could be ANY class we like. Here `A\New\CustomClass’ requires a logger argument as one of these constructor arguments, and we’re passing in our newly declared virtual logger. When DI compilation is run Magento will create this class using our specified virtual logger as the logger for our custom class!
For those wanting to see a mock-up of our custom class see the screenshot below:
User Warning and Wrapping Up
Virtual types can create unintended barriers to understanding exactly what’s going on in your code for other team members. Especially if the knowledge of virtual types is limited, one can argue this is a typical example of Magento trying a little too hard to make everything as abstract as possible. This article by Alan Storm summarizes it nicely. It does, however, become quite powerful and helps reduce development time when used correctly. Just make sure it’s well documented and communicated to the rest of your team!