Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

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 valueWhat is passed
string, number, booleanA simple value
objectAn object of the given class
arrayAn 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.

Thanks for reading!

Donate to the site — we work hard for you.