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
- Creating models that need DI
// Model needs resource model, registry, etc.
$product = $this->productFactory->create();
- Creating multiple instances
foreach ($data as $item) {
$model = $this->modelFactory->create();
$model->setData($item);
$this->resource->save($model);
}
- Creating objects with initial data
$warranty = $this->warrantyFactory->create([
'name' => 'Test',
'duration' => 12
]);
Don't Use Factories When
- Getting existing instances (use get())
// BAD: Using factory for existing object
$session = $this->sessionFactory->create();
// GOOD: Get shared instance
$session = $this->session; // Injected directly
- Creating simple objects without DI
// BAD: Unnecessary factory
$helper = $this->helperFactory->create();
// GOOD: Direct instantiation
$helper = new \Magento\Framework\Helper\Data();
- 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?
2. How do you inject a factory into a class?
3. When should you NOT use a factory?
Flashcards
Question
What does a factory create?
Click to reveal answer
Answer
Model instances with their DI dependencies automatically injected
Question
How is a factory named?
Click to reveal answer
Answer
Model class name + Factory (e.g., WarrantyFactory)
Question
Where are generated factories stored?
Click to reveal answer
Answer
In the generated/ directory after setup:di:compile
Question
When should you use a factory?
Click to reveal answer
Answer
When creating models that need DI or multiple instances
Question
Factory vs ObjectManager::create()?
Click to reveal answer
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