Completed
Pull Request — master (#1527)
by Andreas
10:44
created

DocumentManager   F

Complexity

Total Complexity 81

Size/Duplication

Total Lines 754
Duplicated Lines 7.29 %

Coupling/Cohesion

Components 3
Dependencies 19

Test Coverage

Coverage 91.67%

Importance

Changes 0
Metric Value
wmc 81
lcom 3
cbo 19
dl 55
loc 754
ccs 187
cts 204
cp 0.9167
rs 1.263
c 0
b 0
f 0

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
D createDBRef() 23 82 17
A errorIfClosed() 0 6 2
A isOpen() 0 4 1
A getFilterCollection() 0 8 2

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
use Doctrine\ODM\MongoDB\Types\Type;
32
33
/**
34
 * The DocumentManager class is the central access point for managing the
35
 * persistence of documents.
36
 *
37
 *     <?php
38
 *
39
 *     $config = new Configuration();
40
 *     $dm = DocumentManager::create(new Connection(), $config);
41
 *
42
 * @since       1.0
43
 */
44
class DocumentManager implements ObjectManager
45
{
46
    /**
47
     * The Doctrine MongoDB connection instance.
48
     *
49
     * @var \Doctrine\MongoDB\Connection
50
     */
51
    private $connection;
52
53
    /**
54
     * The used Configuration.
55
     *
56
     * @var \Doctrine\ODM\MongoDB\Configuration
57
     */
58
    private $config;
59
60
    /**
61
     * The metadata factory, used to retrieve the ODM metadata of document classes.
62
     *
63
     * @var \Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactory
64
     */
65
    private $metadataFactory;
66
67
    /**
68
     * The UnitOfWork used to coordinate object-level transactions.
69
     *
70
     * @var UnitOfWork
71
     */
72
    private $unitOfWork;
73
74
    /**
75
     * The event manager that is the central point of the event system.
76
     *
77
     * @var \Doctrine\Common\EventManager
78
     */
79
    private $eventManager;
80
81
    /**
82
     * The Hydrator factory instance.
83
     *
84
     * @var HydratorFactory
85
     */
86
    private $hydratorFactory;
87
88
    /**
89
     * The Proxy factory instance.
90
     *
91
     * @var ProxyFactory
92
     */
93
    private $proxyFactory;
94
95
    /**
96
     * The repository factory used to create dynamic repositories.
97
     *
98
     * @var RepositoryFactory
99
     */
100
    private $repositoryFactory;
101
102
    /**
103
     * SchemaManager instance
104
     *
105
     * @var SchemaManager
106
     */
107
    private $schemaManager;
108
109
    /**
110
     * Array of cached document database instances that are lazily loaded.
111
     *
112
     * @var array
113
     */
114
    private $documentDatabases = array();
115
116
    /**
117
     * Array of cached document collection instances that are lazily loaded.
118
     *
119
     * @var array
120
     */
121
    private $documentCollections = array();
122
123
    /**
124
     * Whether the DocumentManager is closed or not.
125
     *
126
     * @var bool
127
     */
128
    private $closed = false;
129
130
    /**
131
     * Collection of query filters.
132
     *
133
     * @var \Doctrine\ODM\MongoDB\Query\FilterCollection
134
     */
135
    private $filterCollection;
136
137
    /**
138
     * Creates a new Document that operates on the given Mongo connection
139
     * and uses the given Configuration.
140
     *
141
     * @param \Doctrine\MongoDB\Connection|null $conn
142
     * @param Configuration|null $config
143
     * @param \Doctrine\Common\EventManager|null $eventManager
144
     */
145 1078
    protected function __construct(Connection $conn = null, Configuration $config = null, EventManager $eventManager = null)
146
    {
147 1078
        $this->config = $config ?: new Configuration();
148 1078
        $this->eventManager = $eventManager ?: new EventManager();
149 1078
        $this->connection = $conn ?: new Connection(null, array(), $this->config, $this->eventManager);
150
151 1078
        $metadataFactoryClassName = $this->config->getClassMetadataFactoryName();
152 1078
        $this->metadataFactory = new $metadataFactoryClassName();
153 1078
        $this->metadataFactory->setDocumentManager($this);
154 1078
        $this->metadataFactory->setConfiguration($this->config);
155 1078
        if ($cacheDriver = $this->config->getMetadataCacheImpl()) {
156
            $this->metadataFactory->setCacheDriver($cacheDriver);
157
        }
158
159 1078
        $hydratorDir = $this->config->getHydratorDir();
160 1078
        $hydratorNs = $this->config->getHydratorNamespace();
161 1078
        $this->hydratorFactory = new HydratorFactory(
162
            $this,
163 1078
            $this->eventManager,
164
            $hydratorDir,
165
            $hydratorNs,
166 1078
            $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...
167
        );
168
169 1078
        $this->unitOfWork = new UnitOfWork($this, $this->eventManager, $this->hydratorFactory);
170 1078
        $this->hydratorFactory->setUnitOfWork($this->unitOfWork);
171 1078
        $this->schemaManager = new SchemaManager($this, $this->metadataFactory);
172 1078
        $this->proxyFactory = new ProxyFactory($this,
173 1078
            $this->config->getProxyDir(),
174 1078
            $this->config->getProxyNamespace(),
175 1078
            $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...
176
        );
177 1078
        $this->repositoryFactory = $this->config->getRepositoryFactory();
178 1078
    }
179
180
    /**
181
     * Gets the proxy factory used by the DocumentManager to create document proxies.
182
     *
183
     * @return ProxyFactory
184
     */
185 1
    public function getProxyFactory()
186
    {
187 1
        return $this->proxyFactory;
188
    }
189
190
    /**
191
     * Creates a new Document that operates on the given Mongo connection
192
     * and uses the given Configuration.
193
     *
194
     * @static
195
     * @param \Doctrine\MongoDB\Connection|null $conn
196
     * @param Configuration|null $config
197
     * @param \Doctrine\Common\EventManager|null $eventManager
198
     * @return DocumentManager
199
     */
200 1078
    public static function create(Connection $conn = null, Configuration $config = null, EventManager $eventManager = null)
201
    {
202 1078
        return new static($conn, $config, $eventManager);
203
    }
204
205
    /**
206
     * Gets the EventManager used by the DocumentManager.
207
     *
208
     * @return \Doctrine\Common\EventManager
209
     */
210 1128
    public function getEventManager()
211
    {
212 1128
        return $this->eventManager;
213
    }
214
215
    /**
216
     * Gets the PHP Mongo instance that this DocumentManager wraps.
217
     *
218
     * @return \Doctrine\MongoDB\Connection
219
     */
220 1084
    public function getConnection()
221
    {
222 1084
        return $this->connection;
223
    }
224
225
    /**
226
     * Gets the metadata factory used to gather the metadata of classes.
227
     *
228
     * @return \Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactory
229
     */
230 1078
    public function getMetadataFactory()
231
    {
232 1078
        return $this->metadataFactory;
233
    }
234
235
    /**
236
     * Helper method to initialize a lazy loading proxy or persistent collection.
237
     *
238
     * This method is a no-op for other objects.
239
     *
240
     * @param object $obj
241
     */
242
    public function initializeObject($obj)
243
    {
244
        $this->unitOfWork->initializeObject($obj);
245
    }
246
247
    /**
248
     * Gets the UnitOfWork used by the DocumentManager to coordinate operations.
249
     *
250
     * @return UnitOfWork
251
     */
252 1084
    public function getUnitOfWork()
253
    {
254 1084
        return $this->unitOfWork;
255
    }
256
257
    /**
258
     * Gets the Hydrator factory used by the DocumentManager to generate and get hydrators
259
     * for each type of document.
260
     *
261
     * @return \Doctrine\ODM\MongoDB\Hydrator\HydratorInterface
262
     */
263 71
    public function getHydratorFactory()
264
    {
265 71
        return $this->hydratorFactory;
266
    }
267
268
    /**
269
     * Returns SchemaManager, used to create/drop indexes/collections/databases.
270
     *
271
     * @return \Doctrine\ODM\MongoDB\SchemaManager
272
     */
273 55
    public function getSchemaManager()
274
    {
275 55
        return $this->schemaManager;
276
    }
277
278
    /**
279
     * Returns the metadata for a class.
280
     *
281
     * @param string $className The class name.
282
     * @return \Doctrine\ODM\MongoDB\Mapping\ClassMetadata
283
     * @internal Performance-sensitive method.
284
     */
285 844
    public function getClassMetadata($className)
286
    {
287 844
        return $this->metadataFactory->getMetadataFor(ltrim($className, '\\'));
288
    }
289
290
    /**
291
     * Returns the MongoDB instance for a class.
292
     *
293
     * @param string $className The class name.
294
     * @return \Doctrine\MongoDB\Database
295
     */
296 778
    public function getDocumentDatabase($className)
297
    {
298 778
        $className = ltrim($className, '\\');
299
300 778
        if (isset($this->documentDatabases[$className])) {
301 218
            return $this->documentDatabases[$className];
302
        }
303
304 767
        $metadata = $this->metadataFactory->getMetadataFor($className);
305 767
        $db = $metadata->getDatabase();
306 767
        $db = $db ?: $this->config->getDefaultDB();
307 767
        $db = $db ?: 'doctrine';
308 767
        $this->documentDatabases[$className] = $this->connection->selectDatabase($db);
309
310 767
        return $this->documentDatabases[$className];
311
    }
312
313
    /**
314
     * Gets the array of instantiated document database instances.
315
     *
316
     * @return array
317
     */
318
    public function getDocumentDatabases()
319
    {
320
        return $this->documentDatabases;
321
    }
322
323
    /**
324
     * Returns the MongoCollection instance for a class.
325
     *
326
     * @param string $className The class name.
327
     * @throws MongoDBException When the $className param is not mapped to a collection
328
     * @return \Doctrine\MongoDB\Collection
329
     */
330 774
    public function getDocumentCollection($className)
331
    {
332 774
        $className = ltrim($className, '\\');
333
334 774
        $metadata = $this->metadataFactory->getMetadataFor($className);
335 774
        $collectionName = $metadata->getCollection();
336
337 774
        if ( ! $collectionName) {
338
            throw MongoDBException::documentNotMappedToCollection($className);
339
        }
340
341 774
        if ( ! isset($this->documentCollections[$className])) {
342 763
            $db = $this->getDocumentDatabase($className);
343
344 763
            $this->documentCollections[$className] = $metadata->isFile()
345 13
                ? $db->getGridFS($collectionName)
346 756
                : $db->selectCollection($collectionName);
347
        }
348
349 774
        $collection = $this->documentCollections[$className];
350
351 774
        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...
352 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...
353
        }
354
355 774
        return $this->documentCollections[$className];
356
    }
357
358
    /**
359
     * Gets the array of instantiated document collection instances.
360
     *
361
     * @return array
362
     */
363
    public function getDocumentCollections()
364
    {
365
        return $this->documentCollections;
366
    }
367
368
    /**
369
     * Create a new Query instance for a class.
370
     *
371
     * @param string $documentName The document class name.
372
     * @return Query\Builder
373
     */
374 225
    public function createQueryBuilder($documentName = null)
375
    {
376 225
        return new Query\Builder($this, $documentName);
377
    }
378
379
    /**
380
     * Creates a new aggregation builder instance for a class.
381
     *
382
     * @param string $documentName The document class name.
383
     * @return Aggregation\Builder
384
     */
385 11
    public function createAggregationBuilder($documentName)
386
    {
387 11
        return new Aggregation\Builder($this, $documentName);
388
    }
389
390
    /**
391
     * Tells the DocumentManager to make an instance managed and persistent.
392
     *
393
     * The document will be entered into the database at or before transaction
394
     * commit or as a result of the flush operation.
395
     *
396
     * NOTE: The persist operation always considers documents that are not yet known to
397
     * this DocumentManager as NEW. Do not pass detached documents to the persist operation.
398
     *
399
     * @param object $document The instance to make managed and persistent.
400
     * @throws \InvalidArgumentException When the given $document param is not an object
401
     */
402 626 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...
403
    {
404 626
        if ( ! is_object($document)) {
405 1
            throw new \InvalidArgumentException(gettype($document));
406
        }
407 625
        $this->errorIfClosed();
408 624
        $this->unitOfWork->persist($document);
409 620
    }
410
411
    /**
412
     * Removes a document instance.
413
     *
414
     * A removed document will be removed from the database at or before transaction commit
415
     * or as a result of the flush operation.
416
     *
417
     * @param object $document The document instance to remove.
418
     * @throws \InvalidArgumentException when the $document param is not an object
419
     */
420 25 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...
421
    {
422 25
        if ( ! is_object($document)) {
423 1
            throw new \InvalidArgumentException(gettype($document));
424
        }
425 24
        $this->errorIfClosed();
426 23
        $this->unitOfWork->remove($document);
427 23
    }
428
429
    /**
430
     * Refreshes the persistent state of a document from the database,
431
     * overriding any local changes that have not yet been persisted.
432
     *
433
     * @param object $document The document to refresh.
434
     * @throws \InvalidArgumentException When the given $document param is not an object
435
     */
436 24 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...
437
    {
438 24
        if ( ! is_object($document)) {
439 1
            throw new \InvalidArgumentException(gettype($document));
440
        }
441 23
        $this->errorIfClosed();
442 22
        $this->unitOfWork->refresh($document);
443 21
    }
444
445
    /**
446
     * Detaches a document from the DocumentManager, causing a managed document to
447
     * become detached.  Unflushed changes made to the document if any
448
     * (including removal of the document), will not be synchronized to the database.
449
     * Documents which previously referenced the detached document will continue to
450
     * reference it.
451
     *
452
     * @param object $document The document to detach.
453
     * @throws \InvalidArgumentException when the $document param is not an object
454
     */
455 12
    public function detach($document)
456
    {
457 12
        if ( ! is_object($document)) {
458 1
            throw new \InvalidArgumentException(gettype($document));
459
        }
460 11
        $this->unitOfWork->detach($document);
461 11
    }
462
463
    /**
464
     * Merges the state of a detached document into the persistence context
465
     * of this DocumentManager and returns the managed copy of the document.
466
     * The document passed to merge will not become associated/managed with this DocumentManager.
467
     *
468
     * @param object $document The detached document to merge into the persistence context.
469
     * @throws LockException
470
     * @throws \InvalidArgumentException if the $document param is not an object
471
     * @return object The managed copy of the document.
472
     */
473 17 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...
474
    {
475 17
        if ( ! is_object($document)) {
476 1
            throw new \InvalidArgumentException(gettype($document));
477
        }
478 16
        $this->errorIfClosed();
479 15
        return $this->unitOfWork->merge($document);
480
    }
481
482
    /**
483
     * Acquire a lock on the given document.
484
     *
485
     * @param object $document
486
     * @param int $lockMode
487
     * @param int $lockVersion
488
     * @throws \InvalidArgumentException
489
     */
490 9
    public function lock($document, $lockMode, $lockVersion = null)
491
    {
492 9
        if ( ! is_object($document)) {
493
            throw new \InvalidArgumentException(gettype($document));
494
        }
495 9
        $this->unitOfWork->lock($document, $lockMode, $lockVersion);
496 6
    }
497
498
    /**
499
     * Releases a lock on the given document.
500
     *
501
     * @param object $document
502
     * @throws \InvalidArgumentException if the $document param is not an object
503
     */
504 1
    public function unlock($document)
505
    {
506 1
        if ( ! is_object($document)) {
507
            throw new \InvalidArgumentException(gettype($document));
508
        }
509 1
        $this->unitOfWork->unlock($document);
510 1
    }
511
512
    /**
513
     * Gets the repository for a document class.
514
     *
515
     * @param string $documentName  The name of the Document.
516
     * @return DocumentRepository  The repository.
517
     */
518 356
    public function getRepository($documentName)
519
    {
520 356
        return $this->repositoryFactory->getRepository($this, $documentName);
521
    }
522
523
    /**
524
     * Flushes all changes to objects that have been queued up to now to the database.
525
     * This effectively synchronizes the in-memory state of managed objects with the
526
     * database.
527
     *
528
     * @param object $document
529
     * @param array $options Array of options to be used with batchInsert(), update() and remove()
530
     * @throws \InvalidArgumentException
531
     */
532 601
    public function flush($document = null, array $options = array())
533
    {
534 601
        if (null !== $document && ! is_object($document) && ! is_array($document)) {
535
            throw new \InvalidArgumentException(gettype($document));
536
        }
537 601
        $this->errorIfClosed();
538 600
        $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...
539 597
    }
540
541
    /**
542
     * Gets a reference to the document identified by the given type and identifier
543
     * without actually loading it.
544
     *
545
     * If partial objects are allowed, this method will return a partial object that only
546
     * has its identifier populated. Otherwise a proxy is returned that automatically
547
     * loads itself on first access.
548
     *
549
     * @param string $documentName
550
     * @param string|object $identifier
551
     * @return mixed|object The document reference.
552
     */
553 120
    public function getReference($documentName, $identifier)
554
    {
555
        /* @var $class \Doctrine\ODM\MongoDB\Mapping\ClassMetadataInfo */
556 120
        $class = $this->metadataFactory->getMetadataFor(ltrim($documentName, '\\'));
557
558
        // Check identity map first, if its already in there just return it.
559 120
        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...
560 45
            return $document;
561
        }
562
563 94
        $document = $this->proxyFactory->getProxy($class->name, array($class->identifier => $identifier));
564 94
        $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...
565
566 94
        return $document;
567
    }
568
569
    /**
570
     * Gets a partial reference to the document identified by the given type and identifier
571
     * without actually loading it, if the document is not yet loaded.
572
     *
573
     * The returned reference may be a partial object if the document is not yet loaded/managed.
574
     * If it is a partial object it will not initialize the rest of the document state on access.
575
     * Thus you can only ever safely access the identifier of a document obtained through
576
     * this method.
577
     *
578
     * The use-cases for partial references involve maintaining bidirectional associations
579
     * without loading one side of the association or to update a document without loading it.
580
     * Note, however, that in the latter case the original (persistent) document data will
581
     * never be visible to the application (especially not event listeners) as it will
582
     * never be loaded in the first place.
583
     *
584
     * @param string $documentName The name of the document type.
585
     * @param mixed $identifier The document identifier.
586
     * @return object The (partial) document reference.
587
     */
588 1
    public function getPartialReference($documentName, $identifier)
589
    {
590 1
        $class = $this->metadataFactory->getMetadataFor(ltrim($documentName, '\\'));
591
592
        // Check identity map first, if its already in there just return it.
593 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...
594
            return $document;
595
        }
596 1
        $document = $class->newInstance();
597 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...
598 1
        $this->unitOfWork->registerManaged($document, $identifier, array());
599
600 1
        return $document;
601
    }
602
603
    /**
604
     * Finds a Document by its identifier.
605
     *
606
     * This is just a convenient shortcut for getRepository($documentName)->find($id).
607
     *
608
     * @param string $documentName
609
     * @param mixed $identifier
610
     * @param int $lockMode
611
     * @param int $lockVersion
612
     * @return object $document
613
     */
614 186
    public function find($documentName, $identifier, $lockMode = LockMode::NONE, $lockVersion = null)
615
    {
616 186
        return $this->getRepository($documentName)->find($identifier, $lockMode, $lockVersion);
617
    }
618
619
    /**
620
     * Clears the DocumentManager.
621
     *
622
     * All documents that are currently managed by this DocumentManager become
623
     * detached.
624
     *
625
     * @param string|null $documentName if given, only documents of this type will get detached
626
     */
627 411
    public function clear($documentName = null)
628
    {
629 411
        $this->unitOfWork->clear($documentName);
630 411
    }
631
632
    /**
633
     * Closes the DocumentManager. All documents that are currently managed
634
     * by this DocumentManager become detached. The DocumentManager may no longer
635
     * be used after it is closed.
636
     */
637 6
    public function close()
638
    {
639 6
        $this->clear();
640 6
        $this->closed = true;
641 6
    }
642
643
    /**
644
     * Determines whether a document instance is managed in this DocumentManager.
645
     *
646
     * @param object $document
647
     * @throws \InvalidArgumentException When the $document param is not an object
648
     * @return boolean TRUE if this DocumentManager currently manages the given document, FALSE otherwise.
649
     */
650 6
    public function contains($document)
651
    {
652 6
        if ( ! is_object($document)) {
653
            throw new \InvalidArgumentException(gettype($document));
654
        }
655 6
        return $this->unitOfWork->isScheduledForInsert($document) ||
656 6
            $this->unitOfWork->isInIdentityMap($document) &&
657 6
            ! $this->unitOfWork->isScheduledForDelete($document);
658
    }
659
660
    /**
661
     * Gets the Configuration used by the DocumentManager.
662
     *
663
     * @return Configuration
664
     */
665 760
    public function getConfiguration()
666
    {
667 760
        return $this->config;
668
    }
669
670
    /**
671
     * Returns a DBRef array for the supplied document.
672
     *
673
     * @param mixed $document A document object
674
     * @param array $referenceMapping Mapping for the field that references the document
675
     *
676
     * @throws \InvalidArgumentException
677
     * @return array A DBRef array
678
     */
679 219
    public function createDBRef($document, array $referenceMapping = null)
680
    {
681 219
        if ( ! is_object($document)) {
682
            throw new \InvalidArgumentException('Cannot create a DBRef, the document is not an object');
683
        }
684
685 219
        $class = $this->getClassMetadata(get_class($document));
686 219
        $id = $this->unitOfWork->getDocumentIdentifier($document);
687
688 219
        if ($id === null) {
689 1
            throw new \RuntimeException(
690 1
                sprintf('Cannot create a DBRef for class %s without an identifier. Have you forgotten to persist/merge the document first?', $class->name)
691
            );
692
        }
693
694 218
        if ($referenceMapping['storeAs'] === ClassMetadataInfo::REFERENCE_STORE_AS_ID) {
695 27
            if ($class->inheritanceType === ClassMetadataInfo::INHERITANCE_TYPE_SINGLE_COLLECTION) {
696 1
                throw MappingException::simpleReferenceMustNotTargetDiscriminatedDocument($referenceMapping['targetDocument']);
697
            }
698 26
            return $class->getDatabaseIdentifierValue($id);
699
        }
700
701
        $dbRef = array(
702 198
            '$ref' => $class->getCollection(),
703 198
            '$id'  => $class->getDatabaseIdentifierValue($id),
704
        );
705
706 198
        if ($referenceMapping['storeAs'] === ClassMetadataInfo::REFERENCE_STORE_AS_DB_REF_WITH_DB) {
707 193
            $dbRef['$db'] = $this->getDocumentDatabase($class->name)->getName();
708
        }
709
710
        /* If the class has a discriminator (field and value), use it. A child
711
         * class that is not defined in the discriminator map may only have a
712
         * discriminator field and no value, so default to the full class name.
713
         */
714 198 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...
715 18
            $dbRef[$class->discriminatorField] = isset($class->discriminatorValue)
716 16
                ? $class->discriminatorValue
717 4
                : $class->name;
718
        }
719
720
        /* Add a discriminator value if the referenced document is not mapped
721
         * explicitly to a targetDocument class.
722
         */
723 198 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...
724 30
            $discriminatorField = $referenceMapping['discriminatorField'];
725 30
            $discriminatorValue = isset($referenceMapping['discriminatorMap'])
726 8
                ? array_search($class->name, $referenceMapping['discriminatorMap'])
727 30
                : $class->name;
728
729
            /* If the discriminator value was not found in the map, use the full
730
             * class name. In the future, it may be preferable to throw an
731
             * exception here (perhaps based on some strictness option).
732
             *
733
             * @see PersistenceBuilder::prepareEmbeddedDocumentValue()
734
             */
735 30
            if ($discriminatorValue === false) {
736 2
                $discriminatorValue = $class->name;
737
            }
738
739 30
            $dbRef[$discriminatorField] = $discriminatorValue;
740
        }
741
742 198
        if ($referenceMapping !== null && isset($referenceMapping['redundantFields'])) {
743 196
            foreach ($referenceMapping['redundantFields'] as $fieldName) {
744 3
                if ($class->hasAssociation($fieldName)) {
745
                    throw MappingException::redundantAssociationNotAllowed(get_class($document), $fieldName);
746
                }
747
748 3
                if (! $class->hasField($fieldName)) {
749
                    throw MappingException::mappingNotFound(get_class($document), $fieldName);
750
                }
751
752 3
                $mapping = $class->getFieldMapping($fieldName);
753 3
                $value = $class->getFieldValue($document, $fieldName);
754
755 3
                $dbRef[$fieldName] = Type::getType($mapping['type'])->convertToDatabaseValue($value);
756
            }
757
        }
758
759 198
        return $dbRef;
760
    }
761
762
    /**
763
     * Throws an exception if the DocumentManager is closed or currently not active.
764
     *
765
     * @throws MongoDBException If the DocumentManager is closed.
766
     */
767 634
    private function errorIfClosed()
768
    {
769 634
        if ($this->closed) {
770 5
            throw MongoDBException::documentManagerClosed();
771
        }
772 629
    }
773
774
    /**
775
     * Check if the Document manager is open or closed.
776
     *
777
     * @return bool
778
     */
779 1
    public function isOpen()
780
    {
781 1
        return ( ! $this->closed);
782
    }
783
784
    /**
785
     * Gets the filter collection.
786
     *
787
     * @return \Doctrine\ODM\MongoDB\Query\FilterCollection The active filter collection.
788
     */
789 523
    public function getFilterCollection()
790
    {
791 523
        if (null === $this->filterCollection) {
792 523
            $this->filterCollection = new FilterCollection($this);
793
        }
794
795 523
        return $this->filterCollection;
796
    }
797
}
798