Completed
Pull Request — master (#1263)
by Andreas
10:33
created

DocumentManager   F

Complexity

Total Complexity 75

Size/Duplication

Total Lines 734
Duplicated Lines 7.49 %

Coupling/Cohesion

Components 3
Dependencies 18

Test Coverage

Coverage 92.79%

Importance

Changes 1
Bugs 0 Features 1
Metric Value
wmc 75
c 1
b 0
f 1
lcom 3
cbo 18
dl 55
loc 734
ccs 193
cts 208
cp 0.9279
rs 1.8462

37 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 34 5
A getProxyFactory() 0 4 1
A create() 0 4 1
A getEventManager() 0 4 1
A getConnection() 0 4 1
A getMetadataFactory() 0 4 1
A initializeObject() 0 4 1
A getUnitOfWork() 0 4 1
A getHydratorFactory() 0 4 1
A getSchemaManager() 0 4 1
A getClassMetadata() 0 4 1
A getDocumentDatabase() 0 16 4
A getDocumentDatabases() 0 4 1
B getDocumentCollection() 0 27 5
A getDocumentCollections() 0 4 1
A createQueryBuilder() 0 4 1
A createAggregationBuilder() 0 4 1
A persist() 8 8 2
A remove() 8 8 2
A refresh() 8 8 2
A detach() 0 7 2
A merge() 8 8 2
A lock() 0 7 2
A unlock() 0 7 2
A getRepository() 0 4 1
A flush() 0 8 4
A getReference() 0 15 2
A getPartialReference() 0 14 2
A find() 0 4 1
A clear() 0 4 1
A close() 0 5 1
A contains() 0 9 4
A getConfiguration() 0 4 1
A errorIfClosed() 0 6 2
A isOpen() 0 4 1
A getFilterCollection() 0 8 2
C createDBRef() 23 62 11

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like DocumentManager often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use DocumentManager, and based on these observations, apply Extract Interface, too.

1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\ODM\MongoDB;
21
22
use Doctrine\Common\EventManager;
23
use Doctrine\Common\Persistence\ObjectManager;
24
use Doctrine\MongoDB\Connection;
25
use Doctrine\ODM\MongoDB\Mapping\ClassMetadataInfo;
26
use Doctrine\ODM\MongoDB\Mapping\MappingException;
27
use Doctrine\ODM\MongoDB\Hydrator\HydratorFactory;
28
use Doctrine\ODM\MongoDB\Proxy\ProxyFactory;
29
use Doctrine\ODM\MongoDB\Query\FilterCollection;
30
use Doctrine\ODM\MongoDB\Repository\RepositoryFactory;
31
32
/**
33
 * The DocumentManager class is the central access point for managing the
34
 * persistence of documents.
35
 *
36
 *     <?php
37
 *
38
 *     $config = new Configuration();
39
 *     $dm = DocumentManager::create(new Connection(), $config);
40
 *
41
 * @since       1.0
42
 * @author      Jonathan H. Wage <[email protected]>
43
 * @author      Roman Borschel <[email protected]>
44
 */
45
class DocumentManager implements ObjectManager
46
{
47
    /**
48
     * The Doctrine MongoDB connection instance.
49
     *
50
     * @var \Doctrine\MongoDB\Connection
51
     */
52
    private $connection;
53
54
    /**
55
     * The used Configuration.
56
     *
57
     * @var \Doctrine\ODM\MongoDB\Configuration
58
     */
59
    private $config;
60
61
    /**
62
     * The metadata factory, used to retrieve the ODM metadata of document classes.
63
     *
64
     * @var \Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactory
65
     */
66
    private $metadataFactory;
67
68
    /**
69
     * The UnitOfWork used to coordinate object-level transactions.
70
     *
71
     * @var UnitOfWork
72
     */
73
    private $unitOfWork;
74
75
    /**
76
     * The event manager that is the central point of the event system.
77
     *
78
     * @var \Doctrine\Common\EventManager
79
     */
80
    private $eventManager;
81
82
    /**
83
     * The Hydrator factory instance.
84
     *
85
     * @var HydratorFactory
86
     */
87
    private $hydratorFactory;
88
89
    /**
90
     * The Proxy factory instance.
91
     *
92
     * @var ProxyFactory
93
     */
94
    private $proxyFactory;
95
96
    /**
97
     * The repository factory used to create dynamic repositories.
98
     *
99
     * @var RepositoryFactory
100
     */
101
    private $repositoryFactory;
102
103
    /**
104
     * SchemaManager instance
105
     *
106
     * @var SchemaManager
107
     */
108
    private $schemaManager;
109
110
    /**
111
     * Array of cached document database instances that are lazily loaded.
112
     *
113
     * @var array
114
     */
115
    private $documentDatabases = array();
116
117
    /**
118
     * Array of cached document collection instances that are lazily loaded.
119
     *
120
     * @var array
121
     */
122
    private $documentCollections = array();
123
124
    /**
125
     * Whether the DocumentManager is closed or not.
126
     *
127
     * @var bool
128
     */
129
    private $closed = false;
130
131
    /**
132
     * Collection of query filters.
133
     *
134
     * @var \Doctrine\ODM\MongoDB\Query\FilterCollection
135
     */
136
    private $filterCollection;
137
138
    /**
139
     * Creates a new Document that operates on the given Mongo connection
140
     * and uses the given Configuration.
141
     *
142
     * @param \Doctrine\MongoDB\Connection|null $conn
143
     * @param Configuration|null $config
144
     * @param \Doctrine\Common\EventManager|null $eventManager
145
     */
146 933
    protected function __construct(Connection $conn = null, Configuration $config = null, EventManager $eventManager = null)
147
    {
148 933
        $this->config = $config ?: new Configuration();
149 933
        $this->eventManager = $eventManager ?: new EventManager();
150 933
        $this->connection = $conn ?: new Connection(null, array(), $this->config, $this->eventManager);
151
152 933
        $metadataFactoryClassName = $this->config->getClassMetadataFactoryName();
153 933
        $this->metadataFactory = new $metadataFactoryClassName();
154 933
        $this->metadataFactory->setDocumentManager($this);
155 933
        $this->metadataFactory->setConfiguration($this->config);
156 933
        if ($cacheDriver = $this->config->getMetadataCacheImpl()) {
157 1
            $this->metadataFactory->setCacheDriver($cacheDriver);
158
        }
159
160 933
        $hydratorDir = $this->config->getHydratorDir();
161 933
        $hydratorNs = $this->config->getHydratorNamespace();
162 933
        $this->hydratorFactory = new HydratorFactory(
163 933
            $this,
164 933
            $this->eventManager,
165 933
            $hydratorDir,
166 933
            $hydratorNs,
167 933
            $this->config->getAutoGenerateHydratorClasses()
0 ignored issues
show
Bug introduced by
It seems like $this->config->getAutoGenerateHydratorClasses() targeting Doctrine\ODM\MongoDB\Con...nerateHydratorClasses() can also be of type boolean; however, Doctrine\ODM\MongoDB\Hyd...rFactory::__construct() does only seem to accept integer, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
168 933
        );
169
170 933
        $this->unitOfWork = new UnitOfWork($this, $this->eventManager, $this->hydratorFactory);
171 933
        $this->hydratorFactory->setUnitOfWork($this->unitOfWork);
172 933
        $this->schemaManager = new SchemaManager($this, $this->metadataFactory);
173 933
        $this->proxyFactory = new ProxyFactory($this,
174 933
            $this->config->getProxyDir(),
175 933
            $this->config->getProxyNamespace(),
176 933
            $this->config->getAutoGenerateProxyClasses()
0 ignored issues
show
Bug introduced by
It seems like $this->config->getAutoGenerateProxyClasses() targeting Doctrine\ODM\MongoDB\Con...oGenerateProxyClasses() can also be of type boolean; however, Doctrine\ODM\MongoDB\Pro...yFactory::__construct() does only seem to accept integer, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
177 933
        );
178 933
        $this->repositoryFactory = $this->config->getRepositoryFactory();
179 933
    }
180
181
    /**
182
     * Gets the proxy factory used by the DocumentManager to create document proxies.
183
     *
184
     * @return ProxyFactory
185
     */
186 1
    public function getProxyFactory()
187
    {
188 1
        return $this->proxyFactory;
189
    }
190
191
    /**
192
     * Creates a new Document that operates on the given Mongo connection
193
     * and uses the given Configuration.
194
     *
195
     * @static
196
     * @param \Doctrine\MongoDB\Connection|null $conn
197
     * @param Configuration|null $config
198
     * @param \Doctrine\Common\EventManager|null $eventManager
199
     * @return DocumentManager
200
     */
201 933
    public static function create(Connection $conn = null, Configuration $config = null, EventManager $eventManager = null)
202
    {
203 933
        return new static($conn, $config, $eventManager);
204
    }
205
206
    /**
207
     * Gets the EventManager used by the DocumentManager.
208
     *
209
     * @return \Doctrine\Common\EventManager
210
     */
211 765
    public function getEventManager()
212
    {
213 765
        return $this->eventManager;
214
    }
215
216
    /**
217
     * Gets the PHP Mongo instance that this DocumentManager wraps.
218
     *
219
     * @return \Doctrine\MongoDB\Connection
220
     */
221 933
    public function getConnection()
222
    {
223 933
        return $this->connection;
224
    }
225
226
    /**
227
     * Gets the metadata factory used to gather the metadata of classes.
228
     *
229
     * @return \Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactory
230
     */
231 933
    public function getMetadataFactory()
232
    {
233 933
        return $this->metadataFactory;
234
    }
235
236
    /**
237
     * Helper method to initialize a lazy loading proxy or persistent collection.
238
     *
239
     * This method is a no-op for other objects.
240
     *
241
     * @param object $obj
242
     */
243
    public function initializeObject($obj)
244
    {
245
        $this->unitOfWork->initializeObject($obj);
246
    }
247
248
    /**
249
     * Gets the UnitOfWork used by the DocumentManager to coordinate operations.
250
     *
251
     * @return UnitOfWork
252
     */
253 939
    public function getUnitOfWork()
254
    {
255 939
        return $this->unitOfWork;
256
    }
257
258
    /**
259
     * Gets the Hydrator factory used by the DocumentManager to generate and get hydrators
260
     * for each type of document.
261
     *
262
     * @return \Doctrine\ODM\MongoDB\Hydrator\HydratorInterface
263
     */
264 66
    public function getHydratorFactory()
265
    {
266 66
        return $this->hydratorFactory;
267
    }
268
269
    /**
270
     * Returns SchemaManager, used to create/drop indexes/collections/databases.
271
     *
272
     * @return \Doctrine\ODM\MongoDB\SchemaManager
273
     */
274 44
    public function getSchemaManager()
275
    {
276 44
        return $this->schemaManager;
277
    }
278
279
    /**
280
     * Returns the metadata for a class.
281
     *
282
     * @param string $className The class name.
283
     * @return \Doctrine\ODM\MongoDB\Mapping\ClassMetadata
284
     * @internal Performance-sensitive method.
285
     */
286 752
    public function getClassMetadata($className)
287
    {
288 752
        return $this->metadataFactory->getMetadataFor(ltrim($className, '\\'));
289
    }
290
291
    /**
292
     * Returns the MongoDB instance for a class.
293
     *
294
     * @param string $className The class name.
295
     * @return \Doctrine\MongoDB\Database
296
     */
297 695
    public function getDocumentDatabase($className)
298
    {
299 695
        $className = ltrim($className, '\\');
300
301 695
        if (isset($this->documentDatabases[$className])) {
302 188
            return $this->documentDatabases[$className];
303
        }
304
305 687
        $metadata = $this->metadataFactory->getMetadataFor($className);
306 687
        $db = $metadata->getDatabase();
307 687
        $db = $db ? $db : $this->config->getDefaultDB();
308 687
        $db = $db ? $db : 'doctrine';
309 687
        $this->documentDatabases[$className] = $this->connection->selectDatabase($db);
310
311 687
        return $this->documentDatabases[$className];
312
    }
313
314
    /**
315
     * Gets the array of instantiated document database instances.
316
     *
317
     * @return array
318
     */
319
    public function getDocumentDatabases()
320
    {
321
        return $this->documentDatabases;
322
    }
323
324
    /**
325
     * Returns the MongoCollection instance for a class.
326
     *
327
     * @param string $className The class name.
328
     * @throws MongoDBException When the $className param is not mapped to a collection
329
     * @return \Doctrine\MongoDB\Collection
330
     */
331 693
    public function getDocumentCollection($className)
332
    {
333 693
        $className = ltrim($className, '\\');
334
335 693
        $metadata = $this->metadataFactory->getMetadataFor($className);
336 693
        $collectionName = $metadata->getCollection();
337
338 693
        if ( ! $collectionName) {
339
            throw MongoDBException::documentNotMappedToCollection($className);
340
        }
341
342 693
        if ( ! isset($this->documentCollections[$className])) {
343 684
            $db = $this->getDocumentDatabase($className);
344
345 684
            $this->documentCollections[$className] = $metadata->isFile()
346 684
                ? $db->getGridFS($collectionName)
347 684
                : $db->selectCollection($collectionName);
348 684
        }
349
350 693
        $collection = $this->documentCollections[$className];
351
352 693
        if ($metadata->slaveOkay !== null) {
0 ignored issues
show
Bug introduced by
Accessing slaveOkay on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
353 2
            $collection->setSlaveOkay($metadata->slaveOkay);
0 ignored issues
show
Bug introduced by
Accessing slaveOkay on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
354 2
        }
355
356 693
        return $this->documentCollections[$className];
357
    }
358
359
    /**
360
     * Gets the array of instantiated document collection instances.
361
     *
362
     * @return array
363
     */
364
    public function getDocumentCollections()
365
    {
366
        return $this->documentCollections;
367
    }
368
369
    /**
370
     * Create a new Query instance for a class.
371
     *
372
     * @param string $documentName The document class name.
373
     * @return Query\Builder
374
     */
375 209
    public function createQueryBuilder($documentName = null)
376
    {
377 209
        return new Query\Builder($this, $documentName);
378
    }
379
380
    /**
381
     * Create a new aggregation builder instance for a class.
382
     *
383
     * @param string $documentName The document class name.
384
     * @return Aggregation\Builder
385
     */
386 2
    public function createAggregationBuilder($documentName)
387
    {
388 2
        return new Aggregation\Builder($this, $documentName);
389
    }
390
391
    /**
392
     * Tells the DocumentManager to make an instance managed and persistent.
393
     *
394
     * The document will be entered into the database at or before transaction
395
     * commit or as a result of the flush operation.
396
     *
397
     * NOTE: The persist operation always considers documents that are not yet known to
398
     * this DocumentManager as NEW. Do not pass detached documents to the persist operation.
399
     *
400
     * @param object $document The instance to make managed and persistent.
401
     * @throws \InvalidArgumentException When the given $document param is not an object
402
     */
403 557 View Code Duplication
    public function persist($document)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
404
    {
405 557
        if ( ! is_object($document)) {
406 1
            throw new \InvalidArgumentException(gettype($document));
407
        }
408 556
        $this->errorIfClosed();
409 555
        $this->unitOfWork->persist($document);
410 551
    }
411
412
    /**
413
     * Removes a document instance.
414
     *
415
     * A removed document will be removed from the database at or before transaction commit
416
     * or as a result of the flush operation.
417
     *
418
     * @param object $document The document instance to remove.
419
     * @throws \InvalidArgumentException when the $document param is not an object
420
     */
421 20 View Code Duplication
    public function remove($document)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
422
    {
423 20
        if ( ! is_object($document)) {
424 1
            throw new \InvalidArgumentException(gettype($document));
425
        }
426 19
        $this->errorIfClosed();
427 18
        $this->unitOfWork->remove($document);
428 18
    }
429
430
    /**
431
     * Refreshes the persistent state of a document from the database,
432
     * overriding any local changes that have not yet been persisted.
433
     *
434
     * @param object $document The document to refresh.
435
     * @throws \InvalidArgumentException When the given $document param is not an object
436
     */
437 23 View Code Duplication
    public function refresh($document)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
438
    {
439 23
        if ( ! is_object($document)) {
440 1
            throw new \InvalidArgumentException(gettype($document));
441
        }
442 22
        $this->errorIfClosed();
443 21
        $this->unitOfWork->refresh($document);
444 20
    }
445
446
    /**
447
     * Detaches a document from the DocumentManager, causing a managed document to
448
     * become detached.  Unflushed changes made to the document if any
449
     * (including removal of the document), will not be synchronized to the database.
450
     * Documents which previously referenced the detached document will continue to
451
     * reference it.
452
     *
453
     * @param object $document The document to detach.
454
     * @throws \InvalidArgumentException when the $document param is not an object
455
     */
456 9
    public function detach($document)
457
    {
458 9
        if ( ! is_object($document)) {
459 1
            throw new \InvalidArgumentException(gettype($document));
460
        }
461 8
        $this->unitOfWork->detach($document);
462 8
    }
463
464
    /**
465
     * Merges the state of a detached document into the persistence context
466
     * of this DocumentManager and returns the managed copy of the document.
467
     * The document passed to merge will not become associated/managed with this DocumentManager.
468
     *
469
     * @param object $document The detached document to merge into the persistence context.
470
     * @throws LockException
471
     * @throws \InvalidArgumentException if the $document param is not an object
472
     * @return object The managed copy of the document.
473
     */
474 15 View Code Duplication
    public function merge($document)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
475
    {
476 15
        if ( ! is_object($document)) {
477 1
            throw new \InvalidArgumentException(gettype($document));
478
        }
479 14
        $this->errorIfClosed();
480 13
        return $this->unitOfWork->merge($document);
481
    }
482
483
    /**
484
     * Acquire a lock on the given document.
485
     *
486
     * @param object $document
487
     * @param int $lockMode
488
     * @param int $lockVersion
489
     * @throws \InvalidArgumentException
490
     */
491 9
    public function lock($document, $lockMode, $lockVersion = null)
492
    {
493 9
        if ( ! is_object($document)) {
494
            throw new \InvalidArgumentException(gettype($document));
495
        }
496 9
        $this->unitOfWork->lock($document, $lockMode, $lockVersion);
497 6
    }
498
499
    /**
500
     * Releases a lock on the given document.
501
     *
502
     * @param object $document
503
     * @throws \InvalidArgumentException if the $document param is not an object
504
     */
505 1
    public function unlock($document)
506
    {
507 1
        if ( ! is_object($document)) {
508
            throw new \InvalidArgumentException(gettype($document));
509
        }
510 1
        $this->unitOfWork->unlock($document);
511 1
    }
512
513
    /**
514
     * Gets the repository for a document class.
515
     *
516
     * @param string $documentName  The name of the Document.
517
     * @return DocumentRepository  The repository.
518
     */
519 331
    public function getRepository($documentName)
520
    {
521 331
        return $this->repositoryFactory->getRepository($this, $documentName);
522
    }
523
524
    /**
525
     * Flushes all changes to objects that have been queued up to now to the database.
526
     * This effectively synchronizes the in-memory state of managed objects with the
527
     * database.
528
     *
529
     * @param object $document
530
     * @param array $options Array of options to be used with batchInsert(), update() and remove()
531
     * @throws \InvalidArgumentException
532
     */
533 543
    public function flush($document = null, array $options = array())
534
    {
535 543
        if (null !== $document && ! is_object($document) && ! is_array($document)) {
536
            throw new \InvalidArgumentException(gettype($document));
537
        }
538 543
        $this->errorIfClosed();
539 542
        $this->unitOfWork->commit($document, $options);
0 ignored issues
show
Bug introduced by
It seems like $document can also be of type array; however, Doctrine\ODM\MongoDB\UnitOfWork::commit() does only seem to accept object|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
540 539
    }
541
542
    /**
543
     * Gets a reference to the document identified by the given type and identifier
544
     * without actually loading it.
545
     *
546
     * If partial objects are allowed, this method will return a partial object that only
547
     * has its identifier populated. Otherwise a proxy is returned that automatically
548
     * loads itself on first access.
549
     *
550
     * @param string $documentName
551
     * @param string|object $identifier
552
     * @return mixed|object The document reference.
553
     */
554 112
    public function getReference($documentName, $identifier)
555
    {
556
        /* @var $class \Doctrine\ODM\MongoDB\Mapping\ClassMetadataInfo */
557 112
        $class = $this->metadataFactory->getMetadataFor(ltrim($documentName, '\\'));
558
559
        // Check identity map first, if its already in there just return it.
560 112
        if ($document = $this->unitOfWork->tryGetById($identifier, $class)) {
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\ODM\Mong...ping\ClassMetadataInfo> is not a sub-type of object<Doctrine\ODM\Mong...\Mapping\ClassMetadata>. It seems like you assume a child class of the class Doctrine\ODM\MongoDB\Mapping\ClassMetadataInfo to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
561 41
            return $document;
562
        }
563
564 88
        $document = $this->proxyFactory->getProxy($class->name, array($class->identifier => $identifier));
565 88
        $this->unitOfWork->registerManaged($document, $identifier, array());
0 ignored issues
show
Documentation introduced by
$identifier is of type string|object, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
566
567 88
        return $document;
568
    }
569
570
    /**
571
     * Gets a partial reference to the document identified by the given type and identifier
572
     * without actually loading it, if the document is not yet loaded.
573
     *
574
     * The returned reference may be a partial object if the document is not yet loaded/managed.
575
     * If it is a partial object it will not initialize the rest of the document state on access.
576
     * Thus you can only ever safely access the identifier of a document obtained through
577
     * this method.
578
     *
579
     * The use-cases for partial references involve maintaining bidirectional associations
580
     * without loading one side of the association or to update a document without loading it.
581
     * Note, however, that in the latter case the original (persistent) document data will
582
     * never be visible to the application (especially not event listeners) as it will
583
     * never be loaded in the first place.
584
     *
585
     * @param string $documentName The name of the document type.
586
     * @param mixed $identifier The document identifier.
587
     * @return object The (partial) document reference.
588
     */
589 1
    public function getPartialReference($documentName, $identifier)
590
    {
591 1
        $class = $this->metadataFactory->getMetadataFor(ltrim($documentName, '\\'));
592
593
        // Check identity map first, if its already in there just return it.
594 1
        if ($document = $this->unitOfWork->tryGetById($identifier, $class)) {
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ODM\Mong...\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
595
            return $document;
596
        }
597 1
        $document = $class->newInstance();
598 1
        $class->setIdentifierValue($document, $identifier);
0 ignored issues
show
Bug introduced by
The method setIdentifierValue() does not exist on Doctrine\Common\Persistence\Mapping\ClassMetadata. Did you maybe mean getIdentifierValues()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
599 1
        $this->unitOfWork->registerManaged($document, $identifier, array());
600
601 1
        return $document;
602
    }
603
604
    /**
605
     * Finds a Document by its identifier.
606
     *
607
     * This is just a convenient shortcut for getRepository($documentName)->find($id).
608
     *
609
     * @param string $documentName
610
     * @param mixed $identifier
611
     * @param int $lockMode
612
     * @param int $lockVersion
613
     * @return object $document
614
     */
615 172
    public function find($documentName, $identifier, $lockMode = LockMode::NONE, $lockVersion = null)
616
    {
617 172
        return $this->getRepository($documentName)->find($identifier, $lockMode, $lockVersion);
618
    }
619
620
    /**
621
     * Clears the DocumentManager.
622
     *
623
     * All documents that are currently managed by this DocumentManager become
624
     * detached.
625
     *
626
     * @param string|null $documentName if given, only documents of this type will get detached
627
     */
628 389
    public function clear($documentName = null)
629
    {
630 389
        $this->unitOfWork->clear($documentName);
631 389
    }
632
633
    /**
634
     * Closes the DocumentManager. All documents that are currently managed
635
     * by this DocumentManager become detached. The DocumentManager may no longer
636
     * be used after it is closed.
637
     */
638 6
    public function close()
639
    {
640 6
        $this->clear();
641 6
        $this->closed = true;
642 6
    }
643
644
    /**
645
     * Determines whether a document instance is managed in this DocumentManager.
646
     *
647
     * @param object $document
648
     * @throws \InvalidArgumentException When the $document param is not an object
649
     * @return boolean TRUE if this DocumentManager currently manages the given document, FALSE otherwise.
650
     */
651 6
    public function contains($document)
652
    {
653 6
        if ( ! is_object($document)) {
654
            throw new \InvalidArgumentException(gettype($document));
655
        }
656 6
        return $this->unitOfWork->isScheduledForInsert($document) ||
657 6
            $this->unitOfWork->isInIdentityMap($document) &&
658 6
            ! $this->unitOfWork->isScheduledForDelete($document);
659
    }
660
661
    /**
662
     * Gets the Configuration used by the DocumentManager.
663
     *
664
     * @return Configuration
665
     */
666 663
    public function getConfiguration()
667
    {
668 663
        return $this->config;
669
    }
670
671
    /**
672
     * Returns a DBRef array for the supplied document.
673
     *
674
     * @param mixed $document A document object
675
     * @param array $referenceMapping Mapping for the field that references the document
676
     *
677
     * @throws \InvalidArgumentException
678
     * @return array A DBRef array
679
     */
680 192
    public function createDBRef($document, array $referenceMapping = null)
681
    {
682 192
        if ( ! is_object($document)) {
683
            throw new \InvalidArgumentException('Cannot create a DBRef, the document is not an object');
684
        }
685
686 192
        $class = $this->getClassMetadata(get_class($document));
687 192
        $id = $this->unitOfWork->getDocumentIdentifier($document);
688
689 192
        if ( ! $id) {
690 1
            throw new \RuntimeException(
691 1
                sprintf('Cannot create a DBRef for class %s without an identifier. Have you forgotten to persist/merge the document first?', $class->name)
692 1
            );
693
        }
694
695 191
        if ( ! empty($referenceMapping['simple'])) {
696 19
            if ($class->inheritanceType === ClassMetadataInfo::INHERITANCE_TYPE_SINGLE_COLLECTION) {
697 1
                throw MappingException::simpleReferenceMustNotTargetDiscriminatedDocument($referenceMapping['targetDocument']);
698
            }
699 18
            return $class->getDatabaseIdentifierValue($id);
700
        }
701
702
        $dbRef = array(
703 174
            '$ref' => $class->getCollection(),
704 174
            '$id'  => $class->getDatabaseIdentifierValue($id),
705 174
            '$db'  => $this->getDocumentDatabase($class->name)->getName(),
706 174
        );
707
708
        /* If the class has a discriminator (field and value), use it. A child
709
         * class that is not defined in the discriminator map may only have a
710
         * discriminator field and no value, so default to the full class name.
711
         */
712 174 View Code Duplication
        if (isset($class->discriminatorField)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
713 17
            $dbRef[$class->discriminatorField] = isset($class->discriminatorValue)
714 17
                ? $class->discriminatorValue
715 17
                : $class->name;
716 17
        }
717
718
        /* Add a discriminator value if the referenced document is not mapped
719
         * explicitly to a targetDocument class.
720
         */
721 174 View Code Duplication
        if ($referenceMapping !== null && ! isset($referenceMapping['targetDocument'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
722 23
            $discriminatorField = $referenceMapping['discriminatorField'];
723 23
            $discriminatorValue = isset($referenceMapping['discriminatorMap'])
724 23
                ? array_search($class->name, $referenceMapping['discriminatorMap'])
725 23
                : $class->name;
726
727
            /* If the discriminator value was not found in the map, use the full
728
             * class name. In the future, it may be preferable to throw an
729
             * exception here (perhaps based on some strictness option).
730
             *
731
             * @see PersistenceBuilder::prepareEmbeddedDocumentValue()
732
             */
733 23
            if ($discriminatorValue === false) {
734 2
                $discriminatorValue = $class->name;
735 2
            }
736
737 23
            $dbRef[$discriminatorField] = $discriminatorValue;
738 23
        }
739
740 174
        return $dbRef;
741
    }
742
743
    /**
744
     * Throws an exception if the DocumentManager is closed or currently not active.
745
     *
746
     * @throws MongoDBException If the DocumentManager is closed.
747
     */
748 564
    private function errorIfClosed()
749
    {
750 564
        if ($this->closed) {
751 5
            throw MongoDBException::documentManagerClosed();
752
        }
753 559
    }
754
755
    /**
756
     * Check if the Document manager is open or closed.
757
     *
758
     * @return bool
759
     */
760 1
    public function isOpen()
761
    {
762 1
        return ( ! $this->closed);
763
    }
764
765
    /**
766
     * Gets the filter collection.
767
     *
768
     * @return \Doctrine\ODM\MongoDB\Query\FilterCollection The active filter collection.
769
     */
770 481
    public function getFilterCollection()
771
    {
772 481
        if (null === $this->filterCollection) {
773 481
            $this->filterCollection = new FilterCollection($this);
774 481
        }
775
776 481
        return $this->filterCollection;
777
    }
778
}
779