├── .gitignore ├── tests ├── bootstrap.php ├── Mapping │ └── PageMapping.php ├── Document │ └── Page.php ├── Driver │ └── ClassMapDriverTest.php └── Provider │ └── DoctrineMongoDbOdmProviderTest.php ├── .travis.yml ├── src ├── Driver │ ├── OdmMappingInterface.php │ └── ClassMapDriver.php └── Provider │ └── DoctrineMongoDbOdmProvider.php ├── phpunit.xml.dist ├── composer.json ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | composer.lock 2 | /cache 3 | /vendor 4 | 5 | .idea -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | setPsr4('Saxulum\Tests\DoctrineMongoDbOdm\\', __DIR__); 5 | 6 | \Doctrine\Common\Annotations\AnnotationRegistry::registerLoader([$loader, 'loadClass']); 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | services: mongodb 4 | 5 | php: 6 | - 5.6 7 | - 7.0 8 | - 7.1 9 | 10 | before_script: 11 | - echo 'extension=mongodb.so' >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini 12 | - composer --prefer-source install 13 | 14 | script: phpunit --coverage-text --verbose 15 | -------------------------------------------------------------------------------- /src/Driver/OdmMappingInterface.php: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 15 | ./tests 16 | 17 | 18 | 19 | 20 | ./src 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /tests/Mapping/PageMapping.php: -------------------------------------------------------------------------------- 1 | setCollection('page'); 16 | 17 | $metadata->mapField([ 18 | 'fieldName' => 'id', 19 | 'type' => 'string', 20 | 'id' => true, 21 | ]); 22 | 23 | $metadata->mapField([ 24 | 'fieldName' => 'title', 25 | 'type' => 'string', 26 | ]); 27 | 28 | $metadata->mapField([ 29 | 'fieldName' => 'body', 30 | 'type' => 'string', 31 | ]); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "saxulum/saxulum-doctrine-mongodb-odm-provider", 3 | "description": "Saxulum Doctrine MongoDB ODM Provider", 4 | "keywords": ["saxulum", "pimple", "cilex", "silex", "doctrine", "mongo", "mongodb", "odm"], 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "Dominik Zogg", 9 | "email": "dominik.zogg@gmail.com" 10 | } 11 | ], 12 | "require": { 13 | "php": "~7.0|~5.6", 14 | "alcaeus/mongo-php-adapter": "^1.1.3", 15 | "doctrine/mongodb-odm": "~1.0", 16 | "pimple/pimple": ">=2.1,<4", 17 | "saxulum/saxulum-doctrine-mongodb-provider": "~2.0" 18 | }, 19 | "require-dev": { 20 | "phpunit/phpunit": "~5.7" 21 | }, 22 | "provide": { 23 | "ext-mongo": "1.6.14" 24 | }, 25 | "suggest": { 26 | "dflydev/psr0-resource-locator-service-provider": "1.0.*@dev" 27 | }, 28 | "autoload": { 29 | "psr-4": { "Saxulum\\DoctrineMongoDbOdm\\": "src/" } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Dominik Zogg 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. -------------------------------------------------------------------------------- /tests/Document/Page.php: -------------------------------------------------------------------------------- 1 | id; 36 | } 37 | 38 | /** 39 | * @param $title 40 | * 41 | * @return $this 42 | */ 43 | public function setTitle($title) 44 | { 45 | $this->title = $title; 46 | 47 | return $this; 48 | } 49 | 50 | /** 51 | * @return string 52 | */ 53 | public function getTitle() 54 | { 55 | return $this->title; 56 | } 57 | 58 | /** 59 | * @param $body 60 | * 61 | * @return $this 62 | */ 63 | public function setBody($body) 64 | { 65 | $this->body = $body; 66 | 67 | return $this; 68 | } 69 | 70 | /** 71 | * @return string 72 | */ 73 | public function getBody() 74 | { 75 | return $this->body; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /tests/Driver/ClassMapDriverTest.php: -------------------------------------------------------------------------------- 1 | getMockBuilder(ClassMetadata::class) 17 | ->disableOriginalConstructor() 18 | ->setMethods(['setCollection', 'mapField']) 19 | ->getMock(); 20 | 21 | $classMetadata 22 | ->expects(self::once()) 23 | ->method('setCollection') 24 | ->with('page'); 25 | 26 | $classMetadata 27 | ->expects(self::at(1)) 28 | ->method('mapField') 29 | ->with([ 30 | 'fieldName' => 'id', 31 | 'type' => 'string', 32 | 'id' => true, 33 | ]); 34 | 35 | $classMetadata 36 | ->expects(self::at(2)) 37 | ->method('mapField') 38 | ->with([ 39 | 'fieldName' => 'title', 40 | 'type' => 'string', 41 | ]); 42 | 43 | $classMetadata 44 | ->expects(self::at(3)) 45 | ->method('mapField') 46 | ->with([ 47 | 'fieldName' => 'body', 48 | 'type' => 'string', 49 | ]); 50 | 51 | $driver = new ClassMapDriver($this->getClassMap()); 52 | 53 | $driver->loadMetadataForClass(Page::class, $classMetadata); 54 | } 55 | 56 | public function testGetAllClassNamesReturnsTheClassMap() 57 | { 58 | $classMap = $this->getClassMap(); 59 | 60 | $driver = new ClassMapDriver($classMap); 61 | 62 | self::assertEquals(array_keys($classMap), $driver->getAllClassNames()); 63 | } 64 | 65 | private function getClassMap() 66 | { 67 | return [Page::class => PageMapping::class]; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Driver/ClassMapDriver.php: -------------------------------------------------------------------------------- 1 | classMap = $classMap; 23 | } 24 | 25 | /** 26 | * Loads the metadata for the specified class into the provided container. 27 | * 28 | * @param string $className 29 | * @param ClassMetadata $metadata 30 | * @throws MappingException 31 | */ 32 | public function loadMetadataForClass($className, ClassMetadata $metadata) 33 | { 34 | if (false === $metadata instanceof OdmClassMetadata) { 35 | throw new MappingException( 36 | sprintf('Metadata is of class "%s" instead of "%s"', get_class($metadata), OdmClassMetadata::class) 37 | ); 38 | } 39 | 40 | if (false === isset($this->classMap[$className])) { 41 | throw new MappingException( 42 | sprintf('No configured mapping for document "%s"', $className) 43 | ); 44 | } 45 | 46 | $mappingClassName = $this->classMap[$className]; 47 | 48 | if (false === ($mapping = new $mappingClassName()) instanceof OdmMappingInterface) { 49 | throw new MappingException('Class %s does not implement the OdmMappingInterface'); 50 | } 51 | 52 | /* @var OdmMappingInterface $mapping */ 53 | /* @var OdmClassMetadata $metadata */ 54 | 55 | $mapping->configureMapping($metadata); 56 | } 57 | 58 | /** 59 | * Gets the names of all mapped classes known to this driver. 60 | * 61 | * @return array the names of all mapped classes known to this driver 62 | */ 63 | public function getAllClassNames() 64 | { 65 | return array_keys($this->classMap); 66 | } 67 | 68 | /** 69 | * Returns whether the class with the specified name should have its metadata loaded. 70 | * This is only the case if it is either mapped as an Entity or a MappedSuperclass. 71 | * 72 | * @param string $className 73 | * 74 | * @return bool 75 | */ 76 | public function isTransient($className) 77 | { 78 | if (isset($this->classMap[$className])) { 79 | return false; 80 | } 81 | 82 | return true; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | saxulum-doctrine-mongodb-odm-provider 2 | ===================================== 3 | 4 | **works with plain silex-php** 5 | 6 | [![Build Status](https://api.travis-ci.org/saxulum/saxulum-doctrine-mongodb-odm-provider.png?branch=master)](https://travis-ci.org/saxulum/saxulum-doctrine-mongodb-odm-provider) 7 | [![Total Downloads](https://poser.pugx.org/saxulum/saxulum-doctrine-mongodb-odm-provider/downloads.png)](https://packagist.org/packages/saxulum/saxulum-doctrine-mongodb-odm-provider) 8 | [![Latest Stable Version](https://poser.pugx.org/saxulum/saxulum-doctrine-mongodb-odm-provider/v/stable.png)](https://packagist.org/packages/saxulum/saxulum-doctrine-mongodb-odm-provider) 9 | 10 | Provides Doctrine MongoDB ODM Document Managers as services to Pimple applications. 11 | 12 | Features 13 | -------- 14 | 15 | * Default Document Manager can be bound to any database connection 16 | * Multiple Document Managers can be defined 17 | * Mechanism for allowing Service Providers to register their own 18 | mappings 19 | 20 | 21 | Requirements 22 | ------------ 23 | 24 | * PHP 5.3+ 25 | * Doctrine MongoDB ODM ~1.0 26 | 27 | 28 | Installation 29 | ------------ 30 | 31 | Through [Composer](http://getcomposer.org) as [saxulum/saxulum-doctrine-mongodb-odm-provider][4]. 32 | 33 | 34 | Usage 35 | ----- 36 | 37 | To get up and running, register `DoctrineMongoDbOdmProvider` and 38 | manually specify the directory that will contain the proxies along 39 | with at least one mapping. 40 | 41 | In each of these examples an Document Manager that is bound to the 42 | default database connection will be provided. It will be accessible 43 | via **mongodbodm.dm**. 44 | 45 | ```php 46 | register(new DoctrineMongoDbProvider, [ 64 | "mongodb.options" => [ 65 | "server" => "mongodb://localhost:27017", 66 | "options" => [ 67 | "username" => "root", 68 | "password" => "root", 69 | "db" => "admin" 70 | ], 71 | ], 72 | ]); 73 | 74 | $app->register(new DoctrineMongoDbOdmProvider, [ 75 | "mongodbodm.proxies_dir" => "/path/to/proxies", 76 | "mongodbodm.hydrator_dir" => "/path/to/hydrator", 77 | "mongodbodm.dm.options" => [ 78 | "database" => "test", 79 | "mappings" => [ 80 | // Using actual filesystem paths 81 | [ 82 | "type" => "annotation", 83 | "namespace" => "Foo\Entities", 84 | "path" => __DIR__."/src/Foo/Entities", 85 | ], 86 | [ 87 | "type" => "xml", 88 | "namespace" => "Bat\Entities", 89 | "path" => __DIR__."/src/Bat/Resources/mappings", 90 | ], 91 | [ 92 | 'type' => 'class_map', 93 | 'namespace' => 'Bar\Entities', 94 | 'map' => [ 95 | 'Bar\Entities\Bar' => 'Sample\Mapping\Bar' 96 | ] 97 | ] 98 | ], 99 | ], 100 | ]); 101 | ``` 102 | 103 | 104 | Configuration 105 | ------------- 106 | 107 | ### Parameters 108 | 109 | * **mongodbodm.dm.options**: 110 | Array of Document Manager options. 111 | 112 | These options are available: 113 | * **connection** (Default: default): 114 | String defining which database connection to use. Used when using 115 | named databases via **mongodbs**. 116 | * **database** 117 | The database which should be uses 118 | * **mappings**: 119 | Array of mapping definitions. 120 | 121 | Each mapping definition should be an array with the following 122 | options: 123 | * **type**: Mapping driver type, one of `annotation`, `xml`, `yml`, `simple_xml`, `simple_yml`, `php` 124 | or `class_map`. 125 | * **namespace**: Namespace in which the entities reside. 126 | 127 | Additionally, each mapping definition should contain one of the 128 | following options: 129 | * **path**: Path to where the mapping files are located. This should 130 | be an actual filesystem path. For the php driver it can be an array 131 | of paths 132 | * **resources_namespace**: A namespaceish path to where the mapping 133 | files are located. Example: `Path\To\Foo\Resources\mappings` 134 | 135 | Each mapping definition can have the following optional options: 136 | * **alias** (Default: null): Set the alias for the document namespace. 137 | 138 | Each **annotation** mapping may also specify the following options: 139 | * **use_simple_annotation_reader** (Default: true): 140 | If `true`, only simple notations like `@Document` will work. 141 | If `false`, more advanced notations and aliasing via `use` will 142 | work. (Example: `use Doctrine\ODM\MongoDB\Mapping AS ODM`, `@ODM\Document`) 143 | Note that if set to `false`, the `AnnotationRegistry` will probably 144 | need to be configured correctly so that it can load your Annotations 145 | classes. See this FAQ: 146 | [Why aren't my Annotations classes being found?](#why-arent-my-annotations-classes-being-found) 147 | 148 | Each **php** mapping may also specify the following options: 149 | * **static** (Default: true): 150 | If `true`, the static php driver will be used, which means each document needs to add: 151 | public static function loadMetadata(ClassMetadata $metadata) 152 | If `false`, the php driver will be used, each document needs to have a mapping file 153 | 154 | Each **class_map** mapping may also specify the following options: 155 | * **map**: 156 | Array key represents the entity class name, value represents the mapping for the entity class 157 | 158 | * **met adata_cache** (Default: setting specified by mongodbodm.default_cache): 159 | String or array describing metadata cache implementation. 160 | * **types** 161 | An array of custom types in the format of 'typeName' => 'Namespace\To\Type\Class' 162 | * **mongodbodm.dms.options**: 163 | Array of Document Manager configuration sets indexed by each Document Manager's 164 | name. Each value should look like **mongodbodm.dm.options**. 165 | 166 | Example configuration: 167 | 168 | ```php 169 | [ 173 | 'server' => 'mongodb://localhost:27017', 174 | 'options' => [ 175 | 'username' => 'root', 176 | 'password' => 'root', 177 | 'db' => 'admin' 178 | ] 179 | ], 180 | 'mongo2' => [ 181 | 'server' => 'mongodb://localhost:27018', 182 | 'options' => [ 183 | 'username' => 'root', 184 | 'password' => 'root', 185 | 'db' => 'admin' 186 | ] 187 | ] 188 | ]; 189 | ``` 190 | 191 | Example usage: 192 | 193 | ```php 194 | 290 | * Beau Simensen ([Doctrine ORM Service Provider][2]) 291 | 292 | 293 | [1]: https://github.com/dflydev 294 | [2]: https://github.com/dflydev/dflydev-doctrine-orm-service-provider 295 | [3]: https://github.com/dflydev/dflydev-psr0-resource-locator-service-provider 296 | [4]: https://packagist.org/packages/saxulum/saxulum-doctrine-mongodb-odm-provider 297 | 298 | [#silex-php]: irc://irc.freenode.net/#silex-php 299 | -------------------------------------------------------------------------------- /tests/Provider/DoctrineMongoDbOdmProviderTest.php: -------------------------------------------------------------------------------- 1 | getMockBuilder('Doctrine\Common\EventManager')->disableOriginalConstructor()->getMock(); 19 | $connection = $this->getMockBuilder('Doctrine\MongoDB\Connection')->disableOriginalConstructor()->getMock(); 20 | 21 | $connection 22 | ->expects($this->any()) 23 | ->method('getEventManager') 24 | ->will($this->returnValue($eventManager)); 25 | 26 | $container['mongodbs'] = new Container([ 27 | 'default' => $connection, 28 | ]); 29 | 30 | $container['mongodbs.event_manager'] = new Container([ 31 | 'default' => $eventManager, 32 | ]); 33 | 34 | return [$container, $connection, $eventManager]; 35 | } 36 | 37 | protected function createMockDefaultApp() 38 | { 39 | list($container, $connection, $eventManager) = $this->createMockDefaultAppAndDeps(); 40 | 41 | return $container; 42 | } 43 | 44 | /** 45 | * Test registration (test expected class for default implementations). 46 | */ 47 | public function testRegisterDefaultImplementations() 48 | { 49 | $container = $this->createMockDefaultApp(); 50 | 51 | $doctrineOrmServiceProvider = new DoctrineMongoDbOdmProvider(); 52 | $doctrineOrmServiceProvider->register($container); 53 | 54 | $this->assertEquals($container['mongodbodm.dm'], $container['mongodbodm.dms']['default']); 55 | $this->assertInstanceOf('Doctrine\Common\Cache\ArrayCache', $container['mongodbodm.dm.config']->getMetadataCacheImpl()); 56 | $this->assertInstanceOf('Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain', $container['mongodbodm.dm.config']->getMetadataDriverImpl()); 57 | } 58 | 59 | /** 60 | * Test registration (test equality for defined implementations). 61 | */ 62 | public function testRegisterDefinedImplementations() 63 | { 64 | $container = $this->createMockDefaultApp(); 65 | 66 | $metadataCache = $this->getMockBuilder('Doctrine\Common\Cache\ArrayCache') 67 | ->disableOriginalConstructor() 68 | ->getMock(); 69 | 70 | $mappingDriverChain = $this->getMockBuilder('Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain') 71 | ->disableOriginalConstructor() 72 | ->getMock(); 73 | 74 | $container['mongodbodm.cache.instances.default.metadata'] = $metadataCache; 75 | 76 | $container['mongodbodm.mapping_driver_chain.instances.default'] = $mappingDriverChain; 77 | 78 | $doctrineOrmServiceProvider = new DoctrineMongoDbOdmProvider(); 79 | $doctrineOrmServiceProvider->register($container); 80 | 81 | $this->assertEquals($container['mongodbodm.dm'], $container['mongodbodm.dms']['default']); 82 | $this->assertEquals($metadataCache, $container['mongodbodm.dm.config']->getMetadataCacheImpl()); 83 | $this->assertEquals($mappingDriverChain, $container['mongodbodm.dm.config']->getMetadataDriverImpl()); 84 | } 85 | 86 | /** 87 | * Test proxy configuration (defaults). 88 | */ 89 | public function testProxyConfigurationDefaults() 90 | { 91 | $container = $this->createMockDefaultApp(); 92 | 93 | $doctrineOrmServiceProvider = new DoctrineMongoDbOdmProvider(); 94 | $doctrineOrmServiceProvider->register($container); 95 | 96 | $this->assertContains('/../../cache/doctrine/proxies', $container['mongodbodm.dm.config']->getProxyDir()); 97 | $this->assertEquals('DoctrineProxy', $container['mongodbodm.dm.config']->getProxyNamespace()); 98 | $this->assertTrue($container['mongodbodm.dm.config']->getAutoGenerateProxyClasses()); 99 | } 100 | 101 | /** 102 | * Test proxy configuration (defined). 103 | */ 104 | public function testProxyConfigurationDefined() 105 | { 106 | $container = $this->createMockDefaultApp(); 107 | 108 | $doctrineOrmServiceProvider = new DoctrineMongoDbOdmProvider(); 109 | $doctrineOrmServiceProvider->register($container); 110 | 111 | $container['mongodbodm.proxies_dir'] = '/path/to/proxies'; 112 | $container['mongodbodm.proxies_namespace'] = 'TestDoctrineMongoDbOdmProxiesNamespace'; 113 | $container['mongodbodm.auto_generate_proxies'] = false; 114 | 115 | $this->assertEquals('/path/to/proxies', $container['mongodbodm.dm.config']->getProxyDir()); 116 | $this->assertEquals('TestDoctrineMongoDbOdmProxiesNamespace', $container['mongodbodm.dm.config']->getProxyNamespace()); 117 | $this->assertFalse($container['mongodbodm.dm.config']->getAutoGenerateProxyClasses()); 118 | } 119 | 120 | /** 121 | * Test hydrator configuration (defaults). 122 | */ 123 | public function testHydratorConfigurationDefaults() 124 | { 125 | $container = $this->createMockDefaultApp(); 126 | 127 | $doctrineOrmServiceProvider = new DoctrineMongoDbOdmProvider(); 128 | $doctrineOrmServiceProvider->register($container); 129 | 130 | $this->assertContains('/../../cache/doctrine/hydrator', $container['mongodbodm.dm.config']->getHydratorDir()); 131 | $this->assertEquals('DoctrineHydrator', $container['mongodbodm.dm.config']->getHydratorNamespace()); 132 | $this->assertTrue($container['mongodbodm.dm.config']->getAutoGenerateHydratorClasses()); 133 | } 134 | 135 | /** 136 | * Test hydrator configuration (defined). 137 | */ 138 | public function testHydratorConfigurationDefined() 139 | { 140 | $container = $this->createMockDefaultApp(); 141 | 142 | $doctrineOrmServiceProvider = new DoctrineMongoDbOdmProvider(); 143 | $doctrineOrmServiceProvider->register($container); 144 | 145 | $container['mongodbodm.hydrator_dir'] = '/path/to/hydrators'; 146 | $container['mongodbodm.hydrator_namespace'] = 'TestDoctrineMongoDbOdmHydratorsNamespace'; 147 | $container['mongodbodm.auto_generate_hydrators'] = false; 148 | 149 | $this->assertEquals('/path/to/hydrators', $container['mongodbodm.dm.config']->getHydratorDir()); 150 | $this->assertEquals('TestDoctrineMongoDbOdmHydratorsNamespace', $container['mongodbodm.dm.config']->getHydratorNamespace()); 151 | $this->assertFalse($container['mongodbodm.dm.config']->getAutoGenerateHydratorClasses()); 152 | } 153 | 154 | /** 155 | * Test Driver Chain locator. 156 | */ 157 | public function testMappingDriverChainLocator() 158 | { 159 | $container = $this->createMockDefaultApp(); 160 | 161 | $doctrineOrmServiceProvider = new DoctrineMongoDbOdmProvider(); 162 | $doctrineOrmServiceProvider->register($container); 163 | 164 | $default = $container['mongodbodm.mapping_driver_chain.locator'](); 165 | $this->assertEquals($default, $container['mongodbodm.mapping_driver_chain.locator']('default')); 166 | $this->assertEquals($default, $container['mongodbodm.dm.config']->getMetadataDriverImpl()); 167 | } 168 | 169 | /** 170 | * Test adding a mapping driver (use default document manager). 171 | */ 172 | public function testAddMappingDriverDefault() 173 | { 174 | $container = $this->createMockDefaultApp(); 175 | 176 | $mappingDriver = $this->getMockBuilder('Doctrine\Common\Persistence\Mapping\Driver\MappingDriver')->disableOriginalConstructor()->getMock(); 177 | 178 | $mappingDriverChain = $this->getMockBuilder('Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain')->disableOriginalConstructor()->getMock(); 179 | $mappingDriverChain 180 | ->expects($this->once()) 181 | ->method('addDriver') 182 | ->with($mappingDriver, 'Test\Namespace'); 183 | 184 | $container['mongodbodm.mapping_driver_chain.instances.default'] = $mappingDriverChain; 185 | 186 | $doctrineOrmServiceProvider = new DoctrineMongoDbOdmProvider(); 187 | $doctrineOrmServiceProvider->register($container); 188 | 189 | $container['mongodbodm.add_mapping_driver']($mappingDriver, 'Test\Namespace'); 190 | } 191 | 192 | /** 193 | * Test adding a mapping driver (specify default document manager by name). 194 | */ 195 | public function testAddMappingDriverNamedEntityManager() 196 | { 197 | $container = $this->createMockDefaultApp(); 198 | 199 | $mappingDriver = $this->getMockBuilder('Doctrine\Common\Persistence\Mapping\Driver\MappingDriver')->disableOriginalConstructor()->getMock(); 200 | 201 | $mappingDriverChain = $this->getMockBuilder('Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain')->disableOriginalConstructor()->getMock(); 202 | $mappingDriverChain 203 | ->expects($this->once()) 204 | ->method('addDriver') 205 | ->with($mappingDriver, 'Test\Namespace'); 206 | 207 | $container['mongodbodm.mapping_driver_chain.instances.default'] = $mappingDriverChain; 208 | 209 | $doctrineOrmServiceProvider = new DoctrineMongoDbOdmProvider(); 210 | $doctrineOrmServiceProvider->register($container); 211 | 212 | $container['mongodbodm.add_mapping_driver']($mappingDriver, 'Test\Namespace'); 213 | } 214 | 215 | /** 216 | * Test mongodbodm.dm_name_from_param_key (). 217 | */ 218 | public function testNameFromParamKey() 219 | { 220 | $container = $this->createMockDefaultApp(); 221 | 222 | $container['my.baz'] = 'baz'; 223 | 224 | $doctrineOrmServiceProvider = new DoctrineMongoDbOdmProvider(); 225 | $doctrineOrmServiceProvider->register($container); 226 | 227 | $container['mongodbodm.dms.default'] = 'foo'; 228 | 229 | $this->assertEquals('foo', $container['mongodbodm.dms.default']); 230 | $this->assertEquals('foo', $container['mongodbodm.dm_name_from_param_key']('my.bar')); 231 | $this->assertEquals('baz', $container['mongodbodm.dm_name_from_param_key']('my.baz')); 232 | } 233 | 234 | /** 235 | * Test specifying an invalid mapping configuration (not an array of arrays). 236 | * 237 | * @expectedException \InvalidArgumentException 238 | * @expectedExceptionMessage The 'mongodbodm.dm.options' option 'mappings' should be an array of arrays. 239 | */ 240 | public function testInvalidMappingAsOption() 241 | { 242 | $container = $this->createMockDefaultApp(); 243 | 244 | $doctrineOrmServiceProvider = new DoctrineMongoDbOdmProvider(); 245 | $doctrineOrmServiceProvider->register($container); 246 | 247 | $container['mongodbodm.dm.options'] = [ 248 | 'mappings' => [ 249 | 'type' => 'annotation', 250 | 'namespace' => 'Foo\Entities', 251 | 'path' => __DIR__.'/src/Foo/Entities', 252 | ], 253 | ]; 254 | 255 | $container['mongodbodm.dms.config']; 256 | } 257 | 258 | /** 259 | * Test if namespace alias can be set through the mapping options. 260 | */ 261 | public function testMappingAlias() 262 | { 263 | $container = $this->createMockDefaultApp(); 264 | 265 | $doctrineOrmServiceProvider = new DoctrineMongoDbOdmProvider(); 266 | $doctrineOrmServiceProvider->register($container); 267 | 268 | $alias = 'Foo'; 269 | $namespace = 'Foo\Entities'; 270 | 271 | $container['mongodbodm.dm.options'] = [ 272 | 'mappings' => [ 273 | [ 274 | 'type' => 'annotation', 275 | 'namespace' => $namespace, 276 | 'path' => __DIR__.'/src/Foo/Entities', 277 | 'alias' => $alias, 278 | ], 279 | ], 280 | ]; 281 | 282 | $this->assertEquals($namespace, $container['mongodbodm.dm.config']->getDocumentNameSpace($alias)); 283 | } 284 | 285 | public function testAnnotationMapping() 286 | { 287 | if (!extension_loaded('mongodb')) { 288 | $this->markTestSkipped('mongodb is not available'); 289 | } 290 | 291 | $proxyPath = $this->getCacheDir().'/doctrine/proxies'; 292 | $hydratorPath = $this->getCacheDir().'/doctrine/hydrator'; 293 | 294 | @mkdir($proxyPath, 0777, true); 295 | @mkdir($hydratorPath, 0777, true); 296 | 297 | $app = new Container(); 298 | 299 | $app->register(new DoctrineMongoDbProvider(), [ 300 | 'mongodb.options' => [ 301 | 'server' => 'mongodb://localhost:27017', 302 | ], 303 | ]); 304 | 305 | $app->register(new DoctrineMongoDbOdmProvider(), [ 306 | 'mongodbodm.proxies_dir' => $proxyPath, 307 | 'mongodbodm.hydrator_dir' => $hydratorPath, 308 | 'mongodbodm.dm.options' => [ 309 | 'database' => 'test', 310 | 'mappings' => [ 311 | [ 312 | 'type' => 'annotation', 313 | 'namespace' => 'Saxulum\\Tests\\DoctrineMongoDbOdm\\Document', 314 | 'path' => __DIR__.'../../Document', 315 | 'use_simple_annotation_reader' => false, 316 | ], 317 | ], 318 | ], 319 | ]); 320 | 321 | $title = 'title'; 322 | $body = 'body'; 323 | 324 | $page = new Page(); 325 | $page->setTitle($title); 326 | $page->setBody($body); 327 | 328 | $app['mongodbodm.dm']->persist($page); 329 | $app['mongodbodm.dm']->flush(); 330 | 331 | $repository = $app['mongodbodm.dm'] 332 | ->getRepository('Saxulum\\Tests\\DoctrineMongoDbOdm\\Document\\Page') 333 | ; 334 | /** @var DocumentRepository $repository */ 335 | $pageFromDb = $repository->findOneBy([], ['id' => 'DESC']); 336 | /* @var Page $pageFromDb */ 337 | 338 | $this->assertEquals($title, $pageFromDb->getTitle()); 339 | $this->assertEquals($body, $pageFromDb->getBody()); 340 | } 341 | 342 | /** 343 | * @return string 344 | */ 345 | protected function getCacheDir() 346 | { 347 | $cacheDir = __DIR__.'/../../cache'; 348 | 349 | if (!is_dir($cacheDir)) { 350 | mkdir($cacheDir, 0777, true); 351 | } 352 | 353 | return $cacheDir; 354 | } 355 | } 356 | -------------------------------------------------------------------------------- /src/Provider/DoctrineMongoDbOdmProvider.php: -------------------------------------------------------------------------------- 1 | getMongodbOdmDefaults($container) as $key => $value) { 39 | if (!isset($container[$key])) { 40 | $container[$key] = $value; 41 | } 42 | } 43 | 44 | $container['mongodbodm.dm.default_options'] = [ 45 | 'connection' => 'default', 46 | 'database' => null, 47 | 'mappings' => [], 48 | 'types' => [], 49 | ]; 50 | 51 | $container['mongodbodm.dms.options.initializer'] = $container->protect(function () use ($container) { 52 | static $initialized = false; 53 | 54 | if ($initialized) { 55 | return; 56 | } 57 | 58 | $initialized = true; 59 | 60 | if (!isset($container['mongodbodm.dms.options'])) { 61 | $container['mongodbodm.dms.options'] = ['default' => isset($container['mongodbodm.dm.options']) ? $container['mongodbodm.dm.options'] : []]; 62 | } 63 | 64 | $tmp = $container['mongodbodm.dms.options']; 65 | foreach ($tmp as $name => &$options) { 66 | $options = array_replace($container['mongodbodm.dm.default_options'], $options); 67 | 68 | if (!isset($container['mongodbodm.dms.default'])) { 69 | $container['mongodbodm.dms.default'] = $name; 70 | } 71 | } 72 | $container['mongodbodm.dms.options'] = $tmp; 73 | }); 74 | 75 | $container['mongodbodm.dm_name_from_param_key'] = $container->protect(function ($paramKey) use ($container) { 76 | $container['mongodbodm.dms.options.initializer'](); 77 | 78 | if (isset($container[$paramKey])) { 79 | return $container[$paramKey]; 80 | } 81 | 82 | return $container['mongodbodm.dms.default']; 83 | }); 84 | 85 | $container['mongodbodm.dms'] = function () use ($container) { 86 | $container['mongodbodm.dms.options.initializer'](); 87 | 88 | $dms = new Container(); 89 | foreach ($container['mongodbodm.dms.options'] as $name => $options) { 90 | if ($container['mongodbodm.dms.default'] === $name) { 91 | // we use shortcuts here in case the default has been overridden 92 | $config = $container['mongodbodm.dm.config']; 93 | } else { 94 | $config = $container['mongodbodm.dms.config'][$name]; 95 | } 96 | 97 | if (isset($options['database'])) { 98 | $config->setDefaultDB($options['database']); 99 | } 100 | 101 | $dms[$name] = function () use ($container, $options, $config) { 102 | return DocumentManager::create( 103 | $container['mongodbs'][$options['connection']], 104 | $config, 105 | $container['mongodbs.event_manager'][$options['connection']] 106 | ); 107 | }; 108 | } 109 | 110 | return $dms; 111 | }; 112 | 113 | $container['mongodbodm.dms.config'] = function () use ($container) { 114 | $container['mongodbodm.dms.options.initializer'](); 115 | 116 | $configs = new Container(); 117 | foreach ($container['mongodbodm.dms.options'] as $name => $options) { 118 | $config = new Configuration(); 119 | 120 | $container['mongodbodm.cache.configurer']($name, $config, $options); 121 | 122 | $config->setProxyDir($container['mongodbodm.proxies_dir']); 123 | $config->setProxyNamespace($container['mongodbodm.proxies_namespace']); 124 | $config->setAutoGenerateProxyClasses($container['mongodbodm.auto_generate_proxies']); 125 | 126 | $config->setHydratorDir($container['mongodbodm.hydrator_dir']); 127 | $config->setHydratorNamespace($container['mongodbodm.hydrator_namespace']); 128 | $config->setAutoGenerateHydratorClasses($container['mongodbodm.auto_generate_hydrators']); 129 | 130 | $config->setClassMetadataFactoryName($container['mongodbodm.class_metadata_factory_name']); 131 | $config->setDefaultRepositoryClassName($container['mongodbodm.default_repository_class']); 132 | 133 | $config->setRepositoryFactory($container['mongodbodm.repository_factory']); 134 | 135 | /** @var MappingDriverChain $chain */ 136 | $chain = $container['mongodbodm.mapping_driver_chain.locator']($name); 137 | foreach ((array) $options['mappings'] as $entity) { 138 | if (!is_array($entity)) { 139 | throw new \InvalidArgumentException( 140 | "The 'mongodbodm.dm.options' option 'mappings' should be an array of arrays." 141 | ); 142 | } 143 | 144 | if (isset($entity['alias'])) { 145 | $config->addDocumentNamespace($entity['alias'], $entity['namespace']); 146 | } 147 | 148 | switch ($entity['type']) { 149 | case 'annotation': 150 | $useSimpleAnnotationReader = 151 | isset($entity['use_simple_annotation_reader']) 152 | ? $entity['use_simple_annotation_reader'] 153 | : true; 154 | $driver = $config->newDefaultAnnotationDriver((array) $entity['path'], $useSimpleAnnotationReader); 155 | $chain->addDriver($driver, $entity['namespace']); 156 | break; 157 | case 'yml': 158 | $driver = new YamlDriver($entity['path']); 159 | $chain->addDriver($driver, $entity['namespace']); 160 | break; 161 | case 'simple_yml': 162 | $driver = new SimplifiedYamlDriver([$entity['path'] => $entity['namespace']]); 163 | $chain->addDriver($driver, $entity['namespace']); 164 | break; 165 | case 'xml': 166 | $driver = new XmlDriver($entity['path']); 167 | $chain->addDriver($driver, $entity['namespace']); 168 | break; 169 | case 'simple_xml': 170 | $driver = new SimplifiedXmlDriver([$entity['path'] => $entity['namespace']]); 171 | $chain->addDriver($driver, $entity['namespace']); 172 | break; 173 | case 'php': 174 | if (!isset($entity['static']) || $entity['static']) { 175 | $driver = new StaticPHPDriver($entity['path']); 176 | } else { 177 | $driver = new PHPDriver($entity['path']); 178 | } 179 | $chain->addDriver($driver, $entity['namespace']); 180 | break; 181 | case 'class_map': 182 | $driver = new ClassMapDriver($entity['map']); 183 | $chain->addDriver($driver, $entity['namespace']); 184 | break; 185 | default: 186 | throw new \InvalidArgumentException(sprintf('"%s" is not a recognized driver', $entity['type'])); 187 | break; 188 | } 189 | } 190 | $config->setMetadataDriverImpl($chain); 191 | 192 | foreach ((array) $options['types'] as $typeName => $typeClass) { 193 | if (Type::hasType($typeName)) { 194 | Type::overrideType($typeName, $typeClass); 195 | } else { 196 | Type::addType($typeName, $typeClass); 197 | } 198 | } 199 | 200 | $configs[$name] = $config; 201 | } 202 | 203 | return $configs; 204 | }; 205 | 206 | $container['mongodbodm.cache.configurer'] = $container->protect(function ($name, Configuration $config, $options) use ($container) { 207 | $config->setMetadataCacheImpl($container['mongodbodm.cache.locator']($name, 'metadata', $options)); 208 | }); 209 | 210 | $container['mongodbodm.cache.locator'] = $container->protect(function ($name, $cacheName, $options) use ($container) { 211 | $cacheNameKey = $cacheName.'_cache'; 212 | 213 | if (!isset($options[$cacheNameKey])) { 214 | $options[$cacheNameKey] = $container['mongodbodm.default_cache']; 215 | } 216 | 217 | if (isset($options[$cacheNameKey]) && !is_array($options[$cacheNameKey])) { 218 | $options[$cacheNameKey] = [ 219 | 'driver' => $options[$cacheNameKey], 220 | ]; 221 | } 222 | 223 | if (!isset($options[$cacheNameKey]['driver'])) { 224 | throw new \RuntimeException("No driver specified for '$cacheName'"); 225 | } 226 | 227 | $driver = $options[$cacheNameKey]['driver']; 228 | 229 | $cacheInstanceKey = 'mongodbodm.cache.instances.'.$name.'.'.$cacheName; 230 | if (isset($container[$cacheInstanceKey])) { 231 | return $container[$cacheInstanceKey]; 232 | } 233 | 234 | $cache = $container['mongodbodm.cache.factory']($driver, $options[$cacheNameKey]); 235 | 236 | if (isset($options['cache_namespace']) && $cache instanceof CacheProvider) { 237 | $cache->setNamespace($options['cache_namespace']); 238 | } 239 | 240 | return $container[$cacheInstanceKey] = $cache; 241 | }); 242 | 243 | $container['mongodbodm.cache.factory.backing_memcache'] = $container->protect(function () { 244 | return new \Memcache(); 245 | }); 246 | 247 | $container['mongodbodm.cache.factory.memcache'] = $container->protect(function ($cacheOptions) use ($container) { 248 | if (empty($cacheOptions['host']) || empty($cacheOptions['port'])) { 249 | throw new \RuntimeException('Host and port options need to be specified for memcache cache'); 250 | } 251 | 252 | /** @var \Memcache $memcache */ 253 | $memcache = $container['mongodbodm.cache.factory.backing_memcache'](); 254 | $memcache->connect($cacheOptions['host'], $cacheOptions['port']); 255 | 256 | $cache = new MemcacheCache(); 257 | $cache->setMemcache($memcache); 258 | 259 | return $cache; 260 | }); 261 | 262 | $container['mongodbodm.cache.factory.backing_memcached'] = $container->protect(function () { 263 | return new \Memcached(); 264 | }); 265 | 266 | $container['mongodbodm.cache.factory.memcached'] = $container->protect(function ($cacheOptions) use ($container) { 267 | if (empty($cacheOptions['host']) || empty($cacheOptions['port'])) { 268 | throw new \RuntimeException('Host and port options need to be specified for memcached cache'); 269 | } 270 | 271 | /** @var \Memcached $memcached */ 272 | $memcached = $container['mongodbodm.cache.factory.backing_memcached'](); 273 | $memcached->addServer($cacheOptions['host'], $cacheOptions['port']); 274 | 275 | $cache = new MemcachedCache(); 276 | $cache->setMemcached($memcached); 277 | 278 | return $cache; 279 | }); 280 | 281 | $container['mongodbodm.cache.factory.backing_redis'] = $container->protect(function () { 282 | return new \Redis(); 283 | }); 284 | 285 | $container['mongodbodm.cache.factory.redis'] = $container->protect(function ($cacheOptions) use ($container) { 286 | if (empty($cacheOptions['host']) || empty($cacheOptions['port'])) { 287 | throw new \RuntimeException('Host and port options need to be specified for redis cache'); 288 | } 289 | 290 | /** @var \Redis $redis */ 291 | $redis = $container['mongodbodm.cache.factory.backing_redis'](); 292 | $redis->connect($cacheOptions['host'], $cacheOptions['port']); 293 | 294 | if (isset($cacheOptions['password'])) { 295 | $redis->auth($cacheOptions['password']); 296 | } 297 | 298 | $cache = new RedisCache(); 299 | $cache->setRedis($redis); 300 | 301 | return $cache; 302 | }); 303 | 304 | $container['mongodbodm.cache.factory.array'] = $container->protect(function () { 305 | return new ArrayCache(); 306 | }); 307 | 308 | $container['mongodbodm.cache.factory.apc'] = $container->protect(function () { 309 | return new ApcCache(); 310 | }); 311 | 312 | $container['mongodbodm.cache.factory.apcu'] = $container->protect(function () { 313 | return new ApcuCache(); 314 | }); 315 | 316 | $container['mongodbodm.cache.factory.xcache'] = $container->protect(function () { 317 | return new XcacheCache(); 318 | }); 319 | 320 | $container['mongodbodm.cache.factory.filesystem'] = $container->protect(function ($cacheOptions) { 321 | if (empty($cacheOptions['path'])) { 322 | throw new \RuntimeException('FilesystemCache path not defined'); 323 | } 324 | 325 | $cacheOptions += [ 326 | 'extension' => FilesystemCache::EXTENSION, 327 | 'umask' => 0002, 328 | ]; 329 | 330 | return new FilesystemCache($cacheOptions['path'], $cacheOptions['extension'], $cacheOptions['umask']); 331 | }); 332 | 333 | $container['mongodbodm.cache.factory.couchbase'] = $container->protect(function ($cacheOptions) { 334 | if (empty($cacheOptions['host'])) { 335 | $cacheOptions['host'] = '127.0.0.1'; 336 | } 337 | 338 | if (empty($cacheOptions['bucket'])) { 339 | $cacheOptions['bucket'] = 'default'; 340 | } 341 | 342 | $couchbase = new \Couchbase( 343 | $cacheOptions['host'], 344 | $cacheOptions['user'], 345 | $cacheOptions['password'], 346 | $cacheOptions['bucket'] 347 | ); 348 | 349 | $cache = new CouchbaseCache(); 350 | $cache->setCouchbase($couchbase); 351 | 352 | return $cache; 353 | }); 354 | 355 | $container['mongodbodm.cache.factory'] = $container->protect(function ($driver, $cacheOptions) use ($container) { 356 | switch ($driver) { 357 | case 'array': 358 | return $container['mongodbodm.cache.factory.array'](); 359 | case 'apc': 360 | return $container['mongodbodm.cache.factory.apc'](); 361 | case 'apcu': 362 | return $container['mongodbodm.cache.factory.apcu'](); 363 | case 'xcache': 364 | return $container['mongodbodm.cache.factory.xcache'](); 365 | case 'memcache': 366 | return $container['mongodbodm.cache.factory.memcache']($cacheOptions); 367 | case 'memcached': 368 | return $container['mongodbodm.cache.factory.memcached']($cacheOptions); 369 | case 'filesystem': 370 | return $container['mongodbodm.cache.factory.filesystem']($cacheOptions); 371 | case 'redis': 372 | return $container['mongodbodm.cache.factory.redis']($cacheOptions); 373 | case 'couchbase': 374 | return $container['mongodbodm.cache.factory.couchbase']($cacheOptions); 375 | default: 376 | throw new \RuntimeException("Unsupported cache type '$driver' specified"); 377 | } 378 | }); 379 | 380 | $container['mongodbodm.mapping_driver_chain.locator'] = $container->protect(function ($name = null) use ($container) { 381 | $container['mongodbodm.dms.options.initializer'](); 382 | 383 | if (null === $name) { 384 | $name = $container['mongodbodm.dms.default']; 385 | } 386 | 387 | $cacheInstanceKey = 'mongodbodm.mapping_driver_chain.instances.'.$name; 388 | if (isset($container[$cacheInstanceKey])) { 389 | return $container[$cacheInstanceKey]; 390 | } 391 | 392 | return $container[$cacheInstanceKey] = $container['mongodbodm.mapping_driver_chain.factory']($name); 393 | }); 394 | 395 | $container['mongodbodm.mapping_driver_chain.factory'] = $container->protect(function ($name) use ($container) { 396 | return new MappingDriverChain(); 397 | }); 398 | 399 | $container['mongodbodm.add_mapping_driver'] = $container->protect(function (MappingDriver $mappingDriver, $namespace, $name = null) use ($container) { 400 | $container['mongodbodm.dms.options.initializer'](); 401 | 402 | if (null === $name) { 403 | $name = $container['mongodbodm.dms.default']; 404 | } 405 | 406 | /** @var MappingDriverChain $driverChain */ 407 | $driverChain = $container['mongodbodm.mapping_driver_chain.locator']($name); 408 | $driverChain->addDriver($mappingDriver, $namespace); 409 | }); 410 | 411 | $container['mongodbodm.repository_factory'] = function ($container) { 412 | return new DefaultRepositoryFactory(); 413 | }; 414 | 415 | $container['mongodbodm.dm'] = function ($container) { 416 | $dms = $container['mongodbodm.dms']; 417 | 418 | return $dms[$container['mongodbodm.dms.default']]; 419 | }; 420 | 421 | $container['mongodbodm.dm.config'] = function ($container) { 422 | $configs = $container['mongodbodm.dms.config']; 423 | 424 | return $configs[$container['mongodbodm.dms.default']]; 425 | }; 426 | } 427 | 428 | /** 429 | * @param Container $container 430 | * 431 | * @return array 432 | */ 433 | protected function getMongodbOdmDefaults(Container $container) 434 | { 435 | return [ 436 | 'mongodbodm.proxies_dir' => __DIR__.'/../../cache/doctrine/proxies', 437 | 'mongodbodm.proxies_namespace' => 'DoctrineProxy', 438 | 'mongodbodm.auto_generate_proxies' => true, 439 | 'mongodbodm.default_cache' => [ 440 | 'driver' => 'array', 441 | ], 442 | 'mongodbodm.hydrator_dir' => __DIR__.'/../../cache/doctrine/hydrator', 443 | 'mongodbodm.hydrator_namespace' => 'DoctrineHydrator', 444 | 'mongodbodm.auto_generate_hydrators' => true, 445 | 'mongodbodm.class_metadata_factory_name' => 'Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactory', 446 | 'mongodbodm.default_repository_class' => 'Doctrine\ODM\MongoDB\DocumentRepository', 447 | ]; 448 | } 449 | } 450 | --------------------------------------------------------------------------------