2

As described in https://devdocs.magento.com/guides/v2.3/extension-dev-guide/code-generation.html some classes are autogenerated on request (or at di:compile) based on some rules.
For example if a class that ends with Factory does not exist it is generated with a predefined content. Same goes for Proxy and maybe others.
My question is:

How / if I can do this for my own custom rules.
For example if a class name ends with Something and it does not exist, then it should be generated in the generated/code folder?

6
  • I've been exploring the evolution of this feature over the years and was curious if there's now an option to inject generatedEntities from a module. Despite delving deep into Magento and meticulously setting breakpoints, I've yet to discover a suitable hook to seamlessly integrate it. Any insights or suggestions would be greatly appreciated! Commented Jan 14, 2024 at 12:31
  • 1
    @WillemPoortman. The only think I could come up with based on the accepted answer below can be found here: github.com/UltimateModuleCreator/umc-crud/blob/master/etc/crud/…. This is how the generators are "hooked" into the system. And the actual generators can be found here. github.com/UltimateModuleCreator/umc-crud/tree/master/Generator. Commented Jan 15, 2024 at 11:11
  • 1
    As I've done this a few years ago and my memory is not what it used to be, I don't remember exactly what, how and why I did, but I assume that the reason for creating another di.xml file at the root level was because I didn't find a way to register it from inside a module. I think the code generators come into play early on, before any module configuration is loaded. if this is true, then the answer is simple... you cannot do it from inside a module. I will try to dig today into the code again and see if I can find something that confirms this. Commented Jan 25, 2024 at 10:28
  • 1
    I think I got something.... So flow goes like this: pub/index.php which calls Bootstrap::create(BP, $_SERVER); which calls self::createObjectManagerFactory($rootDir, $initParams); where the object manager is configured and where the entity generator is instantiated, and then returns an instance of Boostrap. But when creating an instalnce of bootstrap the object manager is instantiated and you cannot pass anymore parameters to the entity generator. This is done way before the modules are read Commented Jan 25, 2024 at 10:52
  • 1
    Correct, thanks for confirming the same findings. I think we can close this having to accept this simply can not be done from inside a module. I'm now thinking about moving a di.xml into the app/etc/{custom} directory during module installation if the file doesnt exist. Time for some prototyping now we got the facts straight. Thanks for your time, appreciate it! Commented Jan 26, 2024 at 21:11

1 Answer 1

3

There is a complete lack of documentation on creating custom generators, but its a slight abstract of the Zend code generator. Which you will probs have a better time find documentation for. But in terms of registering a new generator in Magento it's basically 2 steps to do so.

https://framework.zend.com/manual/2.4/en/modules/zend.code.generator.examples.html

Create the generator

Generators are extended from the Magento\Framework\Code\Generator\EntityAbstract class and require the following methods are defined _getDefaultConstructorDefinition _getClassMethods.

Good examples imo to work from are either the factory or proxy generators. \Magento\Framework\ObjectManager\Code\Generator\Factory \Magento\Framework\ObjectManager\Code\Generator\Proxy

But a basic generator would look something along the lines of

app/code/Bigeyedeers/Generator/Code/Generator/Example.php 
<?php namespace Bigeyedeers\Generated\Code\Generator; class Example extends \Magento\Framework\Code\Generator\EntityAbstract { const ENTITY_TYPE = 'example'; protected function _getDefaultConstructorDefinition() { return [ 'name' => '__construct', 'parameters' => [ ['name' => 'objectManager', 'type' => '\\' . \Magento\Framework\ObjectManagerInterface::class], ['name' => 'instanceName', 'defaultValue' => $this->getSourceClassName()], ], 'body' => "\$this->_objectManager = \$objectManager;\n\$this->_instanceName = \$instanceName;", 'docblock' => [ 'shortDescription' => ucfirst(static::ENTITY_TYPE) . ' constructor', 'tags' => [ [ 'name' => 'param', 'description' => '\Magento\Framework\ObjectManagerInterface $objectManager', ], ['name' => 'param', 'description' => 'string $instanceName'], ], ] ]; } protected function _getClassMethods() { $construct = $this->_getDefaultConstructorDefinition(); // public function create(array $data = array()) $create = [ 'name' => 'create', 'parameters' => [['name' => 'data', 'type' => 'array', 'defaultValue' => []]], 'body' => 'return $this->_objectManager->create($this->_instanceName, $data);', 'docblock' => [ 'shortDescription' => 'Create class instance with specified parameters', 'tags' => [ ['name' => 'param', 'description' => 'array $data'], [ 'name' => 'return', 'description' => $this->getSourceClassName() ], ], ], ]; return [$construct, $create]; } } 

Update the generated entities array

I was having trouble adding a new item node into the type declaration from a module. Im sure with more time you'd be able to work it out.

But within app/etc/di.xml add a new item node to the generatedEntities argument of the Magento\Framework\Code\Generator type declartion that points to your new generator class.

app/etc/di.xml 
... <type name="Magento\Framework\Code\Generator"> <arguments> <argument name="generatedEntities" xsi:type="array"> <item name="example" xsi:type="string">\Bigeyedeers\Generated\Code\Generator\Example</item> ... 

Usage

Now you can inject your generated class just like proxies/factories etc. When you compile then it should pull your new generated class

public function __construct(\Magento\Catalog\Model\CategoryExample $categoryExample) { $this->_categoryExample = $categoryExample; } 

Edit by OP
In order not to change the core file app/etc/di.xml, the configuration can be added in app/etc/additional/di.xml (or any other subfolder of app/etc/) and it will be picked up by the config loader automatically.

<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <type name="Magento\Framework\Code\Generator"> <arguments> <argument name="generatedEntities" xsi:type="array"> <item name="example" xsi:type="string">\Bigeyedeers\Generated\Code\Generator</item> </argument> </arguments> </type> </config> 
2
  • this works nicely if I declare my generator in app/etc/di.xml, but editing core code is not a valid option. Can I make it work somehow with all the code in my custom module? Commented Feb 12, 2020 at 6:09
  • 1
    never mind. I found it. I can simply add my own di.xml in a subfolder in app/etc and it will be picked up by the config reader. For example. app/etc/additional/di.xml. Thanks. Commented Feb 12, 2020 at 6:59

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.