Skip to content
intermediate Phase 17 · Themes, Libraries & Generated Code

Magento 2 Generated Code

Generated code - generated/ directory, factories, proxies, interceptors, and auto-generated classes.

45m
0 problems
Topic Progress 0%

Generated Code Overview

Magento 2 generates PHP classes to optimize runtime performance. Instead of using the ObjectManager directly, generated classes provide type-safe, cached dependency resolution.

Generated directory structure:

generated/
├── code/
│   ├── Magento/
│   │   └── Catalog/
│   │       └── Model/
│   │           ├── ProductFactory.php
│   │           └── Product\Interceptor.php
│   └── Vendor/
│       └── Module/
│           └── Proxy/
│               └── SomeServiceProxy.php
└── metadata/
    ├── global.php          # Compiled global DI
    ├── frontend.php        # Compiled frontend DI
    ├── adminhtml.php       # Compiled admin DI
    └── crontab.php         # Compiled cron DI

Types of generated code:

  1. Factories - Create instances of any injectable class
  2. Proxies - Lazy-load dependencies on first method call
  3. Interceptors - Wrap classes with plugin (before/after/around) methods
  4. Metadata - Compiled DI configuration for fast loading

When code is generated:

# Production mode - compile before deployment
php bin/magento setup:di:compile

# Developer mode - auto-generated on demand
php bin/magento deploy:mode:set developer

Generated code should never be edited manually. It is recreated by the compilation process.

Factory Classes

Factories are generated for every injectable class and provide a type-safe way to create new instances.

Original class:

<?php
namespace Vendor\Module\Model;

class Item
{
    public function __construct(
        \Magento\Framework\DataObject\Factory $objectFactory,
        \Psr\Log\LoggerInterface $logger
    ) {
        // Constructor injection
    }
}

Generated factory:

<?php
namespace Vendor\Module\Model\ItemFactory;

// Auto-generated - do not edit
class Factory
{
    private $objectManager;
    private $instanceName = 'Vendor\\Module\\Model\\Item';
    
    public function __construct(
        \Magento\Framework\ObjectManager\ObjectManager $objectManager
    ) {
        $this->objectManager = $objectManager;
    }
    
    public function create(array $data = [])
    {
        return $this->objectManager->create(
            $this->instanceName,
            $data
        );
    }
}

Using factories:

public function __construct(
    \Vendor\Module\Model\ItemFactory $itemFactory
) {
    $this->itemFactory = $itemFactory;
}

public function createNewItem()
{
    $item = $this->itemFactory->create();
    $item->setName('New Item');
    $item->setStatus(1);
    return $item;
}

Factories are preferred over ObjectManager::create() because they provide IDE autocompletion, type safety, and follow Magento's coding standards.

Proxy Classes

Proxies wrap a class and defer its instantiation until the first method is called. This improves performance for classes with expensive dependencies.

When to use proxies:

  • Class has many dependencies but only one is used rarely
  • Dependency involves network calls or file I/O
  • Loading the dependency on every request is wasteful

Configure proxy via di.xml:

<config>
    <type name="Vendor\Module\Model\Importer">
        <arguments>
            <argument name="feedParser" xsi:type="object">
                Vendor\Module\Model\FeedParser\Proxy
            </argument>
        </arguments>
    </type>
</config>

Generated proxy class (simplified):

<?php
namespace Vendor\Module\Model\FeedParser;

class Proxy implements \Vendor\Module\Model\FeedParserInterface
{
    private $objectManager;
    private $instanceName = 'Vendor\\Module\\Model\\FeedParser';
    private $shared = false;
    private $instance;
    
    public function __construct(
        \Magento\Framework\ObjectManager\ObjectManager $objectManager,
        string $instanceName = '',
        bool $shared = false
    ) {
        $this->objectManager = $objectManager;
        $this->instanceName = $instanceName ?: $this->instanceName;
        $this->shared = $shared;
    }
    
    private function loadInstance()
    {
        if ($this->instance === null) {
            $this->instance = $this->objectManager->create(
                $this->instanceName
            );
        }
        return $this->instance;
    }
    
    public function parse($url)
    {
        return $this->loadInstance()->parse($url);
    }
}

Proxy naming convention: {ClassName}\Proxy or {Namespace}\{ClassName}\Proxy

Interceptors (Plugin System)

Interceptors are generated wrapper classes that enable the plugin system. They call before, after, and around methods defined in plugin configuration.

Plugin configuration:

<config>
    <type name="Magento\Catalog\Model\ProductRepository">
        <plugin name="vendor_after_save"
                type="Vendor\Module\Plugin\ProductAfterSave"
                sortOrder="10"/>
    </type>
</config>

Plugin class:

<?php
namespace Vendor\Module\Plugin;

class ProductAfterSave
{
    public function afterSave(
        \Magento\Catalog\Model\ProductRepository $subject,
        $result
    ) {
        // Code after save
        $this->logger->info('Product saved: ' . $result->getSku());
        return $result;
    }
    
    public function beforeSave(
        \Magento\Catalog\Model\ProductRepository $subject,
        $product
    ) {
        // Code before save
        $product->setData('custom_field', 'value');
        return [$product];
    }
}

Generated interceptor (simplified):

<?php
namespace Magento\Catalog\Model;

class ProductRepository\Interceptor extends ProductRepository
{
    private $pluginManager;
    
    public function save($product)
    {
        // Before plugins
        $pluginResult = $this->pluginManager->call(
            $this,
            'beforeSave',
            [$product]
        );
        if (!empty($pluginResult)) {
            $product = $pluginResult[0];
        }
        
        // Execute original method
        $result = parent::save($product);
        
        // After plugins
        $result = $this->pluginManager->call(
            $this,
            'afterSave',
            [$result]
        );
        
        return $result;
    }
}

Regenerate interceptors:

php bin/magento setup:di:compile
# or in developer mode, they regenerate automatically

Quiz

1. When would you use a Proxy instead of direct instantiation?

Question 1 options

2. What command generates all Magento code artifacts?

Question 2 options

Flashcards

Question

What suffix do generated factory classes have?

Answer

Factory (e.g., ProductFactory)

Question

What suffix do generated proxy classes have?

Answer

Proxy (e.g., SomeClass\Proxy)

Question

What do interceptors wrap?

Answer

Original class methods to enable plugin before/after/around execution

Question

Where is compiled DI metadata stored?

Answer

generated/metadata/{area}.php

Revision Notes

Key Takeaways

  • 1. Magento generates factories, proxies, interceptors, and metadata
  • 2. Factories create new instances of injectable classes
  • 3. Proxies defer expensive dependencies until first use
  • 4. Interceptors enable the plugin system (before/after/around)
  • 5. All generated code goes in the generated/ directory
  • 6. Run setup:di:compile to regenerate all code

Interview Tips

  • Explain the three types of generated code and their purposes
  • Describe when you would configure a proxy
  • Discuss how interceptors enable the plugin system
  • Know the difference between developer and production mode code generation
  • Explain why ObjectManager direct usage is discouraged

Cheat Sheet

Generated Code Cheat Sheet

Output Dir: generated/
Compile: php bin/magento setup:di:compile

Types:

  • Factory ({Class}Factory) - Creates new instances
  • Proxy ({Class}\Proxy) - Lazy-loads dependencies
  • Interceptor ({Class}\Interceptor) - Plugin wrapper

Metadata: generated/metadata/{area}.php

di.xml Proxy Config:

<argument xsi:type="object">ClassName\Proxy</argument>

Mode Behavior:

  • Developer: Auto-regenerate
  • Production: Pre-compile required