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:

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:

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:

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:

Example implementation for Magento 1:

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.

Fabian Schmengler

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.

More Information · Twitter · GitHub