From 2b6dcace12ae27944989fb741d2939bd18de32df Mon Sep 17 00:00:00 2001 From: Sandro Keil Date: Sun, 14 Sep 2014 15:29:17 +0100 Subject: [PATCH 1/2] added mandatory options interface --- CHANGELOG.md | 4 ++ README.md | 17 ++++++-- docs/Configurable.md | 32 +++++++++++++++ src/Service/AbstractConfigurableFactory.php | 15 ++++++- src/Service/MandatoryOptionsInterface.php | 26 +++++++++++++ .../AbstractConfigurableFactoryTest.php | 39 +++++++++++++++++++ .../AbstractMandatoryOptionsFactory.php | 17 ++++++++ 7 files changed, 145 insertions(+), 5 deletions(-) create mode 100644 src/Service/MandatoryOptionsInterface.php create mode 100644 test/Service/TestAsset/AbstractMandatoryOptionsFactory.php diff --git a/CHANGELOG.md b/CHANGELOG.md index af03865..82221f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # EasyConfig CHANGELOG +## 1.3.0 TBA + +- Added MandatoryOptionsInterface to check for mandatory options automatically + ## 1.2.0 (2014-04-29) - Updated source to PSR-4 diff --git a/README.md b/README.md index 4959ddb..13ea2eb 100644 --- a/README.md +++ b/README.md @@ -43,15 +43,16 @@ return array( ``` So `doctrine` is the module, `connection` is the scope and `orm_default` is the name. After that the specified instance options follow. -With [AbstractConfigurableFactory](https://github.com/sandrokeil/EasyConfig/tree/master/docs/Configurable.md) we can easily access to these options also with an option class. +With [AbstractConfigurableFactory](docs/Configurable.md) we can easily access to these options also with an option class and mandatory options check. ```php use Sake\EasyConfig\Service\AbstractConfigurableFactory; use Sake\EasyConfig\Service\OptionsClassInterface; +use Sake\EasyConfig\Service\MandatoryOptionsInterface; use Zend\ServiceManager\FactoryInterface; use Zend\ServiceManager\ServiceLocatorInterface; -class MyDBALConnectionFactory extends AbstractConfigurableFactory implements FactoryInterface, OptionsClassInterface +class MyDBALConnectionFactory extends AbstractConfigurableFactory implements FactoryInterface, OptionsClassInterface, MandatoryOptionsInterface { public function createService(ServiceLocatorInterface $serviceLocator) { @@ -69,6 +70,14 @@ class MyDBALConnectionFactory extends AbstractConfigurableFactory implements Fac return $instance; } + public function getMandatoryOptions() + { + return array( + 'driverClass', + 'params', + ); + } + public function getOptionsClass() { return '\DoctrineORMModule\Options\DBALConnection'; @@ -104,13 +113,13 @@ Put the following into your composer.json } } -It is not necessary to add this module to your `config/application.config.php`. +It is *not necessary* to add this module to your `config/application.config.php`. ## Documentation You can find documentation about the usages of factories at the following links: - * [Configurable - Get an options class or an array of options](https://github.com/sandrokeil/EasyConfig/tree/master/docs/Configurable.md) + * [Configurable - Get an options class or an array of options with mandytoy](https://github.com/sandrokeil/EasyConfig/tree/master/docs/Configurable.md) * [ConstructorOptionConfig - Inject options via constructor](https://github.com/sandrokeil/EasyConfig/tree/master/docs/ConstructorOptionConfig.md) * [OptionHydratorConfig - Inject options with a hydrator](https://github.com/sandrokeil/EasyConfig/tree/master/docs/OptionHydratorConfig.md) * [ServiceConfig - Inject an other service to instance](https://github.com/sandrokeil/EasyConfig/tree/master/docs/ServiceConfig.md) diff --git a/docs/Configurable.md b/docs/Configurable.md index 38f022d..0304d9a 100644 --- a/docs/Configurable.md +++ b/docs/Configurable.md @@ -57,6 +57,38 @@ class MyDBALConnectionFactory extends AbstractConfigurableFactory implements Fac } } ``` + +## Mandatory Options check +You can also check for mandatory options automatically with `MandatoryOptionsInterface`. Now we want also check that +option `driverClass` and `params` are available. So we also implement in the example above the interface +`MandatoryOptionsInterface`. If one of these options is missing, an exception is raised. + +```php +use Sake\EasyConfig\Service\AbstractConfigurableFactory; +use Sake\EasyConfig\Service\MandatoryOptionsInterface; +use Zend\ServiceManager\FactoryInterface; +use Zend\ServiceManager\ServiceLocatorInterface; + +class MyDBALConnectionFactory extends AbstractConfigurableFactory implements FactoryInterface, MandatoryOptionsInterface +{ + // same code as above + + /** + * Returns a list of mandatory options which must be available + * + * @return array + */ + public function getMandatoryOptions() + { + return array( + 'driverClass', + 'params', + ); + } +} +``` +This can also be combined with `OptionsClassInterface` + ## Option Class If you implement `OptionClassInterface` then you get a option class. Your options class should extend from `\Zend\Stdlib\AbstractOptions`. ``` diff --git a/src/Service/AbstractConfigurableFactory.php b/src/Service/AbstractConfigurableFactory.php index 57fc307..d722c8f 100644 --- a/src/Service/AbstractConfigurableFactory.php +++ b/src/Service/AbstractConfigurableFactory.php @@ -42,7 +42,20 @@ public function getOptions(ServiceLocatorInterface $sl) )); } $options = $options[$this->getModule()][$this->getScope()][$this->getName()]; - + // check for mandatory options + if ($this instanceof MandatoryOptionsInterface) { + foreach ($this->getMandatoryOptions() as $option) { + if (!isset($options[$option])) { + throw new Exception\RuntimeException(sprintf( + 'Mandatory option "%s" was not set for configuration "%s.%s.%s".', + $option, + $this->getName(), + $this->getModule(), + $this->getScope() + )); + } + } + } // create option class if ($this instanceof OptionsClassInterface) { $optionClass = $this->getOptionsClass(); diff --git a/src/Service/MandatoryOptionsInterface.php b/src/Service/MandatoryOptionsInterface.php new file mode 100644 index 0000000..62b7b09 --- /dev/null +++ b/src/Service/MandatoryOptionsInterface.php @@ -0,0 +1,26 @@ +assertSame('foo', $optionsClass->getFoo()); $this->assertSame('bar', $optionsClass->getBar()); } + + /** + * Tests if getOptions() works with mandatory options interface + * + * @covers \Sake\EasyConfig\Service\AbstractConfigurableFactory::getOptions + */ + public function testGetOptionsShouldCheckMandatoryOptions() + { + $stub = parent::getStub('\SakeTest\EasyConfig\Service\TestAsset\AbstractMandatoryOptionsFactory'); + + $stub->expects($this->any()) + ->method('getMandatoryOptions') + ->will($this->returnValue(array('invokables'))); + + $options = $stub->getOptions($this->serviceManager); + + $this->assertArrayHasKey('invokables', $options); + } + + /** + * Tests if getOptions() throws a runtime exception if mandatory option is missing + * + * @covers \Sake\EasyConfig\Service\AbstractConfigurableFactory::getOptions + */ + public function testGetOptionsShouldThrowRuntimeExceptionIfMandatoryOptionIsMissing() + { + $stub = parent::getStub('\SakeTest\EasyConfig\Service\TestAsset\AbstractMandatoryOptionsFactory'); + + $stub->expects($this->any()) + ->method('getMandatoryOptions') + ->will($this->returnValue(array('invalid'))); + + $this->setExpectedException( + '\Sake\EasyConfig\Service\Exception\RuntimeException', + 'Mandatory option "invalid"' + ); + + $stub->getOptions($this->serviceManager); + } } diff --git a/test/Service/TestAsset/AbstractMandatoryOptionsFactory.php b/test/Service/TestAsset/AbstractMandatoryOptionsFactory.php new file mode 100644 index 0000000..4a99d64 --- /dev/null +++ b/test/Service/TestAsset/AbstractMandatoryOptionsFactory.php @@ -0,0 +1,17 @@ + Date: Wed, 24 Sep 2014 14:40:03 +0100 Subject: [PATCH 2/2] updated docs for MandatoryOptionsInterface --- CHANGELOG.md | 3 +- README.md | 43 +++++++++++++---- docs/Configurable.md | 53 ++++++++++++++++++++- src/Service/AbstractConfigurableFactory.php | 4 +- 4 files changed, 90 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 82221f1..43b953a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,9 @@ # EasyConfig CHANGELOG -## 1.3.0 TBA +## 1.3.0 (2014-09-24) - Added MandatoryOptionsInterface to check for mandatory options automatically +- Added empty Module.php e.g. for modules.zendframework.com ## 1.2.0 (2014-04-29) diff --git a/README.md b/README.md index 13ea2eb..f1f857a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Easy Config module for Zend Framework 2 +# EasyConfig module for Zend Framework 2 > You want to configure your factories via your module config? @@ -18,7 +18,7 @@ [![Total Downloads](https://poser.pugx.org/sandrokeil/easy-config/downloads.png)](https://packagist.org/packages/sandrokeil/easy-config) [![License](https://poser.pugx.org/sandrokeil/easy-config/license.png)](https://packagist.org/packages/sandrokeil/easy-config) -EasyConfig provides some abstract factories to easily create instances depending on configuration or retrieve specified module options. +EasyConfig provides some abstract factories and some interfaces to easily create instances depending on configuration or retrieve specified module options. * **Well tested.** Besides unit test and continuous integration/inspection this solution is also ready for production use. * **Great foundations.** Based on [Zend Framework 2](https://github.com/zendframework/zf2) @@ -43,7 +43,7 @@ return array( ``` So `doctrine` is the module, `connection` is the scope and `orm_default` is the name. After that the specified instance options follow. -With [AbstractConfigurableFactory](docs/Configurable.md) we can easily access to these options also with an option class and mandatory options check. +With [AbstractConfigurableFactory](docs/Configurable.md) we can easily access to these options also with an option class and mandatory options check. See [docs](docs/Configurable.md) for a detailed explanation. ```php use Sake\EasyConfig\Service\AbstractConfigurableFactory; @@ -70,6 +70,11 @@ class MyDBALConnectionFactory extends AbstractConfigurableFactory implements Fac return $instance; } + /** + * Returns a list of mandatory options which must be available + * + * @return array + */ public function getMandatoryOptions() { return array( @@ -78,21 +83,41 @@ class MyDBALConnectionFactory extends AbstractConfigurableFactory implements Fac ); } + /** + * Return the option class name (fcqn) where options are injected via constructor + * + * @return string + */ public function getOptionsClass() { return '\DoctrineORMModule\Options\DBALConnection'; } + /** + * Module name + * + * @return string + */ public function getModule() { return 'doctrine'; } + /** + * Config scope + * + * @return string + */ public function getScope() { return 'connection'; } + /** + * Config name + * + * @return string + */ public function getName() { return 'orm_default'; @@ -119,10 +144,10 @@ It is *not necessary* to add this module to your `config/application.config.php` You can find documentation about the usages of factories at the following links: - * [Configurable - Get an options class or an array of options with mandytoy](https://github.com/sandrokeil/EasyConfig/tree/master/docs/Configurable.md) - * [ConstructorOptionConfig - Inject options via constructor](https://github.com/sandrokeil/EasyConfig/tree/master/docs/ConstructorOptionConfig.md) - * [OptionHydratorConfig - Inject options with a hydrator](https://github.com/sandrokeil/EasyConfig/tree/master/docs/OptionHydratorConfig.md) - * [ServiceConfig - Inject an other service to instance](https://github.com/sandrokeil/EasyConfig/tree/master/docs/ServiceConfig.md) - * [ServiceManagerConfig - Inject Options to a service plugin manager](https://github.com/sandrokeil/EasyConfig/tree/master/docs/ServiceManagerConfig.md) - * [ServiceOptionConfig - Inject one or more services](https://github.com/sandrokeil/EasyConfig/tree/master/docs/ServiceOptionConfig.md) + * [Configurable - Get an options class or an array of options with mandytoy](docs/Configurable.md) + * [ConstructorOptionConfig - Inject options via constructor](docs/ConstructorOptionConfig.md) + * [OptionHydratorConfig - Inject options with a hydrator](docs/OptionHydratorConfig.md) + * [ServiceConfig - Inject an other service to instance](docs/ServiceConfig.md) + * [ServiceManagerConfig - Inject Options to a service plugin manager](docs/ServiceManagerConfig.md) + * [ServiceOptionConfig - Inject one or more services](docs/ServiceOptionConfig.md) diff --git a/docs/Configurable.md b/docs/Configurable.md index 0304d9a..f07df1a 100644 --- a/docs/Configurable.md +++ b/docs/Configurable.md @@ -32,6 +32,29 @@ class MyDBALConnectionFactory extends AbstractConfigurableFactory implements Fac { // get options for doctrine.connection.orm_default $options = $this->getOptions($serviceLocator); + + // check if mandatory options are available + if (empty($options['driverClass'])) { + throw new Exception\RuntimeException( + sprintf( + 'Driver class was not set for configuration %s.%s.%s', + $this->getModule(), + $this->getScope(), + $this->getName() + ) + ); + } + + if (empty($options['params'])) { + throw new Exception\RuntimeException( + sprintf( + 'Params was not set for configuration %s.%s.%s', + $this->getModule(), + $this->getScope(), + $this->getName() + ) + ); + } $driverClass = $options['driverClass']; $params = $options['params']; @@ -71,7 +94,20 @@ use Zend\ServiceManager\ServiceLocatorInterface; class MyDBALConnectionFactory extends AbstractConfigurableFactory implements FactoryInterface, MandatoryOptionsInterface { - // same code as above + public function createService(ServiceLocatorInterface $serviceLocator) + { + // get options for doctrine.connection.orm_default + $options = $this->getOptions($serviceLocator); + + // mandatory options check is automatically done by MandatoryOptionsInterface + + $driverClass = $options['driverClass']; + $params = $options['params']; + + // create your instance and set options + + return $instance; + } /** * Returns a list of mandatory options which must be available @@ -85,6 +121,21 @@ class MyDBALConnectionFactory extends AbstractConfigurableFactory implements Fac 'params', ); } + + public function getModule() + { + return 'doctrine'; + } + + public function getScope() + { + return 'connection'; + } + + public function getName() + { + return 'orm_default'; + } } ``` This can also be combined with `OptionsClassInterface` diff --git a/src/Service/AbstractConfigurableFactory.php b/src/Service/AbstractConfigurableFactory.php index d722c8f..c2d457b 100644 --- a/src/Service/AbstractConfigurableFactory.php +++ b/src/Service/AbstractConfigurableFactory.php @@ -49,9 +49,9 @@ public function getOptions(ServiceLocatorInterface $sl) throw new Exception\RuntimeException(sprintf( 'Mandatory option "%s" was not set for configuration "%s.%s.%s".', $option, - $this->getName(), $this->getModule(), - $this->getScope() + $this->getScope(), + $this->getName() )); } }