This article is part of a series on design patterns for framework independent code, especially to write extensions for Magento 1 and Magento 2 alike.
All Parts:
- Introduction: Magento 1 and Magento 2: Shared Code for Extensions
- Part 1: Accessing Configuration Data
- Part 2: Using Dependency Injection
- Part 3: Building Bridges
- Part 4: Preparing Data For Output
- Part 5: Using Advanced Autoloading
- Part 6: Leveraging PSR Interfaces
- Part 7: Iterating Iterators
A common dependency is the dependency on configuration values. This one is relatively easy to solve. We follow the Depency Inversion principle and instead of calling Mage::getStoreConfig() or $scopeConfig->getValue(), we make the Magento module and the library depend on an abstraction.
This abstraction could be a simple interface on which the library depends and which is implemented in the module:
1 2 3 4 |
interface StoreConfiguration { public function getValue($code) } |
We have got to be careful here because it is not explicit on which configuration values it depends on. That’s fine as long as you only use configuration from the module itself, but as soon as you start reading core configuration, there is an implicit dependency on these configuration keys, the abstraction leaks implementation details. Of course you can map keys that are different in Magento 1 and Magento 2 (or OroCommerce, etc.) in the implementations of the StoreConfiguration interface. But since the interface does not say which keys will be needed, you need to know every call. Given that you probably don’t write all implementations at once, it’s a guaranteed source of bugs.
So, what can we do instead? A possible solution is to add a method for each configuration value that we need. I decided to go a different route and create value objects that encapsulate the configuration values. The module instantiates these objects and passes them to the library. And instead of having one big “config” class, I divided it by section. Here is an example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
namespace IntegerNet\Solr\Config; final class GeneralConfig { private $active; private $serverName; private $log; /** * @param bool $active * @param bool $log */ public function __construct($active, $serverName, $log) { $this->active = $active; $this->serverName = $serverName; $this->log = $log; } /** * @return boolean */ public function isActive() { return $this->active; } /** * @return string */ public function getServerName() { return $this->serverName; } /** * @return boolean */ public function isLog() { return $this->log; } } |
Value Object – “a small object such as a Money or date range object. Their key property is that they follow value semantics rather than reference semantics. […] value objects should be entirely immutable. If you want to change a value object you should replace the object with a new one and not be allowed to update the values of the value object itself” — Martin Fowler on Value Objects
I still use a central interface but now it returns these value objects:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
namespace IntegerNet\Solr\Implementor; /** * Interface for configuration reader. One instance per store. */ interface Config { /** * Returns general Solr module configuration * * @return \IntegerNet\Solr\Config\GeneralConfig */ public function getGeneralConfig(); /** * Returns Solr server configuration * * @return \IntegerNet\Solr\Config\ServerConfig */ public function getServerConfig(); [...] } |
Example implementation for Magento 1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
final class IntegerNet_Solr_Model_Config_Store implements Config { protected $_storeId; protected $_general; protected $_server; [...] public function getGeneralConfig() { if ($this->_general === null) { $prefix = 'integernet_solr/general/'; $this->_general = new GeneralConfig( $this->_getConfigFlag($prefix . 'is_active'), $this->_getConfig($prefix . 'server_name'), $this->_getConfigFlag($prefix . 'log') ); } return $this->_general; } [...] protected function _getConfig($path) { return Mage::getStoreConfig($path, $this->_storeId); } protected function _getConfigFlag($path) { return Mage::getStoreConfigFlag($path, $this->_storeId); } } |
Why is this class “final”? It’s not intended to be overridden and I don’t want to maintain a “protected API”. It’s a good idea to follow the rule of thumb:
“Make your classes always final, if they implement an interface, and no other public methods are defined” — Marco Pivetta on when to declare classes final
Tieing it together
How do we pass the configuration to our library? There usually is some kind of initialization, in which I would pass an ApplicationContext which contains – amongst others – the configuration object (or an array of those if we deal with multiple store views at once).
Single classes only require the configuration value objects they need and receive them as constructor arguments.
Read more on how to initialize objects in the next part: Using Dependency Injection.

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.