Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Внедрение зависимостей

Внедрение зависимостей — способ передать классу объекты, которые ему нужны для работы. В Magento объекты не создают словом new: класс перечисляет нужное в конструкторе, а специальная часть ядра — менеджер объектов — создаёт и передаёт всё сама. Вы уже пользовались этим, когда действие получало ResultFactory.

Свой класс-служба

Создадим класс, который формирует приветствие и пишет строку в журнал. Классы с логикой кладут в папку Model модуля; файл создают вручную.

// app/code/Acme/Hello/Model/Greeter.php
namespace Acme\Hello\Model;
 
use Psr\Log\LoggerInterface;
 
class Greeter
{
    public function __construct(private LoggerInterface $logger) {}
 
    public function greet(string $name): string
    {
        $this->logger->info('Приветствие для ' . $name);
 
        return 'Здравствуйте, ' . $name . '!';
    }
}

Класс просит журнал по типу LoggerInterface. Регистрировать Greeter нигде не нужно: Magento создаст его, когда он кому-то понадобится.

Использование

Чтобы получить Greeter, другой класс тоже просит его в конструкторе:

// app/code/Acme/Hello/ViewModel/Greeting.php
namespace Acme\Hello\ViewModel;
 
use Acme\Hello\Model\Greeter;
use Magento\Framework\View\Element\Block\ArgumentInterface;
 
class Greeting implements ArgumentInterface
{
    public function __construct(private Greeter $greeter) {}
 
    public function getGreeting(): string
    {
        return $this->greeter->greet('Анна');
    }
}

Выведите в шаблоне $escaper->escapeHtml($viewModel->getGreeting()), удалите содержимое папки generated и откройте /hello. На странице появится «Здравствуйте, Анна!», а в файле var/log/system.log — новая строка.

Как Magento выбирает объект

Менеджер объектов смотрит на типы параметров конструктора. Если тип — класс, создаётся объект этого класса. Если тип — интерфейс, нужно правило, какой класс за ним стоит. Такое правило называют предпочтением (preference) и записывают в файле di.xml. Для LoggerInterface оно уже есть в ядре.

Свой интерфейс и предпочтение

Интерфейсы модуля по соглашению лежат в папке Api:

// app/code/Acme/Hello/Api/GreeterInterface.php
namespace Acme\Hello\Api;
 
interface GreeterInterface
{
    public function greet(string $name): string;
}

Допишите к классу Greeter слова implements \Acme\Hello\Api\GreeterInterface и создайте файл etc/di.xml:

<?xml version="1.0"?>
<!-- app/code/Acme/Hello/etc/di.xml -->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <preference for="Acme\Hello\Api\GreeterInterface" type="Acme\Hello\Model\Greeter"/>
</config>

Теперь в конструкторе Greeting можно просить GreeterInterface — придёт объект Greeter. После правки di.xml выполните bin/magento cache:clean. Польза в том, что другой модуль может подставить свою реализацию, не меняя ваш код.

app/code/Acme/Hello/
├── Api/
│   └── GreeterInterface.php    новый файл
├── Model/
│   └── Greeter.php             новый файл
├── ViewModel/Greeting.php
└── etc/
    ├── di.xml                  новый файл
    └── module.xml

Где действует di.xml

ФайлГде действует
etc/di.xmlВезде
etc/frontend/di.xmlТолько на витрине
etc/adminhtml/di.xmlТолько в панели управления

Сгенерированные классы

Для работы внедрения Magento создаёт вспомогательные классы в папке generated. В режиме разработчика это происходит на лету, поэтому после изменения конструктора папку достаточно очистить. На рабочем сервере всё собирают заранее командой bin/magento setup:di:compile.

Частая ошибка

Вызов ObjectManager::getInstance()->get(...) прямо в своём классе работает, но ломает всю идею: зависимости становятся невидимыми, их нельзя заменить через di.xml и проверить тестом. Всё, что нужно классу, перечисляйте в конструкторе.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.