I would like to wrap my head around using extension attributes, for example for quote items.
It is no problem adding a custom attribute to such an entity using a setup class like in Magento 1, this isn't what this question is about.
At the moment the magic overwhelms me when I want to expose such an attribute that was added by an extension via the entities API as an extension attribute.

UPDATE: I know how the regular factories are generated. This question is about the special factories that instantiate the generated implementations for the generated extension attribute interfaces.

Here are the steps I take to get it to work. I'm adding these so whoever attempts to answer doesn't need to go into those details.
My question is HOW or WHY it works.

Steps for exposing an extension attribute via an entity API:

  1. Create an etc/extension_attributes.xml that adds the attribute to the entity interface
  2. Create a plugin to add the attribute value to the entities ExtensionAttributes instance.

In order to do the second point, the entities ExtensionAttributes instance is needed. For this reason the plugin depends on a factory, which the object manager supplies via DI.

For the quote item example Magento\Quote\Api\Data\CartItemExtensionFactory has to be used.
I guess the type of this factory somehow must be the trigger for the generation magic.

Magento then generates the matching interface \Magento\Quote\Api\Data\CartItemExtensionInterface with the setters and getters for all the extension attributes.
However, it does not seem generate the concrete implementation for that interface. At lease PHPStorm isn't seeing it.

How does Magento gather the information it needs to generate the class? How can the generated interface methods be called on a concrete instance? Is it a class that just gets generated in memory only?

I'm happy it works, but that isn't really satisfying. Magentos capability to use attributes created automatically by extensions is one key factor to its success. As a module developer, I believe I need a thorough understanding of the whole process.
Should I have time I'd just dig into this myself, but I would prefer if I just could get an explanation.

UPDATE 2: Took a little time to read through \Magento\Framework\Api\Code\Generator\ExtensionAttributesInterfaceGenerator and \Magento\Framework\Api\Code\Generator\ExtensionAttributesGenerator. Now I at least have a rough idea what is going on. If no one beats me to it I'll write a description of the full process at one point, as I think it would be a useful reference.

  • 2
    did the The Vinai .. asked the question ..Omg – Amit Bera Dec 16 '15 at 6:30

First of all autogeneration is happening based on class name suffix, e.g. Factory, ExtensionInterface (see \Magento\Framework\Api\Code\Generator\ExtensionAttributesInterfaceGenerator::EXTENSION_INTERFACE_SUFFIX) or Extension (see \Magento\Framework\Api\Code\Generator\ExtensionAttributesGenerator::EXTENSION_SUFFIX).

Proper generator is selected based on suffix here \Magento\Framework\Code\Generator::generateClass.

Let's assume Magento mode is developer and missing classes can be generated on fly (similar process will happen when compiler is used). When object manager tries to instantiate let's say Magento\Quote\Api\Data\CartItemExtensionFactory and it does not exist, the following happens:

  1. Autoloader fails to instantiate class and initiates code generation here \Magento\Framework\Code\Generator\Autoloader::load
  2. Then class suffix is determined as Factory (list of all declared suffixes can be found here \Magento\Framework\ObjectManager\DefinitionFactory::getCodeGenerator) and corresponding Factory generator class (Magento\Framework\ObjectManager\Code\Generator\Factory) is used to generate missing factory
  3. All autogenerated classes are always based on another classes, in case of factory, source class name is calculated just by removing Factory suffix, it will be Magento\Quote\Api\Data\CartItemExtension. This class does not exist and autogeneration is invoked once again by autoloader, but this time for Extension class
  4. Now suffix is Extension and \Magento\Framework\Api\Code\Generator\ExtensionAttributesGenerator will be used to generate this class
  5. Source class for Extension class generation is calculated as Magento\Quote\Api\Data\CartItemInterface, it exists and Extension class is successfully generated. However, on the attempt to include Extension class file, autogeneration is triggered once again because Magento\Quote\Api\Data\CartItemExtension implements Magento\Quote\Api\Data\CartItemExtensionInterface, which does not exist
  6. Suffix is ExtensionInterface and \Magento\Framework\Api\Code\Generator\ExtensionAttributesInterfaceGenerator will be used for generation
  7. ExtensionInterface and Extension classes are generated based on information from extension_attributes.xml, accessible via \Magento\Framework\Api\ExtensionAttribute\Config, then Factory gets generated

One important note is that there is no preference for ExtensionInterface in di.xml because both Extension and ExtensionInterface are autogenerated. This is not a problem because ExtentionInterface is not expected to be injected via construct directly.

  • @Vinai you are welcome. Bounty was a nice surprise, thanks. Update: just FYI, if bounty was started after the answer had been accepted, it is not awarded automatically. – Alex Paliarush Dec 24 '15 at 8:59
  • How are the classes loaded into PHP? Are the generated classes in actual files or are they defined in memory via a eval() statement? Can I find the generated classes in /generated directory? – cjohansson Nov 4 '20 at 13:19

For me, tonight, on top of the answer from @Alex, I can see the lines

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

in the class \Magento\Framework\Api\ExtensionAttributesFactory

are where we may want to start debugging if the extension interface is not getting generated. Pretty much extension attributes is about structuring our class like Magento 2 will expect.

these lines are saying:

  • is the class in our extension_attributes an interface

  • does it extends \Magento\Framework\Api\ExtensibleDataInterface

  • has this interface a function called getExtensionAttributes

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.