From 9ca8ca3c00b5cbaa10a7eecd9c5b19dc2c4a7699 Mon Sep 17 00:00:00 2001 From: Guido Contreras Woda Date: Wed, 17 Dec 2014 14:04:56 -0300 Subject: [PATCH 1/4] Adds support for all current doctrine metadata drivers --- config/doctrine.php | 32 +++++++- src/Configuration/ConfigurationFactory.php | 92 ++++++++++++++++++++++ src/LaravelDoctrineServiceProvider.php | 26 +++--- src/Metadata/AnnotationsStrategy.php | 36 +++++++++ src/Metadata/MetadataStrategyFactory.php | 82 +++++++++++++++++++ src/Metadata/MetadataStrategyInterface.php | 13 +++ src/Metadata/StaticPhpStrategy.php | 27 +++++++ src/Metadata/XmlStrategy.php | 27 +++++++ src/Metadata/YmlStrategy.php | 27 +++++++ 9 files changed, 348 insertions(+), 14 deletions(-) create mode 100644 src/Configuration/ConfigurationFactory.php create mode 100644 src/Metadata/AnnotationsStrategy.php create mode 100644 src/Metadata/MetadataStrategyFactory.php create mode 100644 src/Metadata/MetadataStrategyInterface.php create mode 100644 src/Metadata/StaticPhpStrategy.php create mode 100644 src/Metadata/XmlStrategy.php create mode 100644 src/Metadata/YmlStrategy.php diff --git a/config/doctrine.php b/config/doctrine.php index 67562f4..25ed55a 100644 --- a/config/doctrine.php +++ b/config/doctrine.php @@ -1,11 +1,41 @@ false, - + /** @deprecated use 'mapping.params' */ 'metadata' => [ base_path('app/models') ], + /** + * Mapping configuration. Allows for any supported doctrine mapping, including your own! + */ + 'mappings' => [ + /** + * One of xml, yml, annotations, static_php or a custom key set below + */ + 'type' => 'annotations', + /** + * Array of params passed to the driver's constructor + */ + 'params' => [ + /** + * All default doctine drivers expect first param to be an array of paths + */ + [base_path('app/models')] + /** + * Further params may be required (ie. annotations require the simple_annotations boolean here) + */ + ], + /** + * Add custom drivers as $key => callable $factory here. + */ + 'custom_drivers' => [ + /** + * Note that $factory is a php callable, currently "object@method" is not supported + */ + ] + ], 'proxy' => [ 'auto_generate' => false, diff --git a/src/Configuration/ConfigurationFactory.php b/src/Configuration/ConfigurationFactory.php new file mode 100644 index 0000000..1f170bb --- /dev/null +++ b/src/Configuration/ConfigurationFactory.php @@ -0,0 +1,92 @@ +metadataFactory = $metadataFactory; + $this->config = $config; + $this->cacheManager = $cacheManager; + } + + /** + * Reads the config files and builds a doctrine Configuration object. + * + * @return \Doctrine\ORM\Configuration + */ + public function create() + { + $configuration = $this->createConfiguration( + $this->config->get('app.debug'), + $this->config->get('doctrine::proxy.directory') + ); + + if ($cacheProvider = $this->config->get('doctrine::cache_provider')) + { + $cache = $this->cacheManager->getCache($cacheProvider); + + $configuration->setMetadataCacheImpl($cache); + $configuration->setQueryCacheImpl($cache); + $configuration->setResultCacheImpl($cache); + } + + $this->applyMetadata($configuration); + + return $configuration; + } + + /** + * @param bool $isDevMode + * @param string|null $proxyDir + * + * @return \Doctrine\ORM\Configuration + */ + private function createConfiguration($isDevMode = false, $proxyDir = null) + { + $proxyDir = $proxyDir ?: sys_get_temp_dir(); + + $config = new Configuration(); + $config->setProxyDir($proxyDir); + $config->setProxyNamespace('DoctrineProxies'); + $config->setAutoGenerateProxyClasses($isDevMode); + + return $config; + } + + /** + * @param \Doctrine\ORM\Configuration $configuration + * + * @return void + */ + private function applyMetadata(Configuration $configuration) + { + foreach ($this->config->get('doctrine::doctrine.metadata.custom_drivers', []) as $type => $factory) + { + $this->metadataFactory->addCustomType($type, $factory); + } + + $metadataStrategy = $this->metadataFactory->getStrategy( + // Defaults to annotations for BC + $this->config->get('doctrine::doctrine.mappings.driver', 'annotations'), + // Defaults to the previous setup of paths + simple_annotations + // The new params key allows the user to provide an array of arguments + // That will be passed on to the driver constructor + $this->config->get('doctrine::doctrine.mappings.params', [ + $this->config->get('doctrine::doctrine.metadata'), + $this->config->get('doctrine::doctrine.simple_annotations') + ]) + ); + + $metadataStrategy->apply($configuration); + } +} diff --git a/src/LaravelDoctrineServiceProvider.php b/src/LaravelDoctrineServiceProvider.php index f509e30..4c6457f 100644 --- a/src/LaravelDoctrineServiceProvider.php +++ b/src/LaravelDoctrineServiceProvider.php @@ -10,6 +10,7 @@ use Illuminate\Auth\AuthManager; use Illuminate\Support\ServiceProvider; use Mitch\LaravelDoctrine\Cache; +use Mitch\LaravelDoctrine\Configuration\ConfigurationFactory; use Mitch\LaravelDoctrine\Configuration\DriverMapper; use Mitch\LaravelDoctrine\Configuration\SqlMapper; use Mitch\LaravelDoctrine\Configuration\SqliteMapper; @@ -96,24 +97,23 @@ private function registerEntityManager() { $this->app->singleton(EntityManager::class, function ($app) { $config = $app['config']['doctrine::doctrine']; - $metadata = Setup::createAnnotationMetadataConfiguration( - $config['metadata'], - $app['config']['app.debug'], - $config['proxy']['directory'], - $app[CacheManager::class]->getCache($config['cache_provider']), - $config['simple_annotations'] - ); - $metadata->addFilter('trashed', TrashedFilter::class); - $metadata->setAutoGenerateProxyClasses($config['proxy']['auto_generate']); - $metadata->setDefaultRepositoryClassName($config['repository']); - $metadata->setSQLLogger($config['logger']); + + /** @type ConfigurationFactory $configurationFactory */ + $configurationFactory = $app->make(ConfigurationFactory::class); + + $configuration = $configurationFactory->create(); + + $configuration->addFilter('trashed', TrashedFilter::class); + $configuration->setAutoGenerateProxyClasses($config['proxy']['auto_generate']); + $configuration->setDefaultRepositoryClassName($config['repository']); + $configuration->setSQLLogger($config['logger']); if (isset($config['proxy']['namespace'])) - $metadata->setProxyNamespace($config['proxy']['namespace']); + $configuration->setProxyNamespace($config['proxy']['namespace']); $eventManager = new EventManager; $eventManager->addEventListener(Events::onFlush, new SoftDeletableListener); - $entityManager = EntityManager::create($this->mapLaravelToDoctrineConfig($app['config']), $metadata, $eventManager); + $entityManager = EntityManager::create($this->mapLaravelToDoctrineConfig($app['config']), $configuration, $eventManager); $entityManager->getFilters()->enable('trashed'); return $entityManager; }); diff --git a/src/Metadata/AnnotationsStrategy.php b/src/Metadata/AnnotationsStrategy.php new file mode 100644 index 0000000..c18274a --- /dev/null +++ b/src/Metadata/AnnotationsStrategy.php @@ -0,0 +1,36 @@ +paths = $paths; + $this->useSimpleAnnotations = $useSimpleAnnotations; + } + + /** + * @param \Doctrine\ORM\Configuration $configuration + * + * @return void + */ + public function apply(Configuration $configuration) + { + $configuration->setMetadataDriverImpl( + $configuration->newDefaultAnnotationDriver( + $this->paths, $this->useSimpleAnnotations + ) + ); + } +} diff --git a/src/Metadata/MetadataStrategyFactory.php b/src/Metadata/MetadataStrategyFactory.php new file mode 100644 index 0000000..9c34b7f --- /dev/null +++ b/src/Metadata/MetadataStrategyFactory.php @@ -0,0 +1,82 @@ +types = [ + 'xml' => [$this, 'xmlStrategy'], + 'yml' => [$this, 'ymlStrategy'], + 'annotations' => [$this, 'annotationsStrategy'], + 'static_php' => [$this, 'staticPhpStrategy'] + ]; + } + + /** + * @param string $type + * @param callable $factory + */ + public function addCustomType($type, callable $factory) + { + $this->types[$type] = $factory; + } + + /** + * @param string $type + * @param array $params + * + * @return \Mitch\LaravelDoctrine\Metadata\MetadataStrategyInterface + */ + public function getStrategy($type, array $params) + { + if (!array_key_exists($type, $this->types)) + { + throw new \UnexpectedValueException("Driver $type is not a valid metadata driver. Please choose one of: " . implode(', ', array_keys($this->types))); + } + + $factoryMethod = $this->types[$type]; + + return call_user_func_array($factoryMethod, $params); + } + + /** + * @param array $paths + * + * @return \Mitch\LaravelDoctrine\Metadata\XmlStrategy + */ + public function xmlStrategy(array $paths) + { + return new XmlStrategy($paths); + } + + /** + * @param array $paths + * + * @return \Mitch\LaravelDoctrine\Metadata\YmlStrategy + */ + public function ymlStrategy(array $paths) + { + return new YmlStrategy($paths); + } + + /** + * @param array $paths + * @param $useSimpleAnnotations + * + * @return \Mitch\LaravelDoctrine\Metadata\AnnotationsStrategy + */ + public function annotationsStrategy(array $paths, $useSimpleAnnotations) + { + return new AnnotationsStrategy($paths, $useSimpleAnnotations); + } + + public function staticPhpStrategy(array $paths) + { + return new StaticPhpStrategy($paths); + } +} diff --git a/src/Metadata/MetadataStrategyInterface.php b/src/Metadata/MetadataStrategyInterface.php new file mode 100644 index 0000000..404caaf --- /dev/null +++ b/src/Metadata/MetadataStrategyInterface.php @@ -0,0 +1,13 @@ +paths = $paths; + } + + /** + * @param \Doctrine\ORM\Configuration $configuration + * + * @return void + */ + public function apply(Configuration $configuration) + { + $configuration->setMetadataDriverImpl(new StaticPHPDriver($this->paths)); + } +} diff --git a/src/Metadata/XmlStrategy.php b/src/Metadata/XmlStrategy.php new file mode 100644 index 0000000..a4f6d59 --- /dev/null +++ b/src/Metadata/XmlStrategy.php @@ -0,0 +1,27 @@ +paths = $paths; + } + + /** + * @param \Doctrine\ORM\Configuration $configuration + * + * @return void + */ + public function apply(Configuration $configuration) + { + $configuration->setMetadataDriverImpl(new XmlDriver($this->paths)); + } +} diff --git a/src/Metadata/YmlStrategy.php b/src/Metadata/YmlStrategy.php new file mode 100644 index 0000000..9651ce9 --- /dev/null +++ b/src/Metadata/YmlStrategy.php @@ -0,0 +1,27 @@ +paths = $paths; + } + + /** + * @param \Doctrine\ORM\Configuration $configuration + * + * @return void + */ + public function apply(Configuration $configuration) + { + $configuration->setMetadataDriverImpl(new YamlDriver($this->paths)); + } +} From 68b2c45f66cf0d37ba7ecbf614f540bc6c78cd7d Mon Sep 17 00:00:00 2001 From: Guido Contreras Woda Date: Wed, 17 Dec 2014 14:29:55 -0300 Subject: [PATCH 2/4] Added tests for the metadata strategy factory --- .../Metadata/MetadataStrategyFactoryTest.php | 200 ++++++++++++++++++ 1 file changed, 200 insertions(+) create mode 100644 tests/Metadata/MetadataStrategyFactoryTest.php diff --git a/tests/Metadata/MetadataStrategyFactoryTest.php b/tests/Metadata/MetadataStrategyFactoryTest.php new file mode 100644 index 0000000..534c505 --- /dev/null +++ b/tests/Metadata/MetadataStrategyFactoryTest.php @@ -0,0 +1,200 @@ +msf = new MetadataStrategyFactory; + $this->params = [ + [ + '/some/paths', + '/to/entities', + '/or/xml/yml/files', + ] + ]; + } + + public function testCreateXmlStrategy() + { + $this->assertInstanceOf( + XmlStrategy::class, + $this->msf->getStrategy('xml', $this->params) + ); + + $this->assertInstanceOf( + XmlStrategy::class, + $this->msf->xmlStrategy($this->params[0]) + ); + } + + public function testCreateYmlStrategy() + { + $this->assertInstanceOf( + YmlStrategy::class, + $this->msf->getStrategy('yml', $this->params) + ); + + $this->assertInstanceOf( + YmlStrategy::class, + $this->msf->ymlStrategy($this->params[0]) + ); + } + + public function testCreateStaticPhpStrategy() + { + $this->assertInstanceOf( + StaticPhpStrategy::class, + $this->msf->getStrategy('static_php', $this->params) + ); + + $this->assertInstanceOf( + StaticPhpStrategy::class, + $this->msf->staticPhpStrategy($this->params[0]) + ); + } + + public function testCreateAnnotationsStrategy() + { + $this->assertInstanceOf( + AnnotationsStrategy::class, + $this->msf->getStrategy('annotations', [['some/paths', 'to/entities'], true]) + ); + + $this->assertInstanceOf( + AnnotationsStrategy::class, + $this->msf->getStrategy('annotations', [['some/paths', 'to/entities'], false]) + ); + + $this->assertInstanceOf( + AnnotationsStrategy::class, + $this->msf->annotationsStrategy(['some/paths', 'to/entities'], true) + ); + + $this->assertInstanceOf( + AnnotationsStrategy::class, + $this->msf->annotationsStrategy(['some/paths', 'to/entities'], false) + ); + } + + public function testFailureToCreateUnknownStrategy() + { + $this->setExpectedException(\UnexpectedValueException::class); + $this->msf->getStrategy('invalid_type', []); + } + + public function testCreateCustomStrategyWithAClosure() + { + $this->msf->addCustomType('my_custom_strategy', function(array $paths, $someString, $aBoolean = false){ + return new ACustomMetadataStrategyForTesting( + $paths, + $someString, + $aBoolean + ); + }); + + $this->assertInstanceOf( + ACustomMetadataStrategyForTesting::class, + $strategy = $this->msf->getStrategy('my_custom_strategy', [ + $this->params[0], + 'some_string_value', + true + ]) + ); + + /** @type ACustomMetadataStrategyForTesting $strategy */ + $this->assertEquals($this->params[0], $strategy->paths); + $this->assertEquals('some_string_value', $strategy->someString); + $this->assertEquals(true, $strategy->aBoolean); + } + + public function testCreateCustomStrategyWithAStaticMethod() + { + $this->msf->addCustomType('my_custom_strategy', [ACustomStrategyFactoryForTesting::class, 'staticCreate']); + + $this->assertInstanceOf( + ACustomMetadataStrategyForTesting::class, + $strategy = $this->msf->getStrategy('my_custom_strategy', [ + $this->params[0], + 'some_string_value', + true + ]) + ); + + /** @type ACustomMetadataStrategyForTesting $strategy */ + $this->assertEquals($this->params[0], $strategy->paths); + $this->assertEquals('some_string_value', $strategy->someString); + $this->assertEquals(true, $strategy->aBoolean); + } + + public function testCreateCustomStrategyWithAnInstanceMethod() + { + $this->msf->addCustomType('my_custom_strategy', [new ACustomStrategyFactoryForTesting, 'instanceCreate']); + + $this->assertInstanceOf( + ACustomMetadataStrategyForTesting::class, + $strategy = $this->msf->getStrategy('my_custom_strategy', [ + $this->params[0], + 'some_string_value', + true + ]) + ); + + /** @type ACustomMetadataStrategyForTesting $strategy */ + $this->assertEquals($this->params[0], $strategy->paths); + $this->assertEquals('some_string_value', $strategy->someString); + $this->assertEquals(true, $strategy->aBoolean); + } +} + +class ACustomMetadataStrategyForTesting implements MetadataStrategyInterface { + public $paths; + public $someString; + public $aBoolean; + + function __construct($paths, $someString, $aBoolean) + { + $this->paths = $paths; + $this->someString = $someString; + $this->aBoolean = $aBoolean; + } + + function apply(Configuration $configuration) {} +} + +class ACustomStrategyFactoryForTesting { + public function instanceCreate($paths, $someString, $aBoolean) + { + return new ACustomMetadataStrategyForTesting( + $paths, + $someString, + $aBoolean + ); + } + + public static function staticCreate($paths, $someString, $aBoolean) + { + return new ACustomMetadataStrategyForTesting( + $paths, + $someString, + $aBoolean + ); + } +} \ No newline at end of file From 01a4ffcc71f4cf5c2353ef7ef2ca29115ffeec45 Mon Sep 17 00:00:00 2001 From: Guido Contreras Woda Date: Wed, 17 Dec 2014 15:04:55 -0300 Subject: [PATCH 3/4] Added test for the configuration factory --- src/Configuration/ConfigurationFactory.php | 2 +- .../ConfigurationFactoryTest.php | 73 +++++++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 tests/Configuration/ConfigurationFactoryTest.php diff --git a/src/Configuration/ConfigurationFactory.php b/src/Configuration/ConfigurationFactory.php index 1f170bb..85d89bc 100644 --- a/src/Configuration/ConfigurationFactory.php +++ b/src/Configuration/ConfigurationFactory.php @@ -2,8 +2,8 @@ use Doctrine\ORM\Cache; use Doctrine\ORM\Configuration; -use Illuminate\Contracts\Config\Repository; use Mitch\LaravelDoctrine\CacheManager; +use Illuminate\Contracts\Config\Repository; use Mitch\LaravelDoctrine\Metadata\MetadataStrategyFactory; class ConfigurationFactory diff --git a/tests/Configuration/ConfigurationFactoryTest.php b/tests/Configuration/ConfigurationFactoryTest.php new file mode 100644 index 0000000..ec41779 --- /dev/null +++ b/tests/Configuration/ConfigurationFactoryTest.php @@ -0,0 +1,73 @@ +cacheManager = $this->getMockBuilder(CacheManager::class)->disableOriginalConstructor()->getMock(); + $this->config = $this->getMock(Repository::class); + $this->metadataFactory = $this->getMock(MetadataStrategyFactory::class); + + $paths = ['some/paths']; + $simpleAnnotations = true; + + $params = [$paths, $simpleAnnotations]; + + $this->config + ->expects($this->atLeastOnce()) + ->method('get') + ->will($this->returnValueMap([ + ['app.debug', null, true], + ['doctrine::proxy.directory', null, '/tmp/proxies'], + ['doctrine::cache_provider', null, 'memcached'], + ['doctrine::doctrine.metadata.custom_drivers', [], []], + ['doctrine::doctrine.mappings.driver', 'annotations', 'annotations'], + ['doctrine::doctrine.mappings.params', $params, $params], + ['doctrine::doctrine.metadata', null, $paths], + ['doctrine::doctrine.simple_annotations', null, $simpleAnnotations] + ])); + + $this->cacheManager + ->expects($this->once()) + ->method('getCache') + ->will($this->returnValue( + $this->getMock('\Doctrine\Common\Cache\Cache') + )); + + $strategy = $this->getMock(MetadataStrategyInterface::class); + $strategy->expects($this->once())->method('apply'); + + $this->metadataFactory->expects($this->once()) + ->method('getStrategy') + ->will($this->returnValue($strategy)); + + $this->configurationFactory = new ConfigurationFactory( + $this->metadataFactory, + $this->config, + $this->cacheManager + ); + } + + public function testCreate() + { + $this->configurationFactory->create(); + } +} From f34c710ab8fb0200fcd93a0f4f4a11eaa26f28fd Mon Sep 17 00:00:00 2001 From: Guido Contreras Woda Date: Wed, 17 Dec 2014 15:11:56 -0300 Subject: [PATCH 4/4] Missing line break at end of file --- tests/Metadata/MetadataStrategyFactoryTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Metadata/MetadataStrategyFactoryTest.php b/tests/Metadata/MetadataStrategyFactoryTest.php index 534c505..98231f9 100644 --- a/tests/Metadata/MetadataStrategyFactoryTest.php +++ b/tests/Metadata/MetadataStrategyFactoryTest.php @@ -197,4 +197,4 @@ public static function staticCreate($paths, $someString, $aBoolean) $aBoolean ); } -} \ No newline at end of file +}