Zend Framework 2 : Using $creationOptions in PluginManager
$creationOptions is an optional parameter that we can pass on 2nd parameter when we get service from PluginManager. It can ease coding process when we need to pass a different parameter or injection on the fly while service is created. We can apply it in $invokableClasses or $factories.
Let’s take a look more deep with example. Let’s say, we have a module with PluginManager that collect service in there like this :
We need to call ‘bar’ service with injected Foo class. Ok, let’s start.
1. create PluginInterface
namespace ExamplePluginManagerCreationOptions\Service; interface PluginInterface { /* * @return void */ public function getProperty(); }
2. create a Foo class that implements PluginInterface
namespace ExamplePluginManagerCreationOptions\Service; class Foo implements PluginInterface { /** * @var string */ protected $fooProperty; /** * Construct with $fooProperty param * @param array $fooProperty */ public function __construct(array $fooProperty) { $this->fooProperty = $fooProperty; } /** * {@inheritdoc} */ public function getProperty() { var_dump($this); } }
3. create a Bar class that implements PluginInterface and use Foo in __construct.
namespace ExamplePluginManagerCreationOptions\Service; class Bar implements PluginInterface { /** * @var Foo */ protected $foo; /** * Construct with $foo param * @param Foo $foo */ public function __construct(Foo $foo) { $this->foo = $foo; } /** * {@inheritdoc} */ public function getProperty() { var_dump($this); } }
4. When realizing the dependency above. We usually use factory to create service, but with $creationOptions, we can make it as invokables like the following :
namespace ExamplePluginManagerCreationOptions\Service; use Zend\ServiceManager\AbstractPluginManager; class PluginManager extends AbstractPluginManager { protected $invokableClasses = array( 'bar' => 'ExamplePluginManagerCreationOptions\Service\Bar', ); /** * {@inheritdoc} */ public function validatePlugin($plugin) { if ($plugin instanceof PluginInterface) { // we're okay return; } throw new \InvalidArgumentException(sprintf( 'Plugin of type %s is invalid; must implement %s\PluginInterface', (is_object($plugin) ? get_class($plugin) : gettype($plugin)), __NAMESPACE__ )); } }
5. Then, we register the pluginmanager at PluginManagerFactory as PLUGIN_MANAGER_CLASS const as usual.
namespace ExamplePluginManagerCreationOptions\Factory; use Zend\Mvc\Service\AbstractPluginManagerFactory; class PluginManagerFactory extends AbstractPluginManagerFactory { const PLUGIN_MANAGER_CLASS = 'ExamplePluginManagerCreationOptions\Service\PluginManager'; }
6. Now, we can register at service_manager key in module.config.php
return array( 'service_manager' => array( 'factories' => array( 'testpluginmanager' => 'ExamplePluginManagerCreationOptions\Factory\PluginManagerFactory', ), ), );
7. Ok, then now we can call it :
$testpluginmanager = $this->getServiceLocator()->get('testpluginmanager'); $bar = $testpluginmanager->get( 'bar', new \ExamplePluginManagerCreationOptions\Service\Foo(array('test')) ); $bar->getProperty();
Hm.., do you feel pass everything of instance(s) on the fly when creating service is too much but still want “on the fly” freedom ? Then we can create a factory for it. We can then add “BarServiceFactory” like the following structure :
The “BarServiceFactory” can be like the following :
namespace ExamplePluginManagerCreationOptions\Factory\Service; use ExamplePluginManagerCreationOptions\Service\Bar; use ExamplePluginManagerCreationOptions\Service\Foo; use Zend\ServiceManager\FactoryInterface; use Zend\ServiceManager\MutableCreationOptionsInterface; use Zend\ServiceManager\ServiceLocatorInterface; /** * a factory to create ExamplePluginManagerCreationOptions\Service\Bar * service */ class BarServiceFactory implements FactoryInterface, MutableCreationOptionsInterface { /** * {@inheritdoc} */ public function setCreationOptions(array $creationOptions) { $this->creationOptions = $creationOptions; } /** * {@inheritdoc} */ public function createService(ServiceLocatorInterface $serviceLocator) { //you can call other service by $serviceLocator()->get('servicename') //as far as the service implements PluginInterface defined in //your pluginmanager //otherwise, you need to call top serviceManager //by $serviceLocator->getServiceLocator()->get('servicename') return new Bar(new Foo($this->creationOptions)); } }
Now, we can register it at out PluginManager.
namespace ExamplePluginManagerCreationOptions\Service; use Zend\ServiceManager\AbstractPluginManager; class PluginManager extends AbstractPluginManager { protected $invokableClasses = array( 'bar' => 'ExamplePluginManagerCreationOptions\Service\Bar', ); protected $factories = array( 'barservice' => 'ExamplePluginManagerCreationOptions\Factory\Service\BarServiceFactory', ); /** * {@inheritdoc} */ public function validatePlugin($plugin) { if ($plugin instanceof PluginInterface) { // we're okay return; } throw new \InvalidArgumentException(sprintf( 'Plugin of type %s is invalid; must implement %s\PluginInterface', (is_object($plugin) ? get_class($plugin) : gettype($plugin)), __NAMESPACE__ )); } }
And now, we can call it with :
$testpluginmanager = $this->getServiceLocator()->get('testpluginmanager'); $bar = $testpluginmanager->get( 'barservice', array('test') ); $bar->getProperty();
When everything is ok, then we can get the following data output :
object(ExamplePluginManagerCreationOptions\Service\Bar)[303] protected 'foo' => object(ExamplePluginManagerCreationOptions\Service\Foo)[298] protected 'fooProperty' => array (size=1) 0 => string 'test' (length=4)
Done ;). I hope it is useful.
Note : We can make $this->creationOptions
as collection of array of instances too.
References :
1. http://zend-framework-community.634137.n4.nabble.com/Abstract-Factory-for-custom-Validators-options-discarded-td4661990.html
2. http://zend-framework-community.634137.n4.nabble.com/AbstractPluginManager-amp-options-creationOptions-will-not-work-as-newInstanceArgs-td4656077.html#a4656083
2 comments