Skip to content
intermediate Phase 28 · DI Advanced

Magento Factories - Object Creation

Understanding Magento 2 factories: ObjectManager factory creation, generated factory classes, when to use factories, and factory patterns

45m
0 problems
Topic Progress 0%

Why Factories Exist

The Problem Factories Solve

In Magento, models need dependencies injected (e.g., resource models, registry). But you can't inject dependencies into objects you create dynamically.

// BAD: No DI for dynamically created object
$warranty = new Warranty();  // Can't get dependencies injected
$this->resource->save($warranty);

Factory Solution

Factories create objects WITH dependencies injected:

// GOOD: Factory creates object with DI
$warranty = $this->warrantyFactory->create();
// Warranty has resource model, registry, etc. injected
$this->resource->save($warranty);

How Factories Work

1. You inject WarrantyFactory (auto-generated)
2. Call create() on the factory
3. Factory uses ObjectManager to create Warranty
4. Warranty's constructor dependencies are resolved
5. Object is returned with all dependencies injected

Factory Interface

// Auto-generated interface
interface WarrantyFactoryInterface
{
    public function create(array $data = []): Warranty;
}

// Actual factory class (auto-generated)
class WarrantyFactory
{
    public function create(array $data = []): Warranty
    {
        return $this->objectManager->create(Warranty::class, ['data' => $data]);
    }
}

Using Factories

Injecting Factories

namespace Amazon\Prep\Service;

class WarrantyService
{
    public function __construct(
        private \Amazon\Prep\Model\WarrantyFactory $warrantyFactory,
        private \Amazon\Prep\Model\ResourceModel\Warranty $warrantyResource
    ) {}

    public function createWarranty(string $name, int $duration): Warranty
    {
        $warranty = $this->warrantyFactory->create();
        $warranty->setName($name);
        $warranty->setDuration($duration);
        $this->warrantyResource->save($warranty);
        return $warranty;
    }
}

Factory create() Method

// Create with empty data
$warranty = $this->warrantyFactory->create();

// Create with initial data
$warranty = $this->warrantyFactory->create([
    'name' => 'Premium Warranty',
    'duration' => 24,
    'status' => 'active',
]);

// The 'data' key is passed to setData()
$warranty = $this->warrantyFactory->create([
    'data' => [
        'name' => 'Test',
    ]
]);

Factory Naming Convention

Model class: Amazon\Prep\Model\Warranty
Factory:     Amazon\Prep\Model\WarrantyFactory

Model class: Amazon\Prep\Model\ResourceModel\Warranty\Collection
Factory:     Amazon\Prep\Model\ResourceModel\Warranty\CollectionFactory

The factory name is always the model class name + Factory.

Generated Factory Classes

Auto-Generation

When you run bin/magento setup:di:compile, Magento generates factory classes in the generated/ directory.

generated/
├── Amazon/
│   └── Prep/
│       └── Model/
│           ├── WarrantyFactory.php
│           └── Warranty/
│               └── CollectionFactory.php

Generated Factory Code

<?php
namespace Amazon\Prep\Model;

class WarrantyFactory
{
    private $objectManager;
    private $instanceName = 'Amazon\Prep\Model\Warranty';

    public function __construct(\Magento\Framework\ObjectManager $objectManager)
    {
        $this->objectManager = $objectManager;
    }

    public function create(array $data = [])
    {
        return $this->objectManager->create($this->instanceName, ['data' => $data]);
    }
}

Factory Generation Rules

Pattern Generated Factory
Vendor\Module\Model\Product Vendor\Module\Model\ProductFactory
Vendor\Module\Model\ResourceModel\Product Vendor\Module\Model\ResourceModel\ProductFactory
Vendor\Module\Model\ResourceModel\Product\Collection Vendor\Module\Model\ResourceModel\Product\CollectionFactory

Custom Factory Name

Override the generated factory:

<type name="Amazon\Prep\Model\WarrantyFactory">
    <arguments>
        <argument name="instanceName" xsi:type="string">Amazon\Prep\Model\CustomWarranty</argument>
    </arguments>
</type>

When to Use Factories

Use Factories When

  1. Creating models that need DI
// Model needs resource model, registry, etc.
$product = $this->productFactory->create();
  1. Creating multiple instances
foreach ($data as $item) {
    $model = $this->modelFactory->create();
    $model->setData($item);
    $this->resource->save($model);
}
  1. Creating objects with initial data
$warranty = $this->warrantyFactory->create([
    'name' => 'Test',
    'duration' => 12
]);

Don't Use Factories When

  1. Getting existing instances (use get())
// BAD: Using factory for existing object
$session = $this->sessionFactory->create();

// GOOD: Get shared instance
$session = $this->session;  // Injected directly
  1. Creating simple objects without DI
// BAD: Unnecessary factory
$helper = $this->helperFactory->create();

// GOOD: Direct instantiation
$helper = new \Magento\Framework\Helper\Data();
  1. Getting collections (use factory directly)
// BAD: Creating collection from model factory
$collection = $this->modelFactory->create()->getCollection();

// GOOD: Use collection factory
$collection = $this->collectionFactory->create();

Factory vs ObjectManager::create()

// BAD: Direct ObjectManager
$warranty = ObjectManager::getInstance()->create(Warranty::class);

// GOOD: Factory
$warranty = $this->warrantyFactory->create();

// Factory is better because:
// 1. Dependencies are declared via constructor
// 2. Easier to test (inject mock factory)
// 3. Follows DI principles

Testing with Factories

class WarrantyServiceTest extends TestCase
{
    public function testCreateWarranty(): void
    {
        $warranty = $this->createMock(Warranty::class);
        $factory = $this->createMock(WarrantyFactory::class);
        $factory->method('create')->willReturn($warranty);

        $service = new WarrantyService(
            $factory,
            $this->createMock(WarrantyResource::class)
        );

        $result = $service->createWarranty('Test', 12);
        $this->assertSame($warranty, $result);
    }
}

Quiz

1. Why are factories needed in Magento?

Question 1 options

2. How do you inject a factory into a class?

Question 2 options

3. When should you NOT use a factory?

Question 3 options

Flashcards

Question

What does a factory create?

Answer

Model instances with their DI dependencies automatically injected

Question

How is a factory named?

Answer

Model class name + Factory (e.g., WarrantyFactory)

Question

Where are generated factories stored?

Answer

In the generated/ directory after setup:di:compile

Question

When should you use a factory?

Answer

When creating models that need DI or multiple instances

Question

Factory vs ObjectManager::create()?

Answer

Factories declare dependencies via constructor; ObjectManager::create() bypasses DI

Revision Notes

Key Takeaways

  • 1. Factories create objects with DI dependencies injected
  • 2. Factory name = Model class name + Factory
  • 3. Generated factories are in the generated/ directory
  • 4. Use factories for models needing DI, not for shared instances
  • 5. Factories are easier to test than direct ObjectManager usage

Interview Tips

  • Explain why factories exist in Magento's DI system
  • Know when to use factories vs direct injection
  • Be ready to create and test factory-based code
  • Discuss factory vs ObjectManager::create() trade-offs

Cheat Sheet

Factory:
  Inject: private WarrantyFactory $warrantyFactory
  Create: $this->warrantyFactory->create(['data' => [...]])

Generated in: generated/Vendor/Module/Model/WarrantyFactory.php

Use when: creating models needing DI
Avoid when: getting shared instances or simple objects