A decorator is a class that wraps another class, implements the original interface, delegates method calls to the original class, but may modify the return values or add additional methods. The Decorator design pattern is one of the original design patterns for object oriented programming from the “Gang of Four” book.
A typical use case in Magento templates is when you need methods to access or format custom product attributes.
Here is a line of code that I found in multiple templates in one of our projects
1 2 |
$hasCustomStyle = $product->getData('custom_product_style') && $product->getData('custom_product_style') !== 'default' |
If the product model was in our own hands, we would probably add a method hasCustomStyle()
, but it is not and we do not want to rewrite the product model without cood cause. A helper would be another possibility, but that’s the “old school” way to do it and I don’t like it, because helpers tend to be a mess.
A decorator can be written as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
class DecoratedProduct { private $product; public function __construct(ProductInterface $product) { $this->product = $product; } public function hasStyle() { return $this->product->getData('custom_product_style') && $this->product->getData('custom_product_style') !== 'default'; } public function __call($method, $args) { return $this->product->$method(...$args); } } |
Here I am taking advantage of the dynamic nature of PHP. Instead of implementing ProductInterface
and forwarding all method calls explicitly to $this->product
, I use the magic __call()
method to catch all calls to undefined methods.
“Decorating” the product then takes place in a view model (read more about view models in my separate blog post):
1 2 3 4 5 6 7 8 9 10 |
class Decorator implements ArgumentInterface { /** * @return DecoratedProduct|ProductInterface */ public function decorateProduct(ProductInterface $product): DecoratedProduct { return new DecoratedProduct($product); } } |
Note that I add the Product class as possible return type in the doc block, which allows IDE autocompletion for the original product methods as well as the methods of the decorator.
This is how the decorator will be used in the template, after the view model has been added via layout XML:
1 2 3 4 5 6 7 |
/** @var Decorator $decorator */ $decorator = $block->getData('decorator'); // ... $product = $decorator->decorateProduct($product); // ... if ($product->hasStyle()): // ... |
In our case the product decorator would become part of a theme module and receive a few more theme related methods which were needed across multiple templates (like product view, product list, sliders).
It is a clean way to remove business logic from the template and reuse it in different types of blocks. While these code examples are for Magento 2, you can apply the same concept in Magento 1 templates.

Author: Fabian Schmengler
Fabian Schmengler is Magento developer and trainer at integer_net. His focus lies in backend development, conceptual design and test automation.
Fabian was repeatedly selected as a Magento Master in 2017 and 2018 based on his engagements, active participation on StackExchange and contributions to the Magento 2 core.