Аргументы, виртуальные типы и фабрики
Файл di.xml умеет больше, чем связывать интерфейс с классом. Через него в конструктор передают строки, числа и массивы, создают варианты класса без нового файла PHP, а сгенерированные фабрики и прокси управляют тем, когда объект создаётся.
Аргументы конструктора
Строку или число Magento не может подобрать по типу — значение задают в di.xml. Добавьте параметр в конструктор Greeter:
// app/code/Acme/Hello/Model/Greeter.php
public function __construct(
private LoggerInterface $logger,
private string $greeting = 'Здравствуйте',
) {}<!-- app/code/Acme/Hello/etc/di.xml -->
<type name="Acme\Hello\Model\Greeter">
<arguments>
<argument name="greeting" xsi:type="string">Добрый день</argument>
</arguments>
</type>Имя аргумента совпадает с именем параметра конструктора без знака доллара. Если метод greet() использует $this->greeting, после bin/magento cache:clean страница /hello покажет «Добрый день, Анна!».
Значение xsi:type | Что передаётся |
|---|---|
string, number, boolean | Простое значение |
object | Объект указанного класса |
array | Массив из элементов item |
Массив
<!-- 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">Анна</item>
<item name="ivan" xsi:type="string">Иван</item>
</argument>
</arguments>
</type>Конструктор получит параметр array $names с двумя элементами. Другой модуль может добавить в этот массив свой item в собственном di.xml — так устроены многие точки расширения ядра.
Виртуальный тип
Виртуальный тип — существующий класс с другими аргументами, которому дали своё имя. Файл PHP для него не нужен. Частый пример — журнал, пишущий в отдельный файл:
<!-- 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>Классов Acme\Hello\Logger и Acme\Hello\Logger\Handler на диске нет — это имена виртуальных типов. Теперь Greeter пишет в файл var/log/hello.log, а остальные классы — в общий журнал.
Фабрики
Объект из конструктора — один на весь запрос: все классы получают один и тот же экземпляр. Когда каждый раз нужен новый объект — товар, отзыв, запись, — просят фабрику. Её имя — имя класса плюс слово 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;
}
}Каждый вызов create() возвращает новый пустой товар. Файл ProductFactory писать не нужно: Magento сгенерирует его в папке generated.
Прокси
Некоторые объекты дорого создавать, а нужны они не при каждом вызове. Прокси — сгенерированная обёртка, которая создаёт настоящий объект только при первом обращении к нему. Его подставляют в di.xml, дописав к имени класса \Proxy:
<!-- 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>Код класса Greeting при этом не меняется.
Частая ошибка
Слова Factory и Proxy вписывают не туда. Фабрику указывают типом параметра в конструкторе PHP, а прокси — только в di.xml. Тип Greeter\Proxy в конструкторе делает зависимость от сгенерированного класса явной и мешает её заменить.