Comment Magento2 génère-t-il l'ExtensionFactory et l'ExtensionAttributeInterface spécifiques?

28

Je voudrais faire le tour de ma tête en utilisant des attributs d'extension, par exemple pour les éléments de devis.
Ce n'est pas un problème d'ajouter un attribut personnalisé à une telle entité en utilisant une classe d'installation comme dans Magento 1, ce n'est pas de cela qu'il s'agit.
En ce moment, la magie me submerge quand je veux exposer un tel attribut qui a été ajouté par une extension via l'API d'entités comme attribut d'extension.

MISE À JOUR : Je sais comment les usines régulières sont générées. Cette question concerne les usines spéciales qui instancient les implémentations générées pour les interfaces d'attribut d'extension générées.

Voici les étapes que je prends pour le faire fonctionner. J'ajoute ces informations afin que quiconque tente de répondre n'ait pas besoin d'entrer dans ces détails.
Ma question est COMMENT ou POURQUOI cela fonctionne.

Étapes pour exposer un attribut d'extension via une API d'entité:

  1. Créer un etc/extension_attributes.xmlqui ajoute l'attribut à l'interface d'entité
  2. Créez un plugin pour ajouter la valeur d'attribut à l' ExtensionAttributesinstance d' entités .

Pour faire le deuxième point, l' ExtensionAttributesinstance d' entités est nécessaire. Pour cette raison, le plugin dépend d'une usine, que le gestionnaire d'objets fournit via DI.

Pour l'exemple d'article de devis Magento\Quote\Api\Data\CartItemExtensionFactorydoit être utilisé.
Je suppose que le type de cette usine doit en quelque sorte être le déclencheur de la magie de génération.

Magento génère ensuite l'interface correspondante \Magento\Quote\Api\Data\CartItemExtensionInterfaceavec les setters et les getters pour tous les attributs d'extension.
Cependant, il ne semble pas générer l'implémentation concrète de cette interface. Au bail, PHPStorm ne le voit pas.

Comment Magento collecte-t-il les informations dont il a besoin pour générer la classe? Comment appeler les méthodes d'interface générées sur une instance concrète? Est-ce une classe qui est uniquement générée en mémoire?

Je suis content que ça marche, mais ce n'est pas vraiment satisfaisant. La capacité de Magentos à utiliser des attributs créés automatiquement par des extensions est un facteur clé de son succès. En tant que développeur de modules, je pense avoir besoin d'une compréhension approfondie de l'ensemble du processus.
Si j'avais le temps, je creuserais cela moi-même, mais je préférerais que je puisse simplement obtenir une explication.

MISE À JOUR 2 : A pris un peu de temps pour lire \Magento\Framework\Api\Code\Generator\ExtensionAttributesInterfaceGeneratoret \Magento\Framework\Api\Code\Generator\ExtensionAttributesGenerator. Maintenant, j'ai au moins une idée approximative de ce qui se passe. Si personne ne me bat, j'écrirai une description du processus complet à un moment donné, car je pense que ce serait une référence utile.

Vinai
la source
2
a fait le Vinai .. a posé la question ..Omg
Amit Bera

Réponses:

26

Tout d'abord, la génération automatique se produit en fonction du suffixe du nom de classe, par exemple Factory, ExtensionInterface(voir \Magento\Framework\Api\Code\Generator\ExtensionAttributesInterfaceGenerator::EXTENSION_INTERFACE_SUFFIX) ou Extension(voir \Magento\Framework\Api\Code\Generator\ExtensionAttributesGenerator::EXTENSION_SUFFIX).

Le générateur approprié est sélectionné en fonction du suffixe ici \Magento\Framework\Code\Generator::generateClass.

Supposons que le mode Magento soit activé developeret que les classes manquantes puissent être générées à la volée (un processus similaire se produira lorsque le compilateur sera utilisé). Lorsque le gestionnaire d'objets tente d'instancier, disons Magento\Quote\Api\Data\CartItemExtensionFactoryet qu'il n'existe pas, les événements suivants se produisent:

  1. Le chargeur automatique ne parvient pas à instancier la classe et lance la génération de code ici \Magento\Framework\Code\Generator\Autoloader::load
  2. Ensuite, le suffixe de classe est déterminé comme Factory(la liste de tous les suffixes déclarés peut être trouvée ici \Magento\Framework\ObjectManager\DefinitionFactory::getCodeGenerator) et la classe de générateur Factory correspondante ( Magento\Framework\ObjectManager\Code\Generator\Factory) est utilisée pour générer la fabrique manquante
  3. Toutes les classes générées automatiquement sont toujours basées sur une autre classe, en cas d'usine, le nom de la classe source est calculé simplement en supprimant le Factorysuffixe, ce sera le cas Magento\Quote\Api\Data\CartItemExtension. Cette classe n'existe pas et la génération automatique est à nouveau invoquée par l'autochargeur, mais cette fois pour la classe Extension
  4. Maintenant, le suffixe est Extensionet \Magento\Framework\Api\Code\Generator\ExtensionAttributesGeneratorsera utilisé pour générer cette classe
  5. La classe source pour la génération de classe d'extension est calculée comme Magento\Quote\Api\Data\CartItemInterface, elle existe et la classe d'extension est correctement générée. Cependant, lors de la tentative d'inclusion du fichier de classe d'extension, la génération automatique est déclenchée une fois de plus car Magento\Quote\Api\Data\CartItemExtensionimplémente Magento\Quote\Api\Data\CartItemExtensionInterface, qui n'existe pas
  6. Le suffixe est ExtensionInterfaceet \Magento\Framework\Api\Code\Generator\ExtensionAttributesInterfaceGeneratorsera utilisé pour la génération
  7. Les classes ExtensionInterface et Extension sont générées sur la base d'informations provenant de extension_attributes.xml, accessibles via \Magento\Framework\Api\ExtensionAttribute\Config, puis Factory est généré

Une remarque importante est qu'il n'y a pas de préférence pour ExtensionInterface dans di.xmlcar Extension et ExtensionInterface sont générés automatiquement. Ce n'est pas un problème car ExtentionInterface ne devrait pas être injecté directement via la construction.

Alex Paliarush
la source
@Vinai vous êtes les bienvenus. Bounty a été une belle surprise, merci. Mise à jour: juste pour info, si la prime a été lancée après l'acceptation de la réponse, elle n'est pas attribuée automatiquement.
Alex Paliarush
0

Pour moi, ce soir, en plus de la réponse de @Alex, je peux voir les lignes

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

dans la classe \Magento\Framework\Api\ExtensionAttributesFactory

C'est là que nous pouvons vouloir déboguer si l'interface d'extension n'est pas générée. À peu près les attributs d'extension consistent à structurer notre classe comme Magento 2 s'y attendra.

ces lignes disent:

  • est la classe dans notre extension_attributes une interface

  • est-ce qu'il étend \ Magento \ Framework \ Api \ ExtensibleDataInterface

  • a cette interface une fonction appelée getExtensionAttributes

Hervé Tribouilloy
la source