diff --git a/docs/en/cookbook/resolve-target-document-listener.rst b/docs/en/cookbook/resolve-target-document-listener.rst index be4a77aad5..8d60d2eacc 100644 --- a/docs/en/cookbook/resolve-target-document-listener.rst +++ b/docs/en/cookbook/resolve-target-document-listener.rst @@ -129,7 +129,7 @@ you cannot be guaranteed that the targetDocument resolution will occur reliably: $evm->addEventListener(\Doctrine\ODM\MongoDB\Events::loadClassMetadata, $rtdl); // Create the document manager as you normally would - $dm = \Doctrine\ODM\MongoDB\DocumentManager::create($connectionOptions, $config, $evm); + $dm = \Doctrine\ODM\MongoDB\DocumentManager::create(null, $config, $evm); Final Thoughts -------------- diff --git a/docs/en/reference/eager-cursors.rst b/docs/en/reference/eager-cursors.rst index 256c828704..4a98e95dd2 100644 --- a/docs/en/reference/eager-cursors.rst +++ b/docs/en/reference/eager-cursors.rst @@ -25,7 +25,7 @@ Example: $qb = $dm->createQueryBuilder('User') ->eagerCursor(true); $query = $qb->getQuery(); - $users = $query->execute(); // returns instance of Doctrine\MongoDB\ODM\EagerCursor + $users = $query->execute(); At this point all data is loaded from the database and cursors to MongoDB have been closed but hydration of the data in to objects has not begun. Once diff --git a/docs/en/reference/events.rst b/docs/en/reference/events.rst index 79325d9f7b..dd5f690a8f 100644 --- a/docs/en/reference/events.rst +++ b/docs/en/reference/events.rst @@ -317,7 +317,7 @@ EventManager that is passed to the DocumentManager factory: $eventManager->addEventListener(array(Events::preUpdate), new MyEventListener()); $eventManager->addEventSubscriber(new MyEventSubscriber()); - $documentManager = DocumentManager::create($mongo, $config, $eventManager); + $documentManager = DocumentManager::create(null, $config, $eventManager); You can also retrieve the event manager instance after the DocumentManager was created: diff --git a/docs/en/reference/introduction.rst b/docs/en/reference/introduction.rst index 602b818379..ed3218e282 100644 --- a/docs/en/reference/introduction.rst +++ b/docs/en/reference/introduction.rst @@ -344,7 +344,6 @@ we'll use: setProxyDir(__DIR__ . '/Proxies'); $config->setProxyNamespace('Proxies'); @@ -467,24 +462,36 @@ The final ``bootstrap.php`` file should look like this: $config->setDefaultDB('doctrine_odm'); $config->setMetadataDriverImpl(AnnotationDriver::create(__DIR__ . '/Documents')); - $dm = DocumentManager::create($connection, $config); + $dm = DocumentManager::create(null, $config); That is it! Your ``DocumentManager`` instance is ready to be used! -Using PHP 7 ------------ +Providing a custom client +------------------------- -You can use Doctrine MongoDB ODM with PHP 7, but there are a few extra steps during -the installation. Since the legacy driver (referred to as ``ext-mongo``) is not -available on PHP 7, you will need the new driver (``ext-mongodb``) installed and -use a polyfill to provide the API of the legacy driver. +Passing ``null`` to the factory method as first argument tells the document +manager to create a new MongoDB client instance with the appropriate typemap. +If you want to pass custom options (e.g. SSL options, authentication options) to +the client, you'll have to create it yourself manually: -To do this, you have to require ``alcaeus/mongo-php-adapter`` before adding a composer -dependency to ODM. To do this, run the following command: +.. code-block:: php -.. code-block:: console + DocumentManager::CLIENT_TYPEMAP]); + $config = new Configuration(); + + // ... + + $dm = DocumentManager::create($client, $config); - $ composer config "platform.ext-mongo" "1.6.16" && composer require "alcaeus/mongo-php-adapter" +Please note the ``typeMap`` option. This is necessary so ODM can appropriately +handle the results. If you need the client elsewhere with a different typeMap, +please create separate clients for your application and ODM. .. _MongoDB: https://www.mongodb.com/ .. _10gen: http://www.10gen.com diff --git a/docs/en/tutorials/getting-started.rst b/docs/en/tutorials/getting-started.rst index ebf011801c..ea53e1b012 100755 --- a/docs/en/tutorials/getting-started.rst +++ b/docs/en/tutorials/getting-started.rst @@ -148,7 +148,6 @@ instance. Read more about setting up the Doctrine MongoDB ODM in the setHydratorNamespace('Hydrators'); $config->setMetadataDriverImpl(AnnotationDriver::create('/path/to/document/classes')); - $dm = DocumentManager::create(new Connection(), $config); + $dm = DocumentManager::create(null, $config); Usage ----- diff --git a/lib/Doctrine/ODM/MongoDB/DocumentManager.php b/lib/Doctrine/ODM/MongoDB/DocumentManager.php index daf73d00b6..e8d3c552f3 100644 --- a/lib/Doctrine/ODM/MongoDB/DocumentManager.php +++ b/lib/Doctrine/ODM/MongoDB/DocumentManager.php @@ -39,6 +39,8 @@ */ class DocumentManager implements ObjectManager { + public const CLIENT_TYPEMAP = ['root' => 'array', 'document' => 'array']; + /** * The Doctrine MongoDB connection instance. * @@ -145,7 +147,9 @@ protected function __construct(?Client $client = null, ?Configuration $config = { $this->config = $config ?: new Configuration(); $this->eventManager = $eventManager ?: new EventManager(); - $this->client = $client ?: new Client('mongodb://127.0.0.1', [], ['typeMap' => ['root' => 'array', 'document' => 'array']]); + $this->client = $client ?: new Client('mongodb://127.0.0.1', [], ['typeMap' => self::CLIENT_TYPEMAP]); + + $this->checkTypeMap(); $metadataFactoryClassName = $this->config->getClassMetadataFactoryName(); $this->metadataFactory = new $metadataFactoryClassName(); @@ -790,4 +794,15 @@ public function getFilterCollection() : FilterCollection return $this->filterCollection; } + + private function checkTypeMap() : void + { + $typeMap = $this->client->getTypeMap(); + + foreach (self::CLIENT_TYPEMAP as $part => $expectedType) { + if (! isset($typeMap[$part]) || $typeMap[$part] !== $expectedType) { + throw MongoDBException::invalidTypeMap($part, $expectedType); + } + } + } } diff --git a/lib/Doctrine/ODM/MongoDB/MongoDBException.php b/lib/Doctrine/ODM/MongoDB/MongoDBException.php index 43280e8256..bd1c36fca9 100644 --- a/lib/Doctrine/ODM/MongoDB/MongoDBException.php +++ b/lib/Doctrine/ODM/MongoDB/MongoDBException.php @@ -132,4 +132,9 @@ public static function cannotReadGridFSSourceFile(string $filename) : self { return new self(sprintf('Cannot open file "%s" for uploading to GridFS.', $filename)); } + + public static function invalidTypeMap(string $part, string $epectedType) : self + { + return new self(sprintf('Invalid typemap provided. Type "%s" is required for "%s".', $epectedType, $part)); + } } diff --git a/lib/Doctrine/ODM/MongoDB/Persisters/DocumentPersister.php b/lib/Doctrine/ODM/MongoDB/Persisters/DocumentPersister.php index d102c6da9e..9a54d5acf1 100644 --- a/lib/Doctrine/ODM/MongoDB/Persisters/DocumentPersister.php +++ b/lib/Doctrine/ODM/MongoDB/Persisters/DocumentPersister.php @@ -470,6 +470,10 @@ public function load($criteria, ?object $document = null, array $hints = [], int } } + if ($result === null) { + return null; + } + return $this->createDocument($result, $document, $hints); } @@ -580,18 +584,14 @@ public function unlock(object $document) : void /** * Creates or fills a single document object from an query result. * - * @param object $result The query result. + * @param array $result The query result. * @param object $document The document object to fill, if any. * @param array $hints Hints for document creation. * * @return object The filled and managed document object or NULL, if the query result is empty. */ - private function createDocument($result, ?object $document = null, array $hints = []) : ?object + private function createDocument(array $result, ?object $document = null, array $hints = []) : ?object { - if ($result === null) { - return null; - } - if ($document !== null) { $hints[Query::HINT_REFRESH] = true; $id = $this->class->getPHPIdentifierValue($result['_id']); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/DocumentManagerTest.php b/tests/Doctrine/ODM/MongoDB/Tests/DocumentManagerTest.php index 811d0ec9af..9a27c85255 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/DocumentManagerTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/DocumentManagerTest.php @@ -7,6 +7,7 @@ use Doctrine\Common\EventManager; use Doctrine\ODM\MongoDB\Aggregation\Builder as AggregationBuilder; use Doctrine\ODM\MongoDB\Configuration; +use Doctrine\ODM\MongoDB\DocumentManager; use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; use Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactory; @@ -224,6 +225,29 @@ public function testDifferentStoreAsDbReferences() $this->assertCount(1, $dbRef); $this->assertArrayHasKey('id', $dbRef); } + + /** + * @dataProvider dataInvalidTypeMap + */ + public function testThrowsExceptionOnInvalidTypeMap(string $expectedMessage, Client $client) : void + { + $this->expectException(MongoDBException::class); + $this->expectExceptionMessage($expectedMessage); + DocumentManager::create($client); + } + + public function dataInvalidTypeMap() : array + { + $noTypeMap = new Client(); + $invalidDocumentTypeMap = new Client('mongodb://127.0.0.1', [], ['typeMap' => ['root' => 'array', 'document' => stdClass::class]]); + $invalidRootTypeMap = new Client('mongodb://127.0.0.1', [], ['typeMap' => ['root' => stdClass::class, 'document' => 'array']]); + + return [ + 'No typeMap' => ['Invalid typemap provided. Type "array" is required for "root".', $noTypeMap], + 'Invalid document' => ['Invalid typemap provided. Type "array" is required for "document".', $invalidDocumentTypeMap], + 'Invalid root' => ['Invalid typemap provided. Type "array" is required for "root".', $invalidRootTypeMap], + ]; + } } /** @ODM\Document */