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?

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>
| improve this answer | |
  • 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? – Marius Feb 12 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. – Marius Feb 12 at 6:59

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.