Skip to content

Commit

Permalink
Merge pull request #1871 from alcaeus/enforce-typemap
Browse files Browse the repository at this point in the history
Enforce typemap
  • Loading branch information
alcaeus authored Oct 5, 2018
2 parents 7db4b59 + b62de6d commit da5074b
Show file tree
Hide file tree
Showing 9 changed files with 80 additions and 30 deletions.
2 changes: 1 addition & 1 deletion docs/en/cookbook/resolve-target-document-listener.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
--------------
Expand Down
2 changes: 1 addition & 1 deletion docs/en/reference/eager-cursors.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion docs/en/reference/events.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
43 changes: 25 additions & 18 deletions docs/en/reference/introduction.rst
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,6 @@ we'll use:
<?php
use Doctrine\MongoDB\Connection;
use Doctrine\ODM\MongoDB\Configuration;
use Doctrine\ODM\MongoDB\DocumentManager;
use Doctrine\ODM\MongoDB\Mapping\Driver\AnnotationDriver;
Expand Down Expand Up @@ -379,15 +378,14 @@ the autoloader like so:
Ultimately, our application will utilize ODM through its ``DocumentManager``
class. Before we can instantiate a ``DocumentManager``, we need to construct the
``Connection`` and ``Configuration`` objects required by its factory method:
``Configuration`` object required by its factory method:

.. code-block:: php
<?php
// ...
$connection = new Connection();
$config = new Configuration();
Next, we'll specify some essential configuration options. The following assumes
Expand Down Expand Up @@ -434,7 +432,7 @@ At this point, we have everything necessary to construct a ``DocumentManager``:
// ...
$dm = DocumentManager::create($connection, $config);
$dm = DocumentManager::create(null, $config);
The final ``bootstrap.php`` file should look like this:

Expand All @@ -443,7 +441,6 @@ The final ``bootstrap.php`` file should look like this:
<?php
use Doctrine\Common\Annotations\AnnotationRegistry;
use Doctrine\MongoDB\Connection;
use Doctrine\ODM\MongoDB\Configuration;
use Doctrine\ODM\MongoDB\DocumentManager;
use Doctrine\ODM\MongoDB\Mapping\Driver\AnnotationDriver;
Expand All @@ -457,8 +454,6 @@ The final ``bootstrap.php`` file should look like this:
AnnotationRegistry::registerLoader([$loader, 'loadClass']);
$connection = new Connection();
$config = new Configuration();
$config->setProxyDir(__DIR__ . '/Proxies');
$config->setProxyNamespace('Proxies');
Expand All @@ -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
<?php
use Doctrine\ODM\MongoDB\Configuration;
use Doctrine\ODM\MongoDB\DocumentManager;
use MongoDB\Client;
$client = new Client('mongodb://127.0.0.1', [], ['typeMap' => 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
Expand Down
3 changes: 1 addition & 2 deletions docs/en/tutorials/getting-started.rst
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,6 @@ instance. Read more about setting up the Doctrine MongoDB ODM in the
<?php
use Doctrine\Common\Annotations\AnnotationRegistry;
use Doctrine\MongoDB\Connection;
use Doctrine\ODM\MongoDB\Configuration;
use Doctrine\ODM\MongoDB\DocumentManager;
use Doctrine\ODM\MongoDB\Mapping\Driver\AnnotationDriver;
Expand All @@ -164,7 +163,7 @@ instance. Read more about setting up the Doctrine MongoDB ODM in the
$config->setHydratorNamespace('Hydrators');
$config->setMetadataDriverImpl(AnnotationDriver::create('/path/to/document/classes'));
$dm = DocumentManager::create(new Connection(), $config);
$dm = DocumentManager::create(null, $config);
Usage
-----
Expand Down
17 changes: 16 additions & 1 deletion lib/Doctrine/ODM/MongoDB/DocumentManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
*/
class DocumentManager implements ObjectManager
{
public const CLIENT_TYPEMAP = ['root' => 'array', 'document' => 'array'];

/**
* The Doctrine MongoDB connection instance.
*
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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);
}
}
}
}
5 changes: 5 additions & 0 deletions lib/Doctrine/ODM/MongoDB/MongoDBException.php
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
}
12 changes: 6 additions & 6 deletions lib/Doctrine/ODM/MongoDB/Persisters/DocumentPersister.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down Expand Up @@ -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']);
Expand Down
24 changes: 24 additions & 0 deletions tests/Doctrine/ODM/MongoDB/Tests/DocumentManagerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 */
Expand Down

0 comments on commit da5074b

Please sign in to comment.