Essential PHP Design Patterns in Magento 2
Design Patterns in Magento 2
Magento 2 makes heavy use of well-established design patterns. Understanding these patterns is crucial for writing clean, maintainable extensions that work well with the framework.
1. Dependency Injection
This is the cornerstone of Magento 2's architecture. Instead of creating objects directly, you declare dependencies in your constructor and let the Object Manager inject them:
class ProductRepository
{
public function __construct(
private readonly ProductFactory $productFactory,
private readonly ResourceModel\Product $resourceModel
) {}
public function save(ProductInterface $product): void
{
$this->resourceModel->save($product);
}
}The di.xml configuration file maps interfaces to their concrete implementations, making it easy to swap implementations without changing code.
2. Plugin (Interceptor) Pattern
Plugins let you modify the behavior of any public method without rewriting the class:
class ProductPlugin
{
public function beforeSave(
ProductRepositoryInterface $subject,
ProductInterface $product
): array {
// Modify product before save
$product->setUpdatedAt(date('Y-m-d H:i:s'));
return [$product];
}
}There are three types: before, after, and around. Use them sparingly — around plugins especially can cause performance issues if overused.
3. Observer Pattern
For reacting to system events without modifying core code:
class OrderPlaceAfter implements ObserverInterface
{
public function execute(Observer $observer): void
{
$order = $observer->getEvent()->getOrder();
// React to order placement
}
}4. Factory Pattern
Magento auto-generates factory classes for creating new instances of models:
$product = $this->productFactory->create();
$product->setName('New Product');
$product->setSku('new-sku');Best Practices
- Prefer plugins over class rewrites — they compose better when multiple extensions modify the same class
- Use interfaces in constructor signatures — this makes your code work with any implementation
- Keep observers lightweight — heavy processing should be delegated to queue consumers
- Avoid
aroundplugins whenbefore/afterwill do — they add overhead and complexity
Understanding these patterns makes Magento 2 development much more predictable and maintainable.