Как Magento2 генерирует конкретные ExtensionFactory и ExtensionAttributeInterface?
-
19-12-2019 - |
Вопрос
Я хотел бы осмыслить использование атрибутов расширения, например, для элементов цитат.
Нет проблем добавить пользовательский атрибут к такой сущности с помощью класса настройки, как в Magento 1, этот вопрос не об этом.
В настоящий момент меня охватывает волшебство, когда я хочу представить такой атрибут, который был добавлен расширением через API сущностей, в качестве атрибута расширения.
ОБНОВЛЯТЬ: Я знаю, как генерируются обычные фабрики.Этот вопрос касается специальных фабрик, которые создают экземпляры сгенерированных реализаций для сгенерированных интерфейсов атрибутов расширения.
Вот шаги, которые я предпринимаю, чтобы заставить его работать.Я добавляю их, чтобы тому, кто попытается ответить, не нужно было вдаваться в эти детали.
Мой вопрос КАК или ПОЧЕМУ оно работает.
Шаги по предоставлению атрибута расширения через API сущности:
- Создать
etc/extension_attributes.xml
который добавляет атрибут в интерфейс объекта - Создайте плагин для добавления значения атрибута к объектам.
ExtensionAttributes
пример.
Для выполнения второго пункта сущности ExtensionAttributes
экземпляр нужен.По этой причине плагин зависит от фабрики, которую менеджер объектов предоставляет через DI.
Для примера позиции котировки Magento\Quote\Api\Data\CartItemExtensionFactory
должен быть использован.
Я предполагаю, что тип этой фабрики каким-то образом должен стать триггером магии генерации.
Затем Magento генерирует соответствующий интерфейс. \Magento\Quote\Api\Data\CartItemExtensionInterface
с сеттерами и геттерами для всех атрибутов расширения.
Однако, похоже, он не создает конкретную реализацию этого интерфейса.По крайней мере, PHPStorm этого не видит.
Как Magento собирает информацию, необходимую для создания класса?Как можно вызвать сгенерированные методы интерфейса в конкретном экземпляре?Это класс, который генерируется только в памяти?
Я рад, что это работает, но это не совсем удовлетворяет.Возможность Magentos использовать атрибуты, автоматически создаваемые расширениями, является одним из ключевых факторов ее успеха.Я, как разработчик модулей, считаю, что мне необходимо глубокое понимание всего процесса.
Если бы у меня было время, я бы сам покопался в этом, но я бы предпочел, чтобы я просто мог получить объяснение.
ОБНОВЛЕНИЕ 2:Потребовалось немного времени, чтобы прочитать \Magento\Framework\Api\Code\Generator\ExtensionAttributesInterfaceGenerator
и \Magento\Framework\Api\Code\Generator\ExtensionAttributesGenerator
.Теперь я хотя бы примерно представляю, что происходит.Если меня никто не опередит, я однажды напишу описание всего процесса, так как думаю, что это будет полезным справочником.
Решение
Прежде всего, автогенерация происходит на основе суффикса имени класса, например. Factory
, ExtensionInterface
(видеть \Magento\Framework\Api\Code\Generator\ExtensionAttributesInterfaceGenerator::EXTENSION_INTERFACE_SUFFIX
) или Extension
(видеть \Magento\Framework\Api\Code\Generator\ExtensionAttributesGenerator::EXTENSION_SUFFIX
).
Правильный генератор выбирается на основе суффикса здесь \Magento\Framework\Code\Generator::generateClass
.
Предположим, режим Magento developer
а отсутствующие классы могут быть созданы на лету (аналогичный процесс произойдет при использовании компилятора).Когда диспетчер объектов пытается создать экземпляр, скажем Magento\Quote\Api\Data\CartItemExtensionFactory
а его нет, происходит следующее:
- Автозагрузчику не удается создать экземпляр класса и здесь инициирует генерацию кода.
\Magento\Framework\Code\Generator\Autoloader::load
- Тогда суффикс класса определяется как
Factory
(список всех заявленных суффиксов можно найти здесь\Magento\Framework\ObjectManager\DefinitionFactory::getCodeGenerator
) и соответствующий класс генератора Factory (Magento\Framework\ObjectManager\Code\Generator\Factory
) используется для создания отсутствующей фабрики - Все автоматически сгенерированные классы всегда основаны на других классах, в случае фабрики имя исходного класса вычисляется просто путем удаления
Factory
суффикс, это будетMagento\Quote\Api\Data\CartItemExtension
.Этот класс не существует, и автогенерация снова вызывается автозагрузчиком, но на этот раз для класса расширения. - Теперь суффикс
Extension
и\Magento\Framework\Api\Code\Generator\ExtensionAttributesGenerator
будет использоваться для создания этого класса - Исходный класс для генерации класса расширения рассчитывается как
Magento\Quote\Api\Data\CartItemInterface
, он существует и класс расширения успешно создан.Однако при попытке включить файл класса расширения автогенерация запускается еще раз, посколькуMagento\Quote\Api\Data\CartItemExtension
реализуетMagento\Quote\Api\Data\CartItemExtensionInterface
, которого не существует - Суффикс
ExtensionInterface
и\Magento\Framework\Api\Code\Generator\ExtensionAttributesInterfaceGenerator
будет использоваться для генерации - Классы ExtensionInterface и Extension генерируются на основе информации из
extension_attributes.xml
, доступный через\Magento\Framework\Api\ExtensionAttribute\Config
, затем генерируется Factory
Важно отметить, что ExtensionInterface не имеет предпочтений в di.xml
потому что и Extension, и ExtensionInterface генерируются автоматически.Это не проблема, поскольку не предполагается, что ExtentionInterface будет внедрен напрямую через конструкцию.
Другие советы
Сегодня вечером поверх ответа @Alex я вижу строки
$modelReflection = new \ReflectionClass($extensibleClassName);
if ($modelReflection->isInterface()
&& $modelReflection->isSubclassOf(self::EXTENSIBLE_INTERFACE_NAME)
&& $modelReflection->hasMethod('getExtensionAttributes')
) {
$this->classInterfaceMap[$extensibleClassName] = $extensibleClassName;
return $this->classInterfaceMap[$extensibleClassName];
}
в классе \Magento\Framework\Api\ExtensionAttributesFactory
здесь мы можем начать отладку, если интерфейс расширения не генерируется.Атрибуты расширения в значительной степени связаны со структурированием нашего класса, как этого ожидает Magento 2.
эти строки говорят:
это класс в нашем Extension_attributes интерфейс
расширяет ли он \Magento\Framework\Api\ExtensibleDataInterface
имеет этот интерфейс функцию getExtensionAttributes