Injecting configuration into a ZF2 controller
One thing you may find yourself needing to do is access configuration information in a controller or service class.
The easiest way to do this is to use the ServiceManger‘s initialiser feature. This allows you to write one piece of injection code that can be applied to multiple objects. It’s easier to show this in action!
Let’s assume that we have this configuration file:
config/autoload/global.php:
return array(
'application' => array(
'setting_1' => 234,
)
);
That is, we have a key called 'application' that contains application-specific configuration that we would like to access in our controllers (or service classes).
Firstly we define a interface, ConfigAwareInterface:
module/Application/src/Application/ConfigAwareInterface.php:
namespace Application;
interface ConfigAwareInterface
{
public function setConfig($config);
}
We can now add this to a controller:
module/Application/src/Application/Controller/IndexController.php:
namespace ApplicationController;
use ZendMvcControllerAbstractActionController;
use ZendViewModelViewModel;
use ApplicationConfigAwareInterface;
class IndexController extends AbstractActionController
implements ConfigAwareInterface
{
protected $config;
public function setConfig($config)
{
$this->config = $config;
}
// action methods, etc.
}
In the controller, we add a use statement, implement our interface and the required setConfig() method.
Finally, we add an initializer to the Module class:
module/Application/Module.php:
class Module
{
// Other methods, such as OnBoostrap(), getAutoloaderConfig(), etc.
public function getControllerConfig()
{
return array(
'initializers' => array(
function ($instance, $sm) {
if ($instance instanceof ConfigAwareInterface) {
$locator = $sm->getServiceLocator();
$config = $locator->get('Config');
$instance->setConfig($config['application']);
}
}
)
);
}
}
(We also have a use ApplicationConfigAwareInterface; statement at the top!)
As getControllerConfig() is used by a specific ServiceManager only for controllers, we need to retrieve the main ServiceManager using getServiceLocator() in order to collect the merged configuration. As we only want the settings from within the 'application' key, we only pass that into the controller’s setConfig() method.
The configuration settings are now available to us in any controller that implements ConfigAwareInterface.
We can also do this for service classes – we simply add another initalizer to the Module:
module/Application/Module.php:
public function getServiceConfig()
{
return array(
'initializers' => array(
function ($instance, $sm) {
if ($instance instanceof ConfigAwareInterface) {
$config = $sm->get('Config');
$instance->setConfig($config['application']);
}
}
)
);
}
It also follows that you can use initializers for any type of generic injection, such as mappers, db adapters, loggers, etc. Simply create an interface and write an initalizer.
Thank you for sharing this.
Is the path of the Module.php correct or should it be module/Application/Module.php instead of module/Application/src/Application/Module.php?
cheers
sk,
I meant module/Application and have corrected the post. However, I have seen some people place Module.php in module/Application/src/Application and then have a stub at Module/Application/Module.php that requires the on in module/Application/src/Application/Module.php
Nice!
Quick note, Rob: I personally like to name the incoming argument to a factory, abstract factory, or initializer semantically, so it matches the purpose of the container. This has the side effect of making the call to "getServiceLocator()" make more sense. As an example,
Nice post!
Matthew,
I like that idea!
Hello Rob,
I've always tried to keep controllers unaware of configuration details and keep those in the service layer.
For services, I think only the factories should be able to retrieve configuration keys, however, limited to configuration specific to the service.
Best regards,
Andreas
Speaking of configuration:
Is ZendStdlibAbstractOptions supposed to be the base class of choice for configuration classes, or am I wrong here?
I very much like how it [AbstractOptions] forces you to write proper getters/setters during unit testing with fake configuration.