Completed
Push — master ( fd7345...08b9e1 )
by Andreas
11s
created

DocumentManager   F

Complexity

Total Complexity 72

Size/Duplication

Total Lines 746
Duplicated Lines 0 %

Coupling/Cohesion

Components 3
Dependencies 17

Test Coverage

Coverage 92.65%

Importance

Changes 0
Metric Value
wmc 72
lcom 3
cbo 17
dl 0
loc 746
ccs 189
cts 204
cp 0.9265
rs 2.0338
c 0
b 0
f 0

37 Methods

Rating   Name   Duplication   Size   Complexity  
A detach() 0 7 2
A lock() 0 7 2
A unlock() 0 7 2
A getRepository() 0 4 1
B __construct() 0 37 5
A getProxyFactory() 0 4 1
A create() 0 4 1
A getEventManager() 0 4 1
A getClient() 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 24 4
A getDocumentCollections() 0 4 1
A createQueryBuilder() 0 4 1
A createAggregationBuilder() 0 4 1
A persist() 0 8 2
A remove() 0 8 2
A refresh() 0 8 2
A merge() 0 8 2
A flush() 0 5 1
A getReference() 0 16 2
A getPartialReference() 0 15 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
C createReference() 0 81 12
A errorIfClosed() 0 6 2
A isOpen() 0 4 1
A getFilterCollection() 0 8 2

How to fix   Complexity   

Complex Class

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
declare(strict_types=1);
4
5
namespace Doctrine\ODM\MongoDB;
6
7
use Doctrine\Common\EventManager;
8
use Doctrine\Common\Persistence\ObjectManager;
9
use Doctrine\Common\Persistence\ObjectRepository;
10
use Doctrine\ODM\MongoDB\Hydrator\HydratorFactory;
11
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
12
use Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactory;
13
use Doctrine\ODM\MongoDB\Mapping\MappingException;
14
use Doctrine\ODM\MongoDB\Proxy\ProxyFactory;
15
use Doctrine\ODM\MongoDB\Query\FilterCollection;
16
use Doctrine\ODM\MongoDB\Repository\RepositoryFactory;
17
use MongoDB\Client;
18
use MongoDB\Collection;
19
use MongoDB\Database;
20
use MongoDB\Driver\ReadPreference;
21
use function array_search;
22
use function get_class;
23
use function gettype;
24
use function is_object;
25
use function ltrim;
26
use function sprintf;
27
28
/**
29
 * The DocumentManager class is the central access point for managing the
30
 * persistence of documents.
31
 *
32
 *     <?php
33
 *
34
 *     $config = new Configuration();
35
 *     $dm = DocumentManager::create(new Connection(), $config);
36
 *
37
 */
38
class DocumentManager implements ObjectManager
39
{
40
    /**
41
     * The Doctrine MongoDB connection instance.
42
     *
43
     * @var Client
44
     */
45
    private $client;
46
47
    /**
48
     * The used Configuration.
49
     *
50
     * @var Configuration
51
     */
52
    private $config;
53
54
    /**
55
     * The metadata factory, used to retrieve the ODM metadata of document classes.
56
     *
57
     * @var ClassMetadataFactory
58
     */
59
    private $metadataFactory;
60
61
    /**
62
     * The UnitOfWork used to coordinate object-level transactions.
63
     *
64
     * @var UnitOfWork
65
     */
66
    private $unitOfWork;
67
68
    /**
69
     * The event manager that is the central point of the event system.
70
     *
71
     * @var EventManager
72
     */
73
    private $eventManager;
74
75
    /**
76
     * The Hydrator factory instance.
77
     *
78
     * @var HydratorFactory
79
     */
80
    private $hydratorFactory;
81
82
    /**
83
     * The Proxy factory instance.
84
     *
85
     * @var ProxyFactory
86
     */
87
    private $proxyFactory;
88
89
    /**
90
     * The repository factory used to create dynamic repositories.
91
     *
92
     * @var RepositoryFactory
93
     */
94
    private $repositoryFactory;
95
96
    /**
97
     * SchemaManager instance
98
     *
99
     * @var SchemaManager
100
     */
101
    private $schemaManager;
102
103
    /**
104
     * Array of cached document database instances that are lazily loaded.
105
     *
106
     * @var Database[]
107
     */
108
    private $documentDatabases = [];
109
110
    /**
111
     * Array of cached document collection instances that are lazily loaded.
112
     *
113
     * @var Collection[]
114
     */
115
    private $documentCollections = [];
116
117
    /**
118
     * Whether the DocumentManager is closed or not.
119
     *
120
     * @var bool
121
     */
122
    private $closed = false;
123
124
    /**
125
     * Collection of query filters.
126
     *
127
     * @var FilterCollection
128
     */
129
    private $filterCollection;
130
131
    /**
132
     * Creates a new Document that operates on the given Mongo connection
133
     * and uses the given Configuration.
134
     *
135
     */
136 1584
    protected function __construct(?Client $client = null, ?Configuration $config = null, ?EventManager $eventManager = null)
137
    {
138 1584
        $this->config = $config ?: new Configuration();
139 1584
        $this->eventManager = $eventManager ?: new EventManager();
140 1584
        $this->client = $client ?: new Client('mongodb://127.0.0.1', [], ['typeMap' => ['root' => 'array', 'document' => 'array']]);
141
142 1584
        $metadataFactoryClassName = $this->config->getClassMetadataFactoryName();
143 1584
        $this->metadataFactory = new $metadataFactoryClassName();
144 1584
        $this->metadataFactory->setDocumentManager($this);
145 1584
        $this->metadataFactory->setConfiguration($this->config);
146
147 1584
        $cacheDriver = $this->config->getMetadataCacheImpl();
148 1584
        if ($cacheDriver) {
149
            $this->metadataFactory->setCacheDriver($cacheDriver);
150
        }
151
152 1584
        $hydratorDir = $this->config->getHydratorDir();
153 1584
        $hydratorNs = $this->config->getHydratorNamespace();
154 1584
        $this->hydratorFactory = new HydratorFactory(
155 1584
            $this,
156 1584
            $this->eventManager,
157 1584
            $hydratorDir,
158 1584
            $hydratorNs,
159 1584
            $this->config->getAutoGenerateHydratorClasses()
160
        );
161
162 1584
        $this->unitOfWork = new UnitOfWork($this, $this->eventManager, $this->hydratorFactory);
163 1584
        $this->hydratorFactory->setUnitOfWork($this->unitOfWork);
164 1584
        $this->schemaManager = new SchemaManager($this, $this->metadataFactory);
165 1584
        $this->proxyFactory = new ProxyFactory(
166 1584
            $this,
167 1584
            $this->config->getProxyDir(),
168 1584
            $this->config->getProxyNamespace(),
169 1584
            $this->config->getAutoGenerateProxyClasses()
170
        );
171 1584
        $this->repositoryFactory = $this->config->getRepositoryFactory();
172 1584
    }
173
174
    /**
175
     * Gets the proxy factory used by the DocumentManager to create document proxies.
176
     *
177
     * @return ProxyFactory
178
     */
179 1
    public function getProxyFactory()
180
    {
181 1
        return $this->proxyFactory;
182
    }
183
184
    /**
185
     * Creates a new Document that operates on the given Mongo connection
186
     * and uses the given Configuration.
187
     *
188
     * @static
189
     * @return DocumentManager
190
     */
191 1584
    public static function create(?Client $client = null, ?Configuration $config = null, ?EventManager $eventManager = null)
192
    {
193 1584
        return new static($client, $config, $eventManager);
194
    }
195
196
    /**
197
     * Gets the EventManager used by the DocumentManager.
198
     *
199
     * @return EventManager
200
     */
201 1627
    public function getEventManager()
202
    {
203 1627
        return $this->eventManager;
204
    }
205
206
    /**
207
     * Gets the MongoDB client instance that this DocumentManager wraps.
208
     *
209
     * @return Client
210
     */
211 1584
    public function getClient()
212
    {
213 1584
        return $this->client;
214
    }
215
216
    /**
217
     * Gets the metadata factory used to gather the metadata of classes.
218
     *
219
     * @return ClassMetadataFactory
220
     */
221 1584
    public function getMetadataFactory()
222
    {
223 1584
        return $this->metadataFactory;
224
    }
225
226
    /**
227
     * Helper method to initialize a lazy loading proxy or persistent collection.
228
     *
229
     * This method is a no-op for other objects.
230
     *
231
     * @param object $obj
232
     */
233
    public function initializeObject($obj)
234
    {
235
        $this->unitOfWork->initializeObject($obj);
236
    }
237
238
    /**
239
     * Gets the UnitOfWork used by the DocumentManager to coordinate operations.
240
     *
241
     * @return UnitOfWork
242
     */
243 1590
    public function getUnitOfWork()
244
    {
245 1590
        return $this->unitOfWork;
246
    }
247
248
    /**
249
     * Gets the Hydrator factory used by the DocumentManager to generate and get hydrators
250
     * for each type of document.
251
     *
252
     * @return HydratorFactory
253
     */
254 66
    public function getHydratorFactory()
255
    {
256 66
        return $this->hydratorFactory;
257
    }
258
259
    /**
260
     * Returns SchemaManager, used to create/drop indexes/collections/databases.
261
     *
262
     * @return SchemaManager
263
     */
264 19
    public function getSchemaManager()
265
    {
266 19
        return $this->schemaManager;
267
    }
268
269
    /**
270
     * Returns the metadata for a class.
271
     *
272
     * @param string $className The class name.
273
     * @return ClassMetadata
274
     * @internal Performance-sensitive method.
275
     */
276 1323
    public function getClassMetadata($className)
277
    {
278 1323
        return $this->metadataFactory->getMetadataFor(ltrim($className, '\\'));
279
    }
280
281
    /**
282
     * Returns the MongoDB instance for a class.
283
     *
284
     * @param string $className The class name.
285
     * @return Database
286
     */
287 1257
    public function getDocumentDatabase($className)
288
    {
289 1257
        $className = ltrim($className, '\\');
290
291 1257
        if (isset($this->documentDatabases[$className])) {
292 36
            return $this->documentDatabases[$className];
293
        }
294
295 1253
        $metadata = $this->metadataFactory->getMetadataFor($className);
296 1253
        $db = $metadata->getDatabase();
297 1253
        $db = $db ?: $this->config->getDefaultDB();
298 1253
        $db = $db ?: 'doctrine';
299 1253
        $this->documentDatabases[$className] = $this->client->selectDatabase($db);
300
301 1253
        return $this->documentDatabases[$className];
302
    }
303
304
    /**
305
     * Gets the array of instantiated document database instances.
306
     *
307
     * @return Database[]
308
     */
309
    public function getDocumentDatabases()
310
    {
311
        return $this->documentDatabases;
312
    }
313
314
    /**
315
     * Returns the MongoCollection instance for a class.
316
     *
317
     * @param string $className The class name.
318
     * @throws MongoDBException When the $className param is not mapped to a collection.
319
     * @return Collection
320
     */
321 1259
    public function getDocumentCollection($className)
322
    {
323 1259
        $className = ltrim($className, '\\');
324
325 1259
        $metadata = $this->metadataFactory->getMetadataFor($className);
326 1259
        $collectionName = $metadata->getCollection();
327
328 1259
        if (! $collectionName) {
329
            throw MongoDBException::documentNotMappedToCollection($className);
330
        }
331
332 1259
        if (! isset($this->documentCollections[$className])) {
333 1249
            $db = $this->getDocumentDatabase($className);
334
335 1249
            $options = [];
336 1249
            if ($metadata->readPreference !== null) {
0 ignored issues
show
Bug introduced by
Accessing readPreference 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...
337 3
                $options['readPreference'] = new ReadPreference($metadata->readPreference, $metadata->readPreferenceTags);
0 ignored issues
show
Bug introduced by
Accessing readPreference 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...
Bug introduced by
Accessing readPreferenceTags 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...
338
            }
339
340 1249
            $this->documentCollections[$className] = $db->selectCollection($collectionName, $options);
341
        }
342
343 1259
        return $this->documentCollections[$className];
344
    }
345
346
    /**
347
     * Gets the array of instantiated document collection instances.
348
     *
349
     * @return Collection[]
350
     */
351
    public function getDocumentCollections()
352
    {
353
        return $this->documentCollections;
354
    }
355
356
    /**
357
     * Create a new Query instance for a class.
358
     *
359
     * @param string $documentName The document class name.
360
     * @return Query\Builder
361
     */
362 180
    public function createQueryBuilder($documentName = null)
363
    {
364 180
        return new Query\Builder($this, $documentName);
365
    }
366
367
    /**
368
     * Creates a new aggregation builder instance for a class.
369
     *
370
     * @param string $documentName The document class name.
371
     * @return Aggregation\Builder
372
     */
373 41
    public function createAggregationBuilder($documentName)
374
    {
375 41
        return new Aggregation\Builder($this, $documentName);
376
    }
377
378
    /**
379
     * Tells the DocumentManager to make an instance managed and persistent.
380
     *
381
     * The document will be entered into the database at or before transaction
382
     * commit or as a result of the flush operation.
383
     *
384
     * NOTE: The persist operation always considers documents that are not yet known to
385
     * this DocumentManager as NEW. Do not pass detached documents to the persist operation.
386
     *
387
     * @param object $document The instance to make managed and persistent.
388
     * @throws \InvalidArgumentException When the given $document param is not an object.
389
     */
390 582
    public function persist($document)
391
    {
392 582
        if (! is_object($document)) {
393 1
            throw new \InvalidArgumentException(gettype($document));
394
        }
395 581
        $this->errorIfClosed();
396 580
        $this->unitOfWork->persist($document);
397 576
    }
398
399
    /**
400
     * Removes a document instance.
401
     *
402
     * A removed document will be removed from the database at or before transaction commit
403
     * or as a result of the flush operation.
404
     *
405
     * @param object $document The document instance to remove.
406
     * @throws \InvalidArgumentException When the $document param is not an object.
407
     */
408 25
    public function remove($document)
409
    {
410 25
        if (! is_object($document)) {
411 1
            throw new \InvalidArgumentException(gettype($document));
412
        }
413 24
        $this->errorIfClosed();
414 23
        $this->unitOfWork->remove($document);
415 23
    }
416
417
    /**
418
     * Refreshes the persistent state of a document from the database,
419
     * overriding any local changes that have not yet been persisted.
420
     *
421
     * @param object $document The document to refresh.
422
     * @throws \InvalidArgumentException When the given $document param is not an object.
423
     */
424 23
    public function refresh($document)
425
    {
426 23
        if (! is_object($document)) {
427 1
            throw new \InvalidArgumentException(gettype($document));
428
        }
429 22
        $this->errorIfClosed();
430 21
        $this->unitOfWork->refresh($document);
431 20
    }
432
433
    /**
434
     * Detaches a document from the DocumentManager, causing a managed document to
435
     * become detached.  Unflushed changes made to the document if any
436
     * (including removal of the document), will not be synchronized to the database.
437
     * Documents which previously referenced the detached document will continue to
438
     * reference it.
439
     *
440
     * @param object $document The document to detach.
441
     * @throws \InvalidArgumentException When the $document param is not an object.
442
     */
443 11
    public function detach($document)
444
    {
445 11
        if (! is_object($document)) {
446 1
            throw new \InvalidArgumentException(gettype($document));
447
        }
448 10
        $this->unitOfWork->detach($document);
449 10
    }
450
451
    /**
452
     * Merges the state of a detached document into the persistence context
453
     * of this DocumentManager and returns the managed copy of the document.
454
     * The document passed to merge will not become associated/managed with this DocumentManager.
455
     *
456
     * @param object $document The detached document to merge into the persistence context.
457
     * @throws LockException
458
     * @throws \InvalidArgumentException If the $document param is not an object.
459
     * @return object The managed copy of the document.
460
     */
461 14
    public function merge($document)
462
    {
463 14
        if (! is_object($document)) {
464 1
            throw new \InvalidArgumentException(gettype($document));
465
        }
466 13
        $this->errorIfClosed();
467 12
        return $this->unitOfWork->merge($document);
468
    }
469
470
    /**
471
     * Acquire a lock on the given document.
472
     *
473
     * @param object $document
474
     * @param int    $lockMode
475
     * @param int    $lockVersion
476
     * @throws \InvalidArgumentException
477
     */
478 8
    public function lock($document, $lockMode, $lockVersion = null)
479
    {
480 8
        if (! is_object($document)) {
481
            throw new \InvalidArgumentException(gettype($document));
482
        }
483 8
        $this->unitOfWork->lock($document, $lockMode, $lockVersion);
484 5
    }
485
486
    /**
487
     * Releases a lock on the given document.
488
     *
489
     * @param object $document
490
     * @throws \InvalidArgumentException If the $document param is not an object.
491
     */
492 1
    public function unlock($document)
493
    {
494 1
        if (! is_object($document)) {
495
            throw new \InvalidArgumentException(gettype($document));
496
        }
497 1
        $this->unitOfWork->unlock($document);
498 1
    }
499
500
    /**
501
     * Gets the repository for a document class.
502
     *
503
     * @param string $documentName The name of the Document.
504
     * @return ObjectRepository  The repository.
505
     */
506 328
    public function getRepository($documentName)
507
    {
508 328
        return $this->repositoryFactory->getRepository($this, $documentName);
509
    }
510
511
    /**
512
     * Flushes all changes to objects that have been queued up to now to the database.
513
     * This effectively synchronizes the in-memory state of managed objects with the
514
     * database.
515
     *
516
     * @param array $options Array of options to be used with batchInsert(), update() and remove()
517
     * @throws \InvalidArgumentException
518
     */
519 553
    public function flush(array $options = [])
520
    {
521 553
        $this->errorIfClosed();
522 552
        $this->unitOfWork->commit($options);
523 549
    }
524
525
    /**
526
     * Gets a reference to the document identified by the given type and identifier
527
     * without actually loading it.
528
     *
529
     * If partial objects are allowed, this method will return a partial object that only
530
     * has its identifier populated. Otherwise a proxy is returned that automatically
531
     * loads itself on first access.
532
     *
533
     * @param string        $documentName
534
     * @param string|object $identifier
535
     * @return mixed|object The document reference.
536
     */
537 126
    public function getReference($documentName, $identifier)
538
    {
539
        /** @var ClassMetadata $class */
540 126
        $class = $this->metadataFactory->getMetadataFor(ltrim($documentName, '\\'));
541 126
        $document = $this->unitOfWork->tryGetById($identifier, $class);
542
543
        // Check identity map first, if its already in there just return it.
544 126
        if ($document) {
545 56
            return $document;
546
        }
547
548 97
        $document = $this->proxyFactory->getProxy($class->name, [$class->identifier => $identifier]);
549 97
        $this->unitOfWork->registerManaged($document, $identifier, []);
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...
550
551 97
        return $document;
552
    }
553
554
    /**
555
     * Gets a partial reference to the document identified by the given type and identifier
556
     * without actually loading it, if the document is not yet loaded.
557
     *
558
     * The returned reference may be a partial object if the document is not yet loaded/managed.
559
     * If it is a partial object it will not initialize the rest of the document state on access.
560
     * Thus you can only ever safely access the identifier of a document obtained through
561
     * this method.
562
     *
563
     * The use-cases for partial references involve maintaining bidirectional associations
564
     * without loading one side of the association or to update a document without loading it.
565
     * Note, however, that in the latter case the original (persistent) document data will
566
     * never be visible to the application (especially not event listeners) as it will
567
     * never be loaded in the first place.
568
     *
569
     * @param string $documentName The name of the document type.
570
     * @param mixed  $identifier   The document identifier.
571
     * @return object The (partial) document reference.
572
     */
573 1
    public function getPartialReference($documentName, $identifier)
574
    {
575 1
        $class = $this->metadataFactory->getMetadataFor(ltrim($documentName, '\\'));
576 1
        $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...
577
578
        // Check identity map first, if its already in there just return it.
579 1
        if ($document) {
580
            return $document;
581
        }
582 1
        $document = $class->newInstance();
583 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...
584 1
        $this->unitOfWork->registerManaged($document, $identifier, []);
585
586 1
        return $document;
587
    }
588
589
    /**
590
     * Finds a Document by its identifier.
591
     *
592
     * This is just a convenient shortcut for getRepository($documentName)->find($id).
593
     *
594
     * @param string $documentName
595
     * @param mixed  $identifier
596
     * @param int    $lockMode
597
     * @param int    $lockVersion
598
     * @return object $document
599
     */
600 182
    public function find($documentName, $identifier, $lockMode = LockMode::NONE, $lockVersion = null)
601
    {
602 182
        return $this->getRepository($documentName)->find($identifier, $lockMode, $lockVersion);
0 ignored issues
show
Unused Code introduced by
The call to ObjectRepository::find() has too many arguments starting with $lockMode.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
603
    }
604
605
    /**
606
     * Clears the DocumentManager.
607
     *
608
     * All documents that are currently managed by this DocumentManager become
609
     * detached.
610
     *
611
     * @param string|null $documentName if given, only documents of this type will get detached
612
     */
613 371
    public function clear($documentName = null)
614
    {
615 371
        $this->unitOfWork->clear($documentName);
616 371
    }
617
618
    /**
619
     * Closes the DocumentManager. All documents that are currently managed
620
     * by this DocumentManager become detached. The DocumentManager may no longer
621
     * be used after it is closed.
622
     */
623 6
    public function close()
624
    {
625 6
        $this->clear();
626 6
        $this->closed = true;
627 6
    }
628
629
    /**
630
     * Determines whether a document instance is managed in this DocumentManager.
631
     *
632
     * @param object $document
633
     * @throws \InvalidArgumentException When the $document param is not an object.
634
     * @return bool TRUE if this DocumentManager currently manages the given document, FALSE otherwise.
635
     */
636 3
    public function contains($document)
637
    {
638 3
        if (! is_object($document)) {
639
            throw new \InvalidArgumentException(gettype($document));
640
        }
641 3
        return $this->unitOfWork->isScheduledForInsert($document) ||
642 3
            $this->unitOfWork->isInIdentityMap($document) &&
643 3
            ! $this->unitOfWork->isScheduledForDelete($document);
644
    }
645
646
    /**
647
     * Gets the Configuration used by the DocumentManager.
648
     *
649
     * @return Configuration
650
     */
651 721
    public function getConfiguration()
652
    {
653 721
        return $this->config;
654
    }
655
656
    /**
657
     * Returns a reference to the supplied document.
658
     *
659
     * @param object $document         A document object
660
     * @param array  $referenceMapping Mapping for the field that references the document
661
     *
662
     * @throws \InvalidArgumentException
663
     * @throws MappingException
664
     * @return mixed The reference for the document in question, according to the desired mapping
665
     */
666 224
    public function createReference($document, array $referenceMapping)
667
    {
668 224
        if (! is_object($document)) {
669
            throw new \InvalidArgumentException('Cannot create a DBRef, the document is not an object');
670
        }
671
672 224
        $class = $this->getClassMetadata(get_class($document));
673 224
        $id = $this->unitOfWork->getDocumentIdentifier($document);
674
675 224
        if ($id === null) {
676 1
            throw new \RuntimeException(
677 1
                sprintf('Cannot create a DBRef for class %s without an identifier. Have you forgotten to persist/merge the document first?', $class->name)
678
            );
679
        }
680
681 223
        $storeAs = $referenceMapping['storeAs'] ?? null;
682 223
        $reference = [];
0 ignored issues
show
Unused Code introduced by
$reference is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
683 223
        switch ($storeAs) {
684
            case ClassMetadata::REFERENCE_STORE_AS_ID:
685 46
                if ($class->inheritanceType === ClassMetadata::INHERITANCE_TYPE_SINGLE_COLLECTION) {
686 1
                    throw MappingException::simpleReferenceMustNotTargetDiscriminatedDocument($referenceMapping['targetDocument']);
687
                }
688
689 45
                return $class->getDatabaseIdentifierValue($id);
690
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
691
692
            case ClassMetadata::REFERENCE_STORE_AS_REF:
693 20
                $reference = ['id' => $class->getDatabaseIdentifierValue($id)];
694 20
                break;
695
696
            case ClassMetadata::REFERENCE_STORE_AS_DB_REF:
697
                $reference = [
698 179
                    '$ref' => $class->getCollection(),
699 179
                    '$id'  => $class->getDatabaseIdentifierValue($id),
700
                ];
701 179
                break;
702
703
            case ClassMetadata::REFERENCE_STORE_AS_DB_REF_WITH_DB:
704
                $reference = [
705 17
                    '$ref' => $class->getCollection(),
706 17
                    '$id'  => $class->getDatabaseIdentifierValue($id),
707 17
                    '$db'  => $this->getDocumentDatabase($class->name)->getDatabaseName(),
708
                ];
709 17
                break;
710
711
            default:
712
                throw new \InvalidArgumentException(sprintf('Reference type %s is invalid.', $storeAs));
713
        }
714
715
        /* If the class has a discriminator (field and value), use it. A child
716
         * class that is not defined in the discriminator map may only have a
717
         * discriminator field and no value, so default to the full class name.
718
         */
719 201
        if (isset($class->discriminatorField)) {
720 18
            $reference[$class->discriminatorField] = $class->discriminatorValue ?? $class->name;
721
        }
722
723
        /* Add a discriminator value if the referenced document is not mapped
724
         * explicitly to a targetDocument class.
725
         */
726 201
        if (! isset($referenceMapping['targetDocument'])) {
727 33
            $discriminatorField = $referenceMapping['discriminatorField'];
728 33
            $discriminatorValue = isset($referenceMapping['discriminatorMap'])
729 8
                ? array_search($class->name, $referenceMapping['discriminatorMap'])
730 33
                : $class->name;
731
732
            /* If the discriminator value was not found in the map, use the full
733
             * class name. In the future, it may be preferable to throw an
734
             * exception here (perhaps based on some strictness option).
735
             *
736
             * @see PersistenceBuilder::prepareEmbeddedDocumentValue()
737
             */
738 33
            if ($discriminatorValue === false) {
739 2
                $discriminatorValue = $class->name;
740
            }
741
742 33
            $reference[$discriminatorField] = $discriminatorValue;
743
        }
744
745 201
        return $reference;
746
    }
747
748
    /**
749
     * Throws an exception if the DocumentManager is closed or currently not active.
750
     *
751
     * @throws MongoDBException If the DocumentManager is closed.
752
     */
753 586
    private function errorIfClosed()
754
    {
755 586
        if ($this->closed) {
756 5
            throw MongoDBException::documentManagerClosed();
757
        }
758 581
    }
759
760
    /**
761
     * Check if the Document manager is open or closed.
762
     *
763
     * @return bool
764
     */
765 1
    public function isOpen()
766
    {
767 1
        return ! $this->closed;
768
    }
769
770
    /**
771
     * Gets the filter collection.
772
     *
773
     * @return FilterCollection The active filter collection.
774
     */
775 507
    public function getFilterCollection()
776
    {
777 507
        if ($this->filterCollection === null) {
778 507
            $this->filterCollection = new FilterCollection($this);
779
        }
780
781 507
        return $this->filterCollection;
782
    }
783
}
784