Как добавить новые зависимости в классы

Иногда нам нужно добавить новые зависимости в классы, и нам нужно это сделать обратносовместимым. В этой статье я собираюсь описать как это сделать. Я также расскажу о типичных ошибках.

И так, давай начнём 😎

Введение

На продакшен окружении, Magento должна работать на файловой системе сконфигурированной только для чтения, это рекомендуется с точки зрения безопасности.

На запись доступны только следующие каталоги:

  • app/etc
  • pub/static
  • pub/media
  • var

Важное замечание: папка с сгенерированными классами была перемещена с var/generation в generated/code. Сейчас, эта папка не доступна на запись в продакшене. Эти папки имеют аналогичные разрешения на запись в облачной инфраструктуре Magento.

Итак, если ты добавишь зависимость неправильно, то Magento может поломаться.

Класс в точке доступа

Разработчик пишет класс в точке доступа и добавляет в конструктор зависимость на генерируемую фабрику.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
use YourVendor\SomeModule\Model\GeneratedFactory;

require realpath(__DIR__) . '/../app/bootstrap.php';
$bootstrap = \Magento\Framework\App\Bootstrap::create(BP, $_SERVER);

class SomeClass
{
    private $generatedFactory;

    public function __construct(GeneratedFactory $generatedFactory)
    {
        $this->generatedFactory = $generatedFactory;
    }

    // Some code here...
}

$someObject = $bootstrap->getObjectManager()->create(SomeClass::class);

// There is some code that uses $someObject

Этот пример будет работать в режиме разработчика, GeneratedFactory будет генерироваться на лету. Но в продакшен режиме, на файловой системе только для чтения, ты столкнёшься с ошибкой, что GeneratedFactory не может быть сохранена в каталоге generateted/code.

Это происходит потому, что Magento не сканирует точки входа во время работы команды bin/magento setup:di:compile. И если точка входа содержит определение генерируемых классов, то они будут проигнорированы.

Есть два пути как это исправить.

Как это исправить?

Первый путь - это создать реально существующий класс фабрики в файловой системе.

Второй путь - это переместить класс SomeClass в папку app/code/YourVendor/SomeModule. Тогда сканер кода Magento найдёт в классе SomeClass зависимость на генерируемую фабрику GeneratedFactory и генератор кода сгенерирует эту фабрику.

Пример класса SomeClass:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<?php
namespace YourVendor\SomeModule;

use YourVendor\SomeModule\Model\GeneratedFactory;

class SomeClass
{
    private $generatedFactory;

    public function __construct(GeneratedFactory $generatedFactory)
    {
        $this->generatedFactory = $generatedFactory;
    }

    // Some code here...
}

Редактируем точку входа my_api/index.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<?php

use YourVendor\SomeModule\SomeClass;

require realpath(__DIR__) . '/../app/bootstrap.php';
$bootstrap = \Magento\Framework\App\Bootstrap::create(BP, $_SERVER);

$someObject = $bootstrap->getObjectManager()->create(SomeClass::class);

// There is some code that uses $someObject

Неправильное добаление зависимости

Неправильное добавление зависимости на генерируемый класс в существующий класс. Например, следующий код будет корректно работать в режиме разработчика, но завершиться неудачей в продакшен режиме на файловой система только для чтения.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
<?php
namespace YourVendor\SomeModule;

use YourVendor\SomeModule\Model\GeneratedFactory;
use Magento\Framework\App\ObjectManager;

class SomeClass
{
    private $generatedFactory;
    private $someParam;

    public function __construct($someParam)
    {
        $this->someParam = $someParam;
        $this->generatedFactory = ObjectManager::getInstance()->get(GeneratedFactory::class);
    }

    // Some code here...
}

Как это исправить?

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

Здесь также есть два способа как это исправить.

Первый путь - это создать реально существующий класс фабрики в файловой системе.

Второй путь - это немного изменить конструктор SomeClass

Пример класса SomeClass:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
<?php
namespace YourVendor\SomeModule;

use YourVendor\SomeModule\Model\GeneratedFactory;
use Magento\Framework\App\ObjectManager;

class SomeClass
{
    private $generatedFactory;
    private $someParam;

    public function __construct($someParam, GeneratedFactory $generatedFactory = null)
    {
        $this->someParam = $someParam;
        $this->generatedFactory = $generatedFactory ?: ObjectManager::getInstance()->get(GeneratedFactory::class);
    }

    // Some code here...
}

В этом случае:

  • мы добавили новую зависимость
  • мы сохранили обратную совместимость
  • генерируемый класс будет сгенерирован
  • Magento корректно работает в продакшен режиме на файловой системе только для чтения.

Если у тебя есть какие-либо вопросы или комментарии - пиши, с радостью отвечу 😉

Ты можешь найти пример неправильного DI в модуле Magento в этом репозитории: https://github.com/BaDos/example-wrong-di

comments powered by Disqus
Создано при помощи Hugo
Тема Stack, дизайн Jimmy