├── .gitignore ├── tests ├── bootstrap.php └── Dflydev │ └── Tests │ └── Provider │ └── DoctrineOrm │ └── DoctrineOrmServiceProviderTest.php ├── .travis.yml ├── phpunit.xml.dist ├── composer.json ├── LICENSE ├── changelog.md ├── README.md └── src └── Dflydev └── Provider └── DoctrineOrm └── DoctrineOrmServiceProvider.php /.gitignore: -------------------------------------------------------------------------------- 1 | composer.lock 2 | vendor 3 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | add('Dflydev\Tests', __DIR__); 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | sudo: false 4 | 5 | php: 6 | - 5.3.3 7 | - 5.3 8 | - 5.4 9 | - 5.5 10 | 11 | cache: 12 | directories: 13 | - $HOME/.composer/cache 14 | 15 | before_script: 16 | - composer install --prefer-dist 17 | 18 | script: phpunit --coverage-text --verbose 19 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 15 | ./tests/Dflydev/ 16 | 17 | 18 | 19 | 20 | ./src 21 | 22 | 23 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dflydev/doctrine-orm-service-provider", 3 | "description": "Doctrine ORM Service Provider", 4 | "keywords": ["pimple", "cilex", "silex", "doctrine", "orm"], 5 | "homepage": "http://dflydev.com/projects/doctrine-orm-service-provider/", 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "Dragonfly Development Inc.", 10 | "email": "info@dflydev.com", 11 | "homepage": "http://dflydev.com" 12 | }, 13 | { 14 | "name": "Beau Simensen", 15 | "email": "beau@dflydev.com", 16 | "homepage": "http://beausimensen.com" 17 | } 18 | ], 19 | "require": { 20 | "php": ">=5.3.3", 21 | "pimple/pimple": ">=2.1,<4", 22 | "doctrine/orm": "~2.3" 23 | }, 24 | "autoload": { 25 | "psr-4": { 26 | "Dflydev\\Provider\\DoctrineOrm\\": "src/Dflydev/Provider/DoctrineOrm" 27 | } 28 | }, 29 | "extra": { 30 | "branch-alias": { 31 | "dev-master": "2.0.x-dev" 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Dragonfly Development Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | # 2.0.x 2 | 3 | ## 2.0.x-dev 4 | 5 | ## v2.0.1 (2015-09-07) 6 | 7 | * @dominikzogg: Removed PSR-0 resource locator (#43) 8 | * @bc-luke: Add auth cache option for Redis (#48) 9 | * @c960657: Security fix for FileCache (#57) 10 | * @straccio: Added couchebase cache support (#50) 11 | * @damiankloip: Default cache configuration is now an array (#51) 12 | 13 | ## v2.0.0 (2014-07-24) 14 | 15 | * @andrewshell: Support Pimple 3 (#39) 16 | * @dominikzogg: Add Simple driver support (#38) 17 | * @andrewshell: Use Pimple\Container instead of Pimple for $configs (#34) 18 | * @dominikzogg: Remove Silex tests (#31) 19 | * @simensen: Purge PSR-0 Resource Locator 20 | * @dominikzogg: Support for Pimple 2 (#26) 21 | 22 | # 1.0.x 23 | 24 | ## 1.0.x-dev 25 | 26 | * @bc-luke: Add auth cache option for Redis (#48) 27 | 28 | ## v1.0.5 (2014-06-17) 29 | 30 | * @c960657: Support remaining config options (#22) 31 | * @iolloyd: Remove unneeded passing of Pimple $app (#25) 32 | 33 | ## v1.0.4 (2013-12-11) 34 | 35 | * @mrkrstphr: Custom types (#21) 36 | 37 | ## v1.0.3 (2013-10-25) 38 | 39 | * @tdbui83: Redis cache (#20) 40 | * @Richard-NL: Filesystem cache (#19) 41 | 42 | ## v1.0.2 (2013-09-19) 43 | 44 | * @marcojanssen: Added alias mapping option for entity namespaces (#18) 45 | * @asiermarques: Application level cache namespaces (#15, #16, #17) 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Doctrine ORM Service Provider 2 | ============================= 3 | 4 | Provides Doctrine ORM Entity Managers as services to Pimple applications. 5 | 6 | 7 | Features 8 | -------- 9 | 10 | * Leverages the core [Doctrine Service Provider][1] for either 11 | Silex. 12 | * Default Entity Manager can be bound to any database connection 13 | * Multiple Entity Managers can be defined 14 | * Mechanism for allowing Service Providers to register their own 15 | mappings 16 | 17 | 18 | Requirements 19 | ------------ 20 | 21 | * PHP 5.3+ 22 | * Pimple ~2.1 23 | * Doctrine ~2.3 24 | 25 | Currently requires both **dbs** and **dbs.event_manager** services in 26 | order to work. These can be provided by a Doctrine Service Provider 27 | like the [Silex][1] one. If you can or want to fake it, go for it. :) 28 | 29 | 30 | Installation 31 | ------------ 32 | 33 | Through [Composer](http://getcomposer.org) as [dflydev/doctrine-orm-service-provider][6]. 34 | 35 | 36 | Usage 37 | ----- 38 | 39 | To get up and running, register `DoctrineOrmServiceProvider` and 40 | manually specify the directory that will contain the proxies along 41 | with at least one mapping. 42 | 43 | In each of these examples an Entity Manager that is bound to the 44 | default database connection will be provided. It will be accessible 45 | via **orm.em**. 46 | 47 | ```php 48 | 'pdo_sqlite', 66 | 'path' => '/path/to/sqlite.db', 67 | ); 68 | 69 | // ensure that $container['dbs'] and $container['dbs.event_manager'] 70 | // are available, most likely by way of a core service provider. 71 | 72 | $container->register(new DoctrineOrmServiceProvider, array( 73 | 'orm.proxies_dir' => '/path/to/proxies', 74 | 'orm.em.options' => array( 75 | 'mappings' => array( 76 | // Using actual filesystem paths 77 | array( 78 | 'type' => 'annotation', 79 | 'namespace' => 'Foo\Entities', 80 | 'path' => __DIR__.'/src/Foo/Entities', 81 | ), 82 | array( 83 | 'type' => 'xml', 84 | 'namespace' => 'Bat\Entities', 85 | 'path' => __DIR__.'/src/Bat/Resources/mappings', 86 | ), 87 | // XML/YAML driver (Symfony2 style) 88 | // Mapping files can be named like Foo.orm.yml 89 | // instead of Baz.Entities.Foo.dcm.yml 90 | array( 91 | 'type' => 'simple_yml', 92 | 'namespace' => 'Baz\Entities', 93 | 'path' => __DIR__.'/src/Bat/Resources/config/doctrine', 94 | ), 95 | ), 96 | ), 97 | )); 98 | ``` 99 | 100 | ### Silex 101 | 102 | Version 2.x of this service provider is compatible with version 2.x of Silex and version 1.x of this service provider is compatible with version 1.x of Silex. The following is an example of version 2.x of this service provider and version 2.x of Silex. 103 | 104 | ```php 105 | register(new DoctrineServiceProvider, array( 114 | 'db.options' => array( 115 | 'driver' => 'pdo_sqlite', 116 | 'path' => '/path/to/sqlite.db', 117 | ), 118 | )); 119 | 120 | $app->register(new DoctrineOrmServiceProvider, array( 121 | 'orm.proxies_dir' => '/path/to/proxies', 122 | 'orm.em.options' => array( 123 | 'mappings' => array( 124 | // Using actual filesystem paths 125 | array( 126 | 'type' => 'annotation', 127 | 'namespace' => 'Foo\Entities', 128 | 'path' => __DIR__.'/src/Foo/Entities', 129 | ), 130 | array( 131 | 'type' => 'xml', 132 | 'namespace' => 'Bat\Entities', 133 | 'path' => __DIR__.'/src/Bat/Resources/mappings', 134 | ), 135 | ), 136 | ), 137 | )); 138 | ``` 139 | 140 | Configuration 141 | ------------- 142 | 143 | ### Parameters 144 | 145 | * **orm.em.options**: 146 | Array of Entity Manager options. 147 | 148 | These options are available: 149 | * **connection** (Default: default): 150 | String defining which database connection to use. Used when using 151 | named databases via **dbs**. 152 | * **mappings**: 153 | Array of mapping definitions. 154 | 155 | Each mapping definition should be an array with the following 156 | options: 157 | * **type**: Mapping driver type, one of `annotation`, `xml`, `yml`, `simple_xml`, `simple_yml` or `php`. 158 | * **namespace**: Namespace in which the entities reside. 159 | 160 | *New: the `simple_xml` and `simple_yml` driver types were added in v1.1 and provide support for the [simplified XML driver][8] and [simplified YAML driver][9] of Doctrine.* 161 | 162 | Additionally, each mapping definition should contain one of the 163 | following options: 164 | * **path**: Path to where the mapping files are located. This should 165 | be an actual filesystem path. For the php driver it can be an array 166 | of paths 167 | * **resources_namespace**: A namespaceish path to where the mapping 168 | files are located. Example: `Path\To\Foo\Resources\mappings` 169 | 170 | Each mapping definition can have the following optional options: 171 | * **alias** (Default: null): Set the alias for the entity namespace. 172 | 173 | Each **annotation** mapping may also specify the following options: 174 | * **use_simple_annotation_reader** (Default: true): 175 | If `true`, only simple notations like `@Entity` will work. 176 | If `false`, more advanced notations and aliasing via `use` will 177 | work. (Example: `use Doctrine\ORM\Mapping AS ORM`, `@ORM\Entity`) 178 | Note that if set to `false`, the `AnnotationRegistry` will probably 179 | need to be configured correctly so that it can load your Annotations 180 | classes. See this FAQ: 181 | [Why aren't my Annotations classes being found?](#why-arent-my-annotations-classes-being-found) 182 | * **query_cache** (Default: setting specified by orm.default_cache): 183 | String or array describing query cache implementation. 184 | * **metadata_cache** (Default: setting specified by orm.default_cache): 185 | String or array describing metadata cache implementation. 186 | * **result_cache** (Default: setting specified by orm.default_cache): 187 | String or array describing result cache implementation. 188 | * **hydration_cache** (Default: setting specified by orm.default_cache): 189 | String or array describing hydration cache implementation. 190 | * **types** 191 | An array of custom types in the format of 'typeName' => 'Namespace\To\Type\Class' 192 | * **orm.ems.options**: 193 | Array of Entity Manager configuration sets indexed by each Entity Manager's 194 | name. Each value should look like **orm.em.options**. 195 | 196 | Example configuration: 197 | 198 | ```php 199 | array( 203 | 'connection' => 'mysql', 204 | 'mappings' => array(), 205 | ), 206 | 'sqlite' => array( 207 | 'connection' => 'sqlite', 208 | 'mappings' => array(), 209 | ), 210 | ); 211 | ``` 212 | 213 | Example usage: 214 | 215 | ```php 216 | 21 | */ 22 | class DoctrineOrmServiceProviderTest extends \PHPUnit_Framework_TestCase 23 | { 24 | protected function createMockDefaultAppAndDeps() 25 | { 26 | $container = new Container(); 27 | 28 | $eventManager = $this->getMock('Doctrine\Common\EventManager'); 29 | $connection = $this 30 | ->getMockBuilder('Doctrine\DBAL\Connection') 31 | ->disableOriginalConstructor() 32 | ->getMock(); 33 | 34 | $connection 35 | ->expects($this->any()) 36 | ->method('getEventManager') 37 | ->will($this->returnValue($eventManager)); 38 | 39 | $container['dbs'] = new Container(array( 40 | 'default' => $connection, 41 | )); 42 | 43 | $container['dbs.event_manager'] = new Container(array( 44 | 'default' => $eventManager, 45 | )); 46 | 47 | return array($container, $connection, $eventManager);; 48 | } 49 | 50 | /** 51 | * @return Container 52 | */ 53 | protected function createMockDefaultApp() 54 | { 55 | list ($container, $connection, $eventManager) = $this->createMockDefaultAppAndDeps(); 56 | 57 | return $container; 58 | } 59 | 60 | /** 61 | * Test registration (test expected class for default implementations) 62 | */ 63 | public function testRegisterDefaultImplementations() 64 | { 65 | $container = $this->createMockDefaultApp(); 66 | 67 | $container->register(new DoctrineOrmServiceProvider()); 68 | 69 | $this->assertEquals($container['orm.em'], $container['orm.ems']['default']); 70 | $this->assertInstanceOf('Doctrine\Common\Cache\ArrayCache', $container['orm.em.config']->getQueryCacheImpl()); 71 | $this->assertInstanceOf('Doctrine\Common\Cache\ArrayCache', $container['orm.em.config']->getResultCacheImpl()); 72 | $this->assertInstanceOf('Doctrine\Common\Cache\ArrayCache', $container['orm.em.config']->getMetadataCacheImpl()); 73 | $this->assertInstanceOf('Doctrine\Common\Cache\ArrayCache', $container['orm.em.config']->getHydrationCacheImpl()); 74 | $this->assertInstanceOf('Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain', $container['orm.em.config']->getMetadataDriverImpl()); 75 | } 76 | 77 | /** 78 | * Test registration (test equality for defined implementations) 79 | */ 80 | public function testRegisterDefinedImplementations() 81 | { 82 | $container = $this->createMockDefaultApp(); 83 | 84 | $queryCache = $this->getMock('Doctrine\Common\Cache\ArrayCache'); 85 | $resultCache = $this->getMock('Doctrine\Common\Cache\ArrayCache'); 86 | $metadataCache = $this->getMock('Doctrine\Common\Cache\ArrayCache'); 87 | 88 | $mappingDriverChain = $this->getMock('Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain'); 89 | 90 | $container['orm.cache.instances.default.query'] = $queryCache; 91 | $container['orm.cache.instances.default.result'] = $resultCache; 92 | $container['orm.cache.instances.default.metadata'] = $metadataCache; 93 | 94 | $container['orm.mapping_driver_chain.instances.default'] = $mappingDriverChain; 95 | 96 | $container->register(new DoctrineOrmServiceProvider); 97 | 98 | $this->assertEquals($container['orm.em'], $container['orm.ems']['default']); 99 | $this->assertEquals($queryCache, $container['orm.em.config']->getQueryCacheImpl()); 100 | $this->assertEquals($resultCache, $container['orm.em.config']->getResultCacheImpl()); 101 | $this->assertEquals($metadataCache, $container['orm.em.config']->getMetadataCacheImpl()); 102 | $this->assertEquals($mappingDriverChain, $container['orm.em.config']->getMetadataDriverImpl()); 103 | } 104 | 105 | /** 106 | * Test proxy configuration (defaults) 107 | */ 108 | public function testProxyConfigurationDefaults() 109 | { 110 | $container = $this->createMockDefaultApp(); 111 | 112 | $container->register(new DoctrineOrmServiceProvider); 113 | 114 | $this->assertContains('/../../../../../../../cache/doctrine/proxies', $container['orm.em.config']->getProxyDir()); 115 | $this->assertEquals('DoctrineProxy', $container['orm.em.config']->getProxyNamespace()); 116 | $this->assertEquals(1,$container['orm.em.config']->getAutoGenerateProxyClasses()); 117 | } 118 | 119 | /** 120 | * Test proxy configuration (defined) 121 | */ 122 | public function testProxyConfigurationDefined() 123 | { 124 | $container = $this->createMockDefaultApp(); 125 | 126 | $container->register(new DoctrineOrmServiceProvider); 127 | 128 | $entityRepositoryClassName = get_class($this->getMock('Doctrine\Common\Persistence\ObjectRepository')); 129 | $metadataFactoryName = get_class($this->getMock('Doctrine\Common\Persistence\Mapping\ClassMetadataFactory')); 130 | 131 | $entityListenerResolver = $this->getMock('Doctrine\ORM\Mapping\EntityListenerResolver'); 132 | $repositoryFactory = $this->getMock('Doctrine\ORM\Repository\RepositoryFactory'); 133 | 134 | $container['orm.proxies_dir'] = '/path/to/proxies'; 135 | $container['orm.proxies_namespace'] = 'TestDoctrineOrmProxiesNamespace'; 136 | $container['orm.auto_generate_proxies'] = false; 137 | $container['orm.class_metadata_factory_name'] = $metadataFactoryName; 138 | $container['orm.default_repository_class'] = $entityRepositoryClassName; 139 | $container['orm.entity_listener_resolver'] = $entityListenerResolver; 140 | $container['orm.repository_factory'] = $repositoryFactory; 141 | $container['orm.custom.hydration_modes'] = array('mymode' => 'Doctrine\ORM\Internal\Hydration\SimpleObjectHydrator'); 142 | 143 | $this->assertEquals('/path/to/proxies', $container['orm.em.config']->getProxyDir()); 144 | $this->assertEquals('TestDoctrineOrmProxiesNamespace', $container['orm.em.config']->getProxyNamespace()); 145 | $this->assertEquals(0,$container['orm.em.config']->getAutoGenerateProxyClasses()); 146 | $this->assertEquals($metadataFactoryName, $container['orm.em.config']->getClassMetadataFactoryName()); 147 | $this->assertEquals($entityRepositoryClassName, $container['orm.em.config']->getDefaultRepositoryClassName()); 148 | $this->assertEquals($entityListenerResolver, $container['orm.em.config']->getEntityListenerResolver()); 149 | $this->assertEquals($repositoryFactory, $container['orm.em.config']->getRepositoryFactory()); 150 | $this->assertEquals('Doctrine\ORM\Internal\Hydration\SimpleObjectHydrator', $container['orm.em.config']->getCustomHydrationMode('mymode')); 151 | } 152 | 153 | /** 154 | * Test Driver Chain locator 155 | */ 156 | public function testMappingDriverChainLocator() 157 | { 158 | $container = $this->createMockDefaultApp(); 159 | 160 | $container->register(new DoctrineOrmServiceProvider); 161 | 162 | $default = $container['orm.mapping_driver_chain.locator'](); 163 | $this->assertEquals($default, $container['orm.mapping_driver_chain.locator']('default')); 164 | $this->assertEquals($default, $container['orm.em.config']->getMetadataDriverImpl()); 165 | } 166 | 167 | /** 168 | * Test adding a mapping driver (use default entity manager) 169 | */ 170 | public function testAddMappingDriverDefault() 171 | { 172 | $container = $this->createMockDefaultApp(); 173 | 174 | $mappingDriver = $this->getMock('Doctrine\Common\Persistence\Mapping\Driver\MappingDriver'); 175 | 176 | $mappingDriverChain = $this->getMock('Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain'); 177 | $mappingDriverChain 178 | ->expects($this->once()) 179 | ->method('addDriver') 180 | ->with($mappingDriver, 'Test\Namespace'); 181 | 182 | $container['orm.mapping_driver_chain.instances.default'] = $mappingDriverChain; 183 | 184 | $container->register(new DoctrineOrmServiceProvider); 185 | 186 | $container['orm.add_mapping_driver']($mappingDriver, 'Test\Namespace'); 187 | } 188 | 189 | /** 190 | * Test adding a mapping driver (specify default entity manager by name) 191 | */ 192 | public function testAddMappingDriverNamedEntityManager() 193 | { 194 | $container = $this->createMockDefaultApp(); 195 | 196 | $mappingDriver = $this->getMock('Doctrine\Common\Persistence\Mapping\Driver\MappingDriver'); 197 | 198 | $mappingDriverChain = $this->getMock('Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain'); 199 | $mappingDriverChain 200 | ->expects($this->once()) 201 | ->method('addDriver') 202 | ->with($mappingDriver, 'Test\Namespace'); 203 | 204 | $container['orm.mapping_driver_chain.instances.default'] = $mappingDriverChain; 205 | 206 | $container->register(new DoctrineOrmServiceProvider); 207 | 208 | $container['orm.add_mapping_driver']($mappingDriver, 'Test\Namespace'); 209 | } 210 | 211 | /** 212 | * Test specifying an invalid cache type (just named) 213 | */ 214 | public function testInvalidCacheTypeNamed() 215 | { 216 | $container = $this->createMockDefaultApp(); 217 | 218 | $container->register(new DoctrineOrmServiceProvider); 219 | 220 | $container['orm.em.options'] = array( 221 | 'query_cache' => 'INVALID', 222 | ); 223 | 224 | try { 225 | $container['orm.em']; 226 | 227 | $this->fail('Expected invalid query cache driver exception'); 228 | } catch (\RuntimeException $e) { 229 | $this->assertEquals("Factory 'orm.cache.factory.INVALID' for cache type 'INVALID' not defined (is it spelled correctly?)", $e->getMessage()); 230 | } 231 | } 232 | 233 | /** 234 | * Test specifying an invalid cache type (driver as option) 235 | */ 236 | public function testInvalidCacheTypeDriverAsOption() 237 | { 238 | $container = $this->createMockDefaultApp(); 239 | 240 | $container->register(new DoctrineOrmServiceProvider); 241 | 242 | $container['orm.em.options'] = array( 243 | 'query_cache' => array( 244 | 'driver' => 'INVALID', 245 | ), 246 | ); 247 | 248 | try { 249 | $container['orm.em']; 250 | 251 | $this->fail('Expected invalid query cache driver exception'); 252 | } catch (\RuntimeException $e) { 253 | $this->assertEquals("Factory 'orm.cache.factory.INVALID' for cache type 'INVALID' not defined (is it spelled correctly?)", $e->getMessage()); 254 | } 255 | } 256 | 257 | /** 258 | * Test orm.em_name_from_param_key () 259 | */ 260 | public function testNameFromParamKey() 261 | { 262 | $container = $this->createMockDefaultApp(); 263 | 264 | $container['my.baz'] = 'baz'; 265 | 266 | $container->register(new DoctrineOrmServiceProvider); 267 | 268 | $container['orm.ems.default'] = 'foo'; 269 | 270 | $this->assertEquals('foo', $container['orm.ems.default']); 271 | $this->assertEquals('foo', $container['orm.em_name_from_param_key']('my.bar')); 272 | $this->assertEquals('baz', $container['orm.em_name_from_param_key']('my.baz')); 273 | } 274 | 275 | /** 276 | * Test specifying an invalid mapping configuration (not an array of arrays) 277 | * 278 | * @expectedException \InvalidArgumentException 279 | * @expectedExceptionMessage The 'orm.em.options' option 'mappings' should be an array of arrays. 280 | */ 281 | public function testInvalidMappingAsOption() 282 | { 283 | $container = $this->createMockDefaultApp(); 284 | 285 | $container->register(new DoctrineOrmServiceProvider); 286 | 287 | $container['orm.em.options'] = array( 288 | 'mappings' => array( 289 | 'type' => 'annotation', 290 | 'namespace' => 'Foo\Entities', 291 | 'path' => __DIR__.'/src/Foo/Entities', 292 | ), 293 | ); 294 | 295 | $container['orm.ems.config']; 296 | } 297 | 298 | /** 299 | * Test if namespace alias can be set through the mapping options 300 | */ 301 | public function testMappingAlias() 302 | { 303 | $container = $this->createMockDefaultApp(); 304 | 305 | $container->register(new DoctrineOrmServiceProvider); 306 | 307 | $alias = 'Foo'; 308 | $namespace = 'Foo\Entities'; 309 | 310 | $container['orm.em.options'] = array( 311 | 'mappings' => array( 312 | array( 313 | 'type' => 'annotation', 314 | 'namespace' => $namespace, 315 | 'path' => __DIR__.'/src/Foo/Entities', 316 | 'alias' => $alias 317 | ) 318 | ), 319 | ); 320 | 321 | $this->assertEquals($namespace, $container['orm.em.config']->getEntityNameSpace($alias)); 322 | } 323 | 324 | public function testStrategy() 325 | { 326 | $app = $this->createMockDefaultApp(); 327 | 328 | $doctrineOrmServiceProvider = new DoctrineOrmServiceProvider; 329 | $doctrineOrmServiceProvider->register($app); 330 | 331 | $namingStrategy = $this->getMock('Doctrine\ORM\Mapping\DefaultNamingStrategy'); 332 | $quoteStrategy = $this->getMock('Doctrine\ORM\Mapping\DefaultQuoteStrategy'); 333 | 334 | $app['orm.strategy.naming'] = $namingStrategy; 335 | $app['orm.strategy.quote'] = $quoteStrategy; 336 | 337 | $this->assertEquals($namingStrategy, $app['orm.em.config']->getNamingStrategy()); 338 | $this->assertEquals($quoteStrategy, $app['orm.em.config']->getQuoteStrategy()); 339 | } 340 | 341 | public function testCustomFunctions() 342 | { 343 | $app = $this->createMockDefaultApp(); 344 | 345 | $doctrineOrmServiceProvider = new DoctrineOrmServiceProvider; 346 | $doctrineOrmServiceProvider->register($app); 347 | 348 | $numericFunction = $this->getMock('Doctrine\ORM\Query\AST\Functions\FunctionNode', array(), array('mynum')); 349 | $stringFunction = $this->getMock('Doctrine\ORM\Query\AST\Functions\FunctionNode', array(), array('mynum')); 350 | $datetimeFunction = $this->getMock('Doctrine\ORM\Query\AST\Functions\FunctionNode', array(), array('mynum')); 351 | 352 | $app['orm.custom.functions.string'] = array('mystring' => $numericFunction); 353 | $app['orm.custom.functions.numeric'] = array('mynumeric' => $stringFunction); 354 | $app['orm.custom.functions.datetime'] = array('mydatetime' => $datetimeFunction); 355 | 356 | $this->assertEquals($numericFunction, $app['orm.em.config']->getCustomStringFunction('mystring')); 357 | $this->assertEquals($numericFunction, $app['orm.em.config']->getCustomNumericFunction('mynumeric')); 358 | $this->assertEquals($numericFunction, $app['orm.em.config']->getCustomDatetimeFunction('mydatetime')); 359 | } 360 | } 361 | -------------------------------------------------------------------------------- /src/Dflydev/Provider/DoctrineOrm/DoctrineOrmServiceProvider.php: -------------------------------------------------------------------------------- 1 | 45 | */ 46 | class DoctrineOrmServiceProvider implements ServiceProviderInterface 47 | { 48 | /** 49 | * Register ORM service. 50 | * 51 | * @param Container $container 52 | */ 53 | public function register(Container $container) 54 | { 55 | foreach ($this->getOrmDefaults() as $key => $value) { 56 | if (!isset($container[$key])) { 57 | $container[$key] = $value; 58 | } 59 | } 60 | 61 | $container['orm.em.default_options'] = array( 62 | 'connection' => 'default', 63 | 'mappings' => array(), 64 | 'types' => array() 65 | ); 66 | 67 | $container['orm.ems.options.initializer'] = $container->protect(function () use ($container) { 68 | static $initialized = false; 69 | 70 | if ($initialized) { 71 | return; 72 | } 73 | 74 | $initialized = true; 75 | 76 | if (!isset($container['orm.ems.options'])) { 77 | $container['orm.ems.options'] = array('default' => isset($container['orm.em.options']) ? $container['orm.em.options'] : array()); 78 | } 79 | 80 | $tmp = $container['orm.ems.options']; 81 | foreach ($tmp as $name => &$options) { 82 | $options = array_replace($container['orm.em.default_options'], $options); 83 | 84 | if (!isset($container['orm.ems.default'])) { 85 | $container['orm.ems.default'] = $name; 86 | } 87 | } 88 | $container['orm.ems.options'] = $tmp; 89 | }); 90 | 91 | $container['orm.em_name_from_param_key'] = $container->protect(function ($paramKey) use ($container) { 92 | $container['orm.ems.options.initializer'](); 93 | 94 | if (isset($container[$paramKey])) { 95 | return $container[$paramKey]; 96 | } 97 | 98 | return $container['orm.ems.default']; 99 | }); 100 | 101 | $container['orm.ems'] = function ($container) { 102 | $container['orm.ems.options.initializer'](); 103 | 104 | $ems = new Container(); 105 | foreach ($container['orm.ems.options'] as $name => $options) { 106 | if ($container['orm.ems.default'] === $name) { 107 | // we use shortcuts here in case the default has been overridden 108 | $config = $container['orm.em.config']; 109 | } else { 110 | $config = $container['orm.ems.config'][$name]; 111 | } 112 | 113 | $ems[$name] = function ($ems) use ($container, $options, $config) { 114 | return EntityManager::create( 115 | $container['dbs'][$options['connection']], 116 | $config, 117 | $container['dbs.event_manager'][$options['connection']] 118 | ); 119 | }; 120 | } 121 | 122 | return $ems; 123 | }; 124 | 125 | $container['orm.ems.config'] = function ($container) { 126 | $container['orm.ems.options.initializer'](); 127 | 128 | $configs = new Container(); 129 | foreach ($container['orm.ems.options'] as $name => $options) { 130 | $config = new Configuration; 131 | 132 | $container['orm.cache.configurer']($name, $config, $options); 133 | 134 | $config->setProxyDir($container['orm.proxies_dir']); 135 | $config->setProxyNamespace($container['orm.proxies_namespace']); 136 | $config->setAutoGenerateProxyClasses($container['orm.auto_generate_proxies']); 137 | 138 | $config->setCustomStringFunctions($container['orm.custom.functions.string']); 139 | $config->setCustomNumericFunctions($container['orm.custom.functions.numeric']); 140 | $config->setCustomDatetimeFunctions($container['orm.custom.functions.datetime']); 141 | $config->setCustomHydrationModes($container['orm.custom.hydration_modes']); 142 | 143 | $config->setClassMetadataFactoryName($container['orm.class_metadata_factory_name']); 144 | $config->setDefaultRepositoryClassName($container['orm.default_repository_class']); 145 | 146 | $config->setEntityListenerResolver($container['orm.entity_listener_resolver']); 147 | $config->setRepositoryFactory($container['orm.repository_factory']); 148 | 149 | $config->setNamingStrategy($container['orm.strategy.naming']); 150 | $config->setQuoteStrategy($container['orm.strategy.quote']); 151 | 152 | $chain = $container['orm.mapping_driver_chain.locator']($name); 153 | 154 | foreach ((array) $options['mappings'] as $entity) { 155 | if (!is_array($entity)) { 156 | throw new \InvalidArgumentException( 157 | "The 'orm.em.options' option 'mappings' should be an array of arrays." 158 | ); 159 | } 160 | 161 | if (isset($entity['alias'])) { 162 | $config->addEntityNamespace($entity['alias'], $entity['namespace']); 163 | } 164 | 165 | switch ($entity['type']) { 166 | case 'annotation': 167 | $useSimpleAnnotationReader = 168 | isset($entity['use_simple_annotation_reader']) 169 | ? $entity['use_simple_annotation_reader'] 170 | : true; 171 | $driver = $config->newDefaultAnnotationDriver((array) $entity['path'], $useSimpleAnnotationReader); 172 | $chain->addDriver($driver, $entity['namespace']); 173 | break; 174 | case 'yml': 175 | $driver = new YamlDriver($entity['path']); 176 | $chain->addDriver($driver, $entity['namespace']); 177 | break; 178 | case 'simple_yml': 179 | $driver = new SimplifiedYamlDriver(array($entity['path'] => $entity['namespace'])); 180 | $chain->addDriver($driver, $entity['namespace']); 181 | break; 182 | case 'xml': 183 | $driver = new XmlDriver($entity['path']); 184 | $chain->addDriver($driver, $entity['namespace']); 185 | break; 186 | case 'simple_xml': 187 | $driver = new SimplifiedXmlDriver(array($entity['path'] => $entity['namespace'])); 188 | $chain->addDriver($driver, $entity['namespace']); 189 | break; 190 | case 'php': 191 | $driver = new StaticPHPDriver($entity['path']); 192 | $chain->addDriver($driver, $entity['namespace']); 193 | break; 194 | default: 195 | throw new \InvalidArgumentException(sprintf('"%s" is not a recognized driver', $entity['type'])); 196 | break; 197 | } 198 | } 199 | $config->setMetadataDriverImpl($chain); 200 | 201 | foreach ((array) $options['types'] as $typeName => $typeClass) { 202 | if (Type::hasType($typeName)) { 203 | Type::overrideType($typeName, $typeClass); 204 | } else { 205 | Type::addType($typeName, $typeClass); 206 | } 207 | } 208 | 209 | $configs[$name] = $config; 210 | } 211 | 212 | return $configs; 213 | }; 214 | 215 | $container['orm.cache.configurer'] = $container->protect(function ($name, Configuration $config, $options) use ($container) { 216 | $config->setMetadataCacheImpl($container['orm.cache.locator']($name, 'metadata', $options)); 217 | $config->setQueryCacheImpl($container['orm.cache.locator']($name, 'query', $options)); 218 | $config->setResultCacheImpl($container['orm.cache.locator']($name, 'result', $options)); 219 | $config->setHydrationCacheImpl($container['orm.cache.locator']($name, 'hydration', $options)); 220 | }); 221 | 222 | $container['orm.cache.locator'] = $container->protect(function ($name, $cacheName, $options) use ($container) { 223 | $cacheNameKey = $cacheName . '_cache'; 224 | 225 | if (!isset($options[$cacheNameKey])) { 226 | $options[$cacheNameKey] = $container['orm.default_cache']; 227 | } 228 | 229 | if (isset($options[$cacheNameKey]) && !is_array($options[$cacheNameKey])) { 230 | $options[$cacheNameKey] = array( 231 | 'driver' => $options[$cacheNameKey], 232 | ); 233 | } 234 | 235 | if (!isset($options[$cacheNameKey]['driver'])) { 236 | throw new \RuntimeException("No driver specified for '$cacheName'"); 237 | } 238 | 239 | $driver = $options[$cacheNameKey]['driver']; 240 | 241 | $cacheInstanceKey = 'orm.cache.instances.'.$name.'.'.$cacheName; 242 | if (isset($container[$cacheInstanceKey])) { 243 | return $container[$cacheInstanceKey]; 244 | } 245 | 246 | $cache = $container['orm.cache.factory']($driver, $options[$cacheNameKey]); 247 | 248 | if (isset($options['cache_namespace']) && $cache instanceof CacheProvider) { 249 | $cache->setNamespace($options['cache_namespace']); 250 | } 251 | 252 | return $container[$cacheInstanceKey] = $cache; 253 | }); 254 | 255 | $container['orm.cache.factory.backing_memcache'] = $container->protect(function () { 256 | return new \Memcache; 257 | }); 258 | 259 | $container['orm.cache.factory.memcache'] = $container->protect(function ($cacheOptions) use ($container) { 260 | if (empty($cacheOptions['host']) || empty($cacheOptions['port'])) { 261 | throw new \RuntimeException('Host and port options need to be specified for memcache cache'); 262 | } 263 | 264 | /** @var \Memcache $memcache */ 265 | $memcache = $container['orm.cache.factory.backing_memcache'](); 266 | $memcache->connect($cacheOptions['host'], $cacheOptions['port']); 267 | 268 | $cache = new MemcacheCache; 269 | $cache->setMemcache($memcache); 270 | 271 | return $cache; 272 | }); 273 | 274 | $container['orm.cache.factory.backing_memcached'] = $container->protect(function () { 275 | return new \Memcached; 276 | }); 277 | 278 | $container['orm.cache.factory.memcached'] = $container->protect(function ($cacheOptions) use ($container) { 279 | if (empty($cacheOptions['host']) || empty($cacheOptions['port'])) { 280 | throw new \RuntimeException('Host and port options need to be specified for memcached cache'); 281 | } 282 | 283 | /** @var \Memcached $memcached */ 284 | $memcached = $container['orm.cache.factory.backing_memcached'](); 285 | $memcached->addServer($cacheOptions['host'], $cacheOptions['port']); 286 | 287 | $cache = new MemcachedCache; 288 | $cache->setMemcached($memcached); 289 | 290 | return $cache; 291 | }); 292 | 293 | $container['orm.cache.factory.backing_redis'] = $container->protect(function () { 294 | return new \Redis; 295 | }); 296 | 297 | $container['orm.cache.factory.redis'] = $container->protect(function ($cacheOptions) use ($container) { 298 | if (empty($cacheOptions['host']) || empty($cacheOptions['port'])) { 299 | throw new \RuntimeException('Host and port options need to be specified for redis cache'); 300 | } 301 | 302 | /** @var \Redis $redis */ 303 | $redis = $container['orm.cache.factory.backing_redis'](); 304 | $redis->connect($cacheOptions['host'], $cacheOptions['port']); 305 | 306 | if (isset($cacheOptions['password'])) { 307 | $redis->auth($cacheOptions['password']); 308 | } 309 | 310 | $cache = new RedisCache; 311 | $cache->setRedis($redis); 312 | 313 | return $cache; 314 | }); 315 | 316 | $container['orm.cache.factory.array'] = $container->protect(function () { 317 | return new ArrayCache; 318 | }); 319 | 320 | $container['orm.cache.factory.apc'] = $container->protect(function () { 321 | return new ApcCache; 322 | }); 323 | 324 | $container['orm.cache.factory.xcache'] = $container->protect(function () { 325 | return new XcacheCache; 326 | }); 327 | 328 | $container['orm.cache.factory.filesystem'] = $container->protect(function ($cacheOptions) { 329 | if (empty($cacheOptions['path'])) { 330 | throw new \RuntimeException('FilesystemCache path not defined'); 331 | } 332 | 333 | $cacheOptions += array( 334 | 'extension' => FilesystemCache::EXTENSION, 335 | 'umask' => 0002, 336 | ); 337 | return new FilesystemCache($cacheOptions['path'], $cacheOptions['extension'], $cacheOptions['umask']); 338 | }); 339 | 340 | $container['orm.cache.factory.couchbase'] = $container->protect(function($cacheOptions){ 341 | $host=''; 342 | $bucketName=''; 343 | $user=''; 344 | $password=''; 345 | if (empty($cacheOptions['host'])) { 346 | $host='127.0.0.1'; 347 | } 348 | if (empty($cacheOptions['bucket'])) { 349 | $bucketName='default'; 350 | } 351 | if (!empty($cacheOptions['user'])) { 352 | $user=$cacheOptions['user']; 353 | } 354 | if (!empty($cacheOptions['password'])) { 355 | $password=$cacheOptions['password']; 356 | } 357 | 358 | $couchbase = new \Couchbase($host,$user,$password,$bucketName); 359 | $cache = new CouchbaseCache(); 360 | $cache->setCouchbase($couchbase); 361 | return $cache; 362 | }); 363 | 364 | $container['orm.cache.factory'] = $container->protect(function ($driver, $cacheOptions) use ($container) { 365 | $cacheFactoryKey = 'orm.cache.factory.'.$driver; 366 | if (!isset($container[$cacheFactoryKey])) { 367 | throw new \RuntimeException("Factory '$cacheFactoryKey' for cache type '$driver' not defined (is it spelled correctly?)"); 368 | } 369 | 370 | return $container[$cacheFactoryKey]($cacheOptions); 371 | }); 372 | 373 | $container['orm.mapping_driver_chain.locator'] = $container->protect(function ($name = null) use ($container) { 374 | $container['orm.ems.options.initializer'](); 375 | 376 | if (null === $name) { 377 | $name = $container['orm.ems.default']; 378 | } 379 | 380 | $cacheInstanceKey = 'orm.mapping_driver_chain.instances.'.$name; 381 | if (isset($container[$cacheInstanceKey])) { 382 | return $container[$cacheInstanceKey]; 383 | } 384 | 385 | return $container[$cacheInstanceKey] = $container['orm.mapping_driver_chain.factory']($name); 386 | }); 387 | 388 | $container['orm.mapping_driver_chain.factory'] = $container->protect(function ($name) use ($container) { 389 | return new MappingDriverChain; 390 | }); 391 | 392 | $container['orm.add_mapping_driver'] = $container->protect(function (MappingDriver $mappingDriver, $namespace, $name = null) use ($container) { 393 | $container['orm.ems.options.initializer'](); 394 | 395 | if (null === $name) { 396 | $name = $container['orm.ems.default']; 397 | } 398 | 399 | /** @var MappingDriverChain $driverChain */ 400 | $driverChain = $container['orm.mapping_driver_chain.locator']($name); 401 | $driverChain->addDriver($mappingDriver, $namespace); 402 | }); 403 | 404 | $container['orm.strategy.naming'] = function($container) { 405 | return new DefaultNamingStrategy; 406 | }; 407 | 408 | $container['orm.strategy.quote'] = function($container) { 409 | return new DefaultQuoteStrategy; 410 | }; 411 | 412 | $container['orm.entity_listener_resolver'] = function($container) { 413 | return new DefaultEntityListenerResolver; 414 | }; 415 | 416 | $container['orm.repository_factory'] = function($container) { 417 | return new DefaultRepositoryFactory; 418 | }; 419 | 420 | $container['orm.em'] = function ($container) { 421 | $ems = $container['orm.ems']; 422 | 423 | return $ems[$container['orm.ems.default']]; 424 | }; 425 | 426 | $container['orm.em.config'] = function ($container) { 427 | $configs = $container['orm.ems.config']; 428 | 429 | return $configs[$container['orm.ems.default']]; 430 | }; 431 | } 432 | 433 | /** 434 | * Get default ORM configuration settings. 435 | * 436 | * @return array 437 | */ 438 | protected function getOrmDefaults() 439 | { 440 | return array( 441 | 'orm.proxies_dir' => __DIR__.'/../../../../../../../../cache/doctrine/proxies', 442 | 'orm.proxies_namespace' => 'DoctrineProxy', 443 | 'orm.auto_generate_proxies' => true, 444 | 'orm.default_cache' => array( 445 | 'driver' => 'array', 446 | ), 447 | 'orm.custom.functions.string' => array(), 448 | 'orm.custom.functions.numeric' => array(), 449 | 'orm.custom.functions.datetime' => array(), 450 | 'orm.custom.hydration_modes' => array(), 451 | 'orm.class_metadata_factory_name' => 'Doctrine\ORM\Mapping\ClassMetadataFactory', 452 | 'orm.default_repository_class' => 'Doctrine\ORM\EntityRepository', 453 | ); 454 | } 455 | } 456 | --------------------------------------------------------------------------------