Arguments, virtual types and factories
The di.xml file can do more than bind an interface to a class. It passes strings, numbers and arrays into a constructor and creates variants of a class without a new PHP file, while generated factories and proxies control when an object is created.
Constructor arguments
Magento cannot pick a string or a number by type — the value is set in di.xml. Add a parameter to the Greeter constructor:
// app/code/Acme/Hello/Model/Greeter.php
public function __construct(
private LoggerInterface $logger,
private string $greeting = 'Hello',
) {}<!-- app/code/Acme/Hello/etc/di.xml -->
<type name="Acme\Hello\Model\Greeter">
<arguments>
<argument name="greeting" xsi:type="string">Good afternoon</argument>
</arguments>
</type>The argument name equals the constructor parameter name without the dollar sign. If the greet() method uses $this->greeting, then after bin/magento cache:clean the /hello page shows "Good afternoon, Anna!".
xsi:type value | What is passed |
|---|---|
string, number, boolean | A simple value |
object | An object of the given class |
array | An array of item elements |
An array
<!-- app/code/Acme/Hello/etc/di.xml -->
<type name="Acme\Hello\Model\Greeter">
<arguments>
<argument name="names" xsi:type="array">
<item name="anna" xsi:type="string">Anna</item>
<item name="john" xsi:type="string">John</item>
</argument>
</arguments>
</type>The constructor receives an array $names parameter with two elements. Another module can add its own item to this array in its own di.xml — many core extension points work this way.
A virtual type
A virtual type is an existing class with different arguments that was given its own name. It needs no PHP file. A common example is a logger that writes to a separate file:
<!-- app/code/Acme/Hello/etc/di.xml -->
<virtualType name="Acme\Hello\Logger\Handler" type="Magento\Framework\Logger\Handler\Base">
<arguments>
<argument name="fileName" xsi:type="string">/var/log/hello.log</argument>
</arguments>
</virtualType>
<virtualType name="Acme\Hello\Logger" type="Magento\Framework\Logger\Monolog">
<arguments>
<argument name="name" xsi:type="string">hello</argument>
<argument name="handlers" xsi:type="array">
<item name="default" xsi:type="object">Acme\Hello\Logger\Handler</item>
</argument>
</arguments>
</virtualType>
<type name="Acme\Hello\Model\Greeter">
<arguments>
<argument name="logger" xsi:type="object">Acme\Hello\Logger</argument>
</arguments>
</type>The classes Acme\Hello\Logger and Acme\Hello\Logger\Handler do not exist on disk — these are virtual type names. Now Greeter writes to the var/log/hello.log file, and other classes write to the common log.
Factories
An object from the constructor is one per request: all classes receive the same instance. When a new object is needed every time — a product, a review, a record — ask for a factory. Its name is the class name plus the word Factory:
// app/code/Acme/Hello/Model/DraftCreator.php
namespace Acme\Hello\Model;
use Magento\Catalog\Model\ProductFactory;
class DraftCreator
{
public function __construct(private ProductFactory $productFactory) {}
public function create(string $sku): \Magento\Catalog\Model\Product
{
$product = $this->productFactory->create();
$product->setSku($sku);
return $product;
}
}Every create() call returns a new empty product. You do not write the ProductFactory file: Magento generates it in the generated folder.
Proxies
Some objects are expensive to create and are not needed on every call. A proxy is a generated wrapper that creates the real object only on first use. It is substituted in di.xml by appending \Proxy to the class name:
<!-- app/code/Acme/Hello/etc/di.xml -->
<type name="Acme\Hello\ViewModel\Greeting">
<arguments>
<argument name="greeter" xsi:type="object">Acme\Hello\Model\Greeter\Proxy</argument>
</arguments>
</type>The code of the Greeting class does not change.
Common mistake
The words Factory and Proxy are put in the wrong place. A factory is named as a parameter type in the PHP constructor, while a proxy appears only in di.xml. The Greeter\Proxy type in a constructor makes the dependency on a generated class explicit and prevents replacing it.