¿Cómo genera Magento2 el ExtensionFactory y el ExtensionAttributeInterface específicos?

28

Me gustaría entender cómo usar los atributos de extensión, por ejemplo, para los elementos de cotización.
No es ningún problema agregar un atributo personalizado a dicha entidad utilizando una clase de configuración como en Magento 1, de esto no se trata esta pregunta.
En este momento, la magia me abruma cuando quiero exponer un atributo que fue agregado por una extensión a través de la API de entidades como un atributo de extensión.

ACTUALIZACIÓN : Sé cómo se generan las fábricas regulares. Esta pregunta trata sobre las fábricas especiales que crean instancias de las implementaciones generadas para las interfaces de atributos de extensión generadas.

Estos son los pasos que tomo para que funcione. Estoy agregando estos para que quien intente responder no necesite entrar en esos detalles.
Mi pregunta es CÓMO o POR QUÉ funciona.

Pasos para exponer un atributo de extensión a través de una API de entidad:

  1. Cree un etc/extension_attributes.xmlque agregue el atributo a la interfaz de la entidad
  2. Cree un complemento para agregar el valor del atributo a la ExtensionAttributesinstancia de las entidades .

Para hacer el segundo punto, ExtensionAttributesse necesita la instancia de las entidades . Por esta razón, el complemento depende de una fábrica, que el administrador de objetos suministra a través de DI.

Para el ejemplo del artículo de presupuesto Magento\Quote\Api\Data\CartItemExtensionFactorydebe usarse.
Supongo que el tipo de esta fábrica de alguna manera debe ser el detonante de la magia de la generación.

Luego, Magento genera la interfaz correspondiente \Magento\Quote\Api\Data\CartItemExtensionInterfacecon los establecedores y captadores para todos los atributos de extensión.
Sin embargo, no parece generar la implementación concreta para esa interfaz. Al menos, PHPStorm no lo está viendo.

¿Cómo recopila Magento la información que necesita para generar la clase? ¿Cómo se pueden invocar los métodos de interfaz generados en una instancia concreta? ¿Es una clase que solo se genera en la memoria?

Estoy feliz de que funcione, pero eso no es realmente satisfactorio. La capacidad de Magentos para usar atributos creados automáticamente por extensiones es un factor clave para su éxito. Como desarrollador de módulos, creo que necesito una comprensión profunda de todo el proceso.
Si tuviera tiempo, simplemente investigaría esto yo mismo, pero preferiría si pudiera obtener una explicación.

ACTUALIZACIÓN 2 : Tomó un poco de tiempo para leer \Magento\Framework\Api\Code\Generator\ExtensionAttributesInterfaceGeneratory \Magento\Framework\Api\Code\Generator\ExtensionAttributesGenerator. Ahora al menos tengo una idea aproximada de lo que está sucediendo. Si nadie me gana, escribiré una descripción del proceso completo en un punto, ya que creo que sería una referencia útil.

Vinaí
fuente
2
hizo The Vinai .. hizo la pregunta ..Omg
Amit Bera

Respuestas:

26

En primer lugar, la autogeneración se produce en función del sufijo de nombre de clase, por ejemplo Factory, ExtensionInterface(ver \Magento\Framework\Api\Code\Generator\ExtensionAttributesInterfaceGenerator::EXTENSION_INTERFACE_SUFFIX) o Extension(ver \Magento\Framework\Api\Code\Generator\ExtensionAttributesGenerator::EXTENSION_SUFFIX).

El generador adecuado se selecciona en función del sufijo aquí \Magento\Framework\Code\Generator::generateClass.

Supongamos que el modo Magento es developery que las clases faltantes se pueden generar sobre la marcha (un proceso similar ocurrirá cuando se use el compilador). Cuando el administrador de objetos intenta crear una instancia, digamos Magento\Quote\Api\Data\CartItemExtensionFactoryy no existe, sucede lo siguiente:

  1. El autocargador falla al instanciar la clase e inicia la generación de código aquí \Magento\Framework\Code\Generator\Autoloader::load
  2. Luego, el sufijo de clase se determina como Factory(la lista de todos los sufijos declarados se puede encontrar aquí \Magento\Framework\ObjectManager\DefinitionFactory::getCodeGenerator) y la clase de generador Factory correspondiente ( Magento\Framework\ObjectManager\Code\Generator\Factory) se usa para generar la fábrica que falta
  3. Todas las clases autogeneradas siempre se basan en otras clases, en caso de fábrica, el nombre de la clase fuente se calcula simplemente eliminando el Factorysufijo, lo será Magento\Quote\Api\Data\CartItemExtension. Esta clase no existe y el autocargador invoca una vez más la autogeneración, pero esta vez para la clase Extension
  4. Ahora el sufijo es Extensiony \Magento\Framework\Api\Code\Generator\ExtensionAttributesGeneratorserá usado para generar esta clase
  5. La clase de origen para la generación de la clase de extensión se calcula como Magento\Quote\Api\Data\CartItemInterfaceexiste y la clase de extensión se genera correctamente. Sin embargo, en el intento de incluir el archivo de clase de extensión, la autogeneración se activa una vez más porque se Magento\Quote\Api\Data\CartItemExtensionimplementa Magento\Quote\Api\Data\CartItemExtensionInterface, lo que no existe
  6. El sufijo es ExtensionInterfacey \Magento\Framework\Api\Code\Generator\ExtensionAttributesInterfaceGeneratorserá usado para la generación
  7. Las clases ExtensionInterface y Extension se generan en función de la información extension_attributes.xml, accesible a través de \Magento\Framework\Api\ExtensionAttribute\Config, luego se genera Factory

Una nota importante es que no hay preferencia por ExtensionInterface di.xmlporque tanto Extension como ExtensionInterface se generan automáticamente. Esto no es un problema porque no se espera que ExtentionInterface se inyecte a través de la construcción directamente.

Alex Paliarush
fuente
@Vinai, de nada. Bounty fue una agradable sorpresa, gracias. Actualización: solo para su información, si la recompensa se inició después de que se haya aceptado la respuesta, no se otorga automáticamente.
Alex Paliarush
0

Para mí, esta noche, además de la respuesta de @Alex, puedo ver las líneas

$modelReflection = new \ReflectionClass($extensibleClassName);
        if ($modelReflection->isInterface()
            && $modelReflection->isSubclassOf(self::EXTENSIBLE_INTERFACE_NAME)
            && $modelReflection->hasMethod('getExtensionAttributes')
        ) {
            $this->classInterfaceMap[$extensibleClassName] = $extensibleClassName;
            return $this->classInterfaceMap[$extensibleClassName];
        }

en la clase \Magento\Framework\Api\ExtensionAttributesFactory

es donde es posible que queramos comenzar a depurar si no se genera la interfaz de extensión. Casi todos los atributos de extensión se trata de estructurar nuestra clase como Magento 2 esperará.

Estas líneas dicen:

  • es la clase en nuestra extension_attributes una interfaz

  • ¿se extiende \ Magento \ Framework \ Api \ ExtensibleDataInterface

  • tiene esta interfaz una función llamada getExtensionAttributes

Herve Tribouilloy
fuente