Completed
Pull Request — master (#1846)
by Catalin
16:48
created

DocumentManager::getRepository()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 1
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 MongoDB\GridFS\Bucket;
22
use function array_search;
23
use function get_class;
24
use function gettype;
25
use function is_object;
26
use function ltrim;
27
use function sprintf;
28
29
/**
30
 * The DocumentManager class is the central access point for managing the
31
 * persistence of documents.
32
 *
33
 *     <?php
34
 *
35
 *     $config = new Configuration();
36
 *     $dm = DocumentManager::create(new Connection(), $config);
37
 *
38
 */
39
class DocumentManager implements ObjectManager
40
{
41
    /**
42
     * The Doctrine MongoDB connection instance.
43
     *
44
     * @var Client
45
     */
46
    private $client;
47
48
    /**
49
     * The used Configuration.
50
     *
51
     * @var Configuration
52
     */
53
    private $config;
54
55
    /**
56
     * The metadata factory, used to retrieve the ODM metadata of document classes.
57
     *
58
     * @var ClassMetadataFactory
59
     */
60
    private $metadataFactory;
61
62
    /**
63
     * The UnitOfWork used to coordinate object-level transactions.
64
     *
65
     * @var UnitOfWork
66
     */
67
    private $unitOfWork;
68
69
    /**
70
     * The event manager that is the central point of the event system.
71
     *
72
     * @var EventManager
73
     */
74
    private $eventManager;
75
76
    /**
77
     * The Hydrator factory instance.
78
     *
79
     * @var HydratorFactory
80
     */
81
    private $hydratorFactory;
82
83
    /**
84
     * The Proxy factory instance.
85
     *
86
     * @var ProxyFactory
87
     */
88
    private $proxyFactory;
89
90
    /**
91
     * The repository factory used to create dynamic repositories.
92
     *
93
     * @var RepositoryFactory
94
     */
95
    private $repositoryFactory;
96
97
    /**
98
     * SchemaManager instance
99
     *
100
     * @var SchemaManager
101
     */
102
    private $schemaManager;
103
104
    /**
105
     * Array of cached document database instances that are lazily loaded.
106
     *
107
     * @var Database[]
108
     */
109
    private $documentDatabases = [];
110
111
    /**
112
     * Array of cached document collection instances that are lazily loaded.
113
     *
114
     * @var Collection[]
115
     */
116
    private $documentCollections = [];
117
118
    /**
119
     * Array of cached document bucket instances that are lazily loaded.
120
     *
121
     * @var Bucket[]
122
     */
123
    private $documentBuckets = [];
124
125
    /**
126
     * Whether the DocumentManager is closed or not.
127
     *
128
     * @var bool
129
     */
130
    private $closed = false;
131
132
    /**
133
     * Collection of query filters.
134
     *
135
     * @var FilterCollection
136
     */
137
    private $filterCollection;
138
139
    /**
140
     * Creates a new Document that operates on the given Mongo connection
141
     * and uses the given Configuration.
142
     *
143
     */
144 1602
    protected function __construct(?Client $client = null, ?Configuration $config = null, ?EventManager $eventManager = null)
145
    {
146 1602
        $this->config = $config ?: new Configuration();
147 1602
        $this->eventManager = $eventManager ?: new EventManager();
148 1602
        $this->client = $client ?: new Client('mongodb://127.0.0.1', [], ['typeMap' => ['root' => 'array', 'document' => 'array']]);
149
150 1602
        $metadataFactoryClassName = $this->config->getClassMetadataFactoryName();
151 1602
        $this->metadataFactory = new $metadataFactoryClassName();
152 1602
        $this->metadataFactory->setDocumentManager($this);
153 1602
        $this->metadataFactory->setConfiguration($this->config);
154
155 1602
        $cacheDriver = $this->config->getMetadataCacheImpl();
156 1602
        if ($cacheDriver) {
157
            $this->metadataFactory->setCacheDriver($cacheDriver);
158
        }
159
160 1602
        $hydratorDir = $this->config->getHydratorDir();
161 1602
        $hydratorNs = $this->config->getHydratorNamespace();
162 1602
        $this->hydratorFactory = new HydratorFactory(
163 1602
            $this,
164 1602
            $this->eventManager,
165 1602
            $hydratorDir,
166 1602
            $hydratorNs,
167 1602
            $this->config->getAutoGenerateHydratorClasses()
168
        );
169
170 1602
        $this->unitOfWork = new UnitOfWork($this, $this->eventManager, $this->hydratorFactory);
171 1602
        $this->hydratorFactory->setUnitOfWork($this->unitOfWork);
172 1602
        $this->schemaManager = new SchemaManager($this, $this->metadataFactory);
173 1602
        $this->proxyFactory = new ProxyFactory(
174 1602
            $this,
175 1602
            $this->config->getProxyDir(),
176 1602
            $this->config->getProxyNamespace(),
177 1602
            $this->config->getAutoGenerateProxyClasses()
178
        );
179 1602
        $this->repositoryFactory = $this->config->getRepositoryFactory();
180 1602
    }
181
182
    /**
183
     * Gets the proxy factory used by the DocumentManager to create document proxies.
184
     */
185 1
    public function getProxyFactory(): ProxyFactory
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 1602
    public static function create(?Client $client = null, ?Configuration $config = null, ?EventManager $eventManager = null): DocumentManager
195
    {
196 1602
        return new static($client, $config, $eventManager);
197
    }
198
199
    /**
200
     * Gets the EventManager used by the DocumentManager.
201
     */
202 1665
    public function getEventManager(): EventManager
203
    {
204 1665
        return $this->eventManager;
205
    }
206
207
    /**
208
     * Gets the MongoDB client instance that this DocumentManager wraps.
209
     */
210 1602
    public function getClient(): Client
211
    {
212 1602
        return $this->client;
213
    }
214
215
    /**
216
     * Gets the metadata factory used to gather the metadata of classes.
217
     *
218
     * @return ClassMetadataFactory
219
     */
220 1602
    public function getMetadataFactory()
221
    {
222 1602
        return $this->metadataFactory;
223
    }
224
225
    /**
226
     * Helper method to initialize a lazy loading proxy or persistent collection.
227
     *
228
     * This method is a no-op for other objects.
229
     *
230
     * @param object $obj
231
     */
232
    public function initializeObject($obj)
233
    {
234
        $this->unitOfWork->initializeObject($obj);
235
    }
236
237
    /**
238
     * Gets the UnitOfWork used by the DocumentManager to coordinate operations.
239
     */
240 1609
    public function getUnitOfWork(): UnitOfWork
241
    {
242 1609
        return $this->unitOfWork;
243
    }
244
245
    /**
246
     * Gets the Hydrator factory used by the DocumentManager to generate and get hydrators
247
     * for each type of document.
248
     */
249 69
    public function getHydratorFactory(): HydratorFactory
250
    {
251 69
        return $this->hydratorFactory;
252
    }
253
254
    /**
255
     * Returns SchemaManager, used to create/drop indexes/collections/databases.
256
     */
257 19
    public function getSchemaManager(): SchemaManager
258
    {
259 19
        return $this->schemaManager;
260
    }
261
262
    /**
263
     * Returns the metadata for a class.
264
     *
265
     * @param string $className The class name.
266
     * @return ClassMetadata
267
     * @internal Performance-sensitive method.
268
     */
269 1340
    public function getClassMetadata($className)
270
    {
271 1340
        return $this->metadataFactory->getMetadataFor(ltrim($className, '\\'));
272
    }
273
274
    /**
275
     * Returns the MongoDB instance for a class.
276
     */
277 1269
    public function getDocumentDatabase(string $className): Database
278
    {
279 1269
        $className = ltrim($className, '\\');
280
281 1269
        if (isset($this->documentDatabases[$className])) {
282 37
            return $this->documentDatabases[$className];
283
        }
284
285 1264
        $metadata = $this->metadataFactory->getMetadataFor($className);
286 1264
        $db = $metadata->getDatabase();
287 1264
        $db = $db ?: $this->config->getDefaultDB();
288 1264
        $db = $db ?: 'doctrine';
289 1264
        $this->documentDatabases[$className] = $this->client->selectDatabase($db);
290
291 1264
        return $this->documentDatabases[$className];
292
    }
293
294
    /**
295
     * Gets the array of instantiated document database instances.
296
     *
297
     * @return Database[]
298
     */
299
    public function getDocumentDatabases(): array
300
    {
301
        return $this->documentDatabases;
302
    }
303
304
    /**
305
     * Returns the collection instance for a class.
306
     *
307
     * @throws MongoDBException When the $className param is not mapped to a collection.
308
     */
309 1272
    public function getDocumentCollection(string $className): Collection
310
    {
311 1272
        $className = ltrim($className, '\\');
312
313
        /** @var ClassMetadata $metadata */
314 1272
        $metadata = $this->metadataFactory->getMetadataFor($className);
315 1272
        if ($metadata->isFile) {
316 16
            return $this->getDocumentBucket($className)->getFilesCollection();
317
        }
318
319 1261
        $collectionName = $metadata->getCollection();
320
321 1261
        if (! $collectionName) {
322
            throw MongoDBException::documentNotMappedToCollection($className);
323
        }
324
325 1261
        if (! isset($this->documentCollections[$className])) {
326 1251
            $db = $this->getDocumentDatabase($className);
327
328 1251
            $options = [];
329 1251
            if ($metadata->readPreference !== null) {
330 3
                $options['readPreference'] = new ReadPreference($metadata->readPreference, $metadata->readPreferenceTags);
331
            }
332
333 1251
            $this->documentCollections[$className] = $db->selectCollection($collectionName, $options);
334
        }
335
336 1261
        return $this->documentCollections[$className];
337
    }
338
339
    /**
340
     * Returns the bucket instance for a class.
341
     *
342
     * @throws MongoDBException When the $className param is not mapped to a collection.
343
     */
344 16
    public function getDocumentBucket(string $className): Bucket
345
    {
346 16
        $className = ltrim($className, '\\');
347
348
        /** @var ClassMetadata $metadata */
349 16
        $metadata = $this->metadataFactory->getMetadataFor($className);
350 16
        if (! $metadata->isFile) {
351
            throw MongoDBException::documentBucketOnlyAvailableForGridFSFiles($className);
352
        }
353
354 16
        $bucketName = $metadata->getBucketName();
355
356 16
        if (! $bucketName) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $bucketName of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
357
            throw MongoDBException::documentNotMappedToCollection($className);
358
        }
359
360 16
        if (! isset($this->documentBuckets[$className])) {
361 11
            $db = $this->getDocumentDatabase($className);
362
363 11
            $options = ['bucketName' => $bucketName];
364 11
            if ($metadata->readPreference !== null) {
365
                $options['readPreference'] = new ReadPreference($metadata->readPreference, $metadata->readPreferenceTags);
366
            }
367
368 11
            $this->documentBuckets[$className] = $db->selectGridFSBucket($options);
369
        }
370
371 16
        return $this->documentBuckets[$className];
372
    }
373
374
    /**
375
     * Gets the array of instantiated document collection instances.
376
     *
377
     * @return Collection[]
378
     */
379
    public function getDocumentCollections(): array
380
    {
381
        return $this->documentCollections;
382
    }
383
384
    /**
385
     * Create a new Query instance for a class.
386
     *
387
     * @param string[]|string|null $documentName (optional) an array of document names, the document name, or none
388
     */
389 182
    public function createQueryBuilder($documentName = null): Query\Builder
390
    {
391 182
        return new Query\Builder($this, $documentName);
392
    }
393
394
    /**
395
     * Creates a new aggregation builder instance for a class.
396
     */
397 41
    public function createAggregationBuilder(string $documentName): Aggregation\Builder
398
    {
399 41
        return new Aggregation\Builder($this, $documentName);
400
    }
401
402
    /**
403
     * Tells the DocumentManager to make an instance managed and persistent.
404
     *
405
     * The document will be entered into the database at or before transaction
406
     * commit or as a result of the flush operation.
407
     *
408
     * NOTE: The persist operation always considers documents that are not yet known to
409
     * this DocumentManager as NEW. Do not pass detached documents to the persist operation.
410
     *
411
     * @param object $document The instance to make managed and persistent.
412
     * @throws \InvalidArgumentException When the given $document param is not an object.
413
     */
414 585
    public function persist($document)
415
    {
416 585
        if (! is_object($document)) {
417 1
            throw new \InvalidArgumentException(gettype($document));
418
        }
419 584
        $this->errorIfClosed();
420 583
        $this->unitOfWork->persist($document);
421 579
    }
422
423
    /**
424
     * Removes a document instance.
425
     *
426
     * A removed document will be removed from the database at or before transaction commit
427
     * or as a result of the flush operation.
428
     *
429
     * @param object $document The document instance to remove.
430
     * @throws \InvalidArgumentException When the $document param is not an object.
431
     */
432 26
    public function remove($document)
433
    {
434 26
        if (! is_object($document)) {
435 1
            throw new \InvalidArgumentException(gettype($document));
436
        }
437 25
        $this->errorIfClosed();
438 24
        $this->unitOfWork->remove($document);
439 24
    }
440
441
    /**
442
     * Refreshes the persistent state of a document from the database,
443
     * overriding any local changes that have not yet been persisted.
444
     *
445
     * @param object $document The document to refresh.
446
     * @throws \InvalidArgumentException When the given $document param is not an object.
447
     */
448 25
    public function refresh($document)
449
    {
450 25
        if (! is_object($document)) {
451 1
            throw new \InvalidArgumentException(gettype($document));
452
        }
453 24
        $this->errorIfClosed();
454 23
        $this->unitOfWork->refresh($document);
455 22
    }
456
457
    /**
458
     * Detaches a document from the DocumentManager, causing a managed document to
459
     * become detached.  Unflushed changes made to the document if any
460
     * (including removal of the document), will not be synchronized to the database.
461
     * Documents which previously referenced the detached document will continue to
462
     * reference it.
463
     *
464
     * @param object $document The document to detach.
465
     * @throws \InvalidArgumentException When the $document param is not an object.
466
     */
467 11
    public function detach($document)
468
    {
469 11
        if (! is_object($document)) {
470 1
            throw new \InvalidArgumentException(gettype($document));
471
        }
472 10
        $this->unitOfWork->detach($document);
473 10
    }
474
475
    /**
476
     * Merges the state of a detached document into the persistence context
477
     * of this DocumentManager and returns the managed copy of the document.
478
     * The document passed to merge will not become associated/managed with this DocumentManager.
479
     *
480
     * @param object $document The detached document to merge into the persistence context.
481
     * @throws LockException
482
     * @throws \InvalidArgumentException If the $document param is not an object.
483
     * @return object The managed copy of the document.
484
     */
485 14
    public function merge($document)
486
    {
487 14
        if (! is_object($document)) {
488 1
            throw new \InvalidArgumentException(gettype($document));
489
        }
490 13
        $this->errorIfClosed();
491 12
        return $this->unitOfWork->merge($document);
492
    }
493
494
    /**
495
     * Acquire a lock on the given document.
496
     *
497
     * @throws \InvalidArgumentException
498
     * @throws LockException
499
     */
500 8
    public function lock(object $document, int $lockMode, ?int $lockVersion = null): void
501
    {
502 8
        $this->unitOfWork->lock($document, $lockMode, $lockVersion);
503 5
    }
504
505
    /**
506
     * Releases a lock on the given document.
507
     */
508 1
    public function unlock(object $document): void
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 ObjectRepository  The repository.
518
     */
519 337
    public function getRepository($documentName)
520
    {
521 337
        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 array $options Array of options to be used with batchInsert(), update() and remove()
530
     * @throws \InvalidArgumentException
531
     */
532 557
    public function flush(array $options = [])
533
    {
534 557
        $this->errorIfClosed();
535 556
        $this->unitOfWork->commit($options);
536 553
    }
537
538
    /**
539
     * Gets a reference to the document identified by the given type and identifier
540
     * without actually loading it.
541
     *
542
     * If partial objects are allowed, this method will return a partial object that only
543
     * has its identifier populated. Otherwise a proxy is returned that automatically
544
     * loads itself on first access.
545
     *
546
     * @param string|object $identifier
547
     * @return mixed|object The document reference.
548
     */
549 133
    public function getReference(string $documentName, $identifier)
550
    {
551
        /** @var ClassMetadata $class */
552 133
        $class = $this->metadataFactory->getMetadataFor(ltrim($documentName, '\\'));
553 133
        $document = $this->unitOfWork->tryGetById($identifier, $class);
554
555
        // Check identity map first, if its already in there just return it.
556 133
        if ($document) {
557 56
            return $document;
558
        }
559
560 104
        $document = $this->proxyFactory->getProxy($class->name, [$class->identifier => $identifier]);
561 104
        $this->unitOfWork->registerManaged($document, $identifier, []);
562
563 104
        return $document;
564
    }
565
566
    /**
567
     * Gets a partial reference to the document identified by the given type and identifier
568
     * without actually loading it, if the document is not yet loaded.
569
     *
570
     * The returned reference may be a partial object if the document is not yet loaded/managed.
571
     * If it is a partial object it will not initialize the rest of the document state on access.
572
     * Thus you can only ever safely access the identifier of a document obtained through
573
     * this method.
574
     *
575
     * The use-cases for partial references involve maintaining bidirectional associations
576
     * without loading one side of the association or to update a document without loading it.
577
     * Note, however, that in the latter case the original (persistent) document data will
578
     * never be visible to the application (especially not event listeners) as it will
579
     * never be loaded in the first place.
580
     *
581
     * @param mixed $identifier The document identifier.
582
     * @return object The (partial) document reference.
583
     */
584 1
    public function getPartialReference(string $documentName, $identifier)
585
    {
586 1
        $class = $this->metadataFactory->getMetadataFor(ltrim($documentName, '\\'));
587 1
        $document = $this->unitOfWork->tryGetById($identifier, $class);
588
589
        // Check identity map first, if its already in there just return it.
590 1
        if ($document) {
591
            return $document;
592
        }
593 1
        $document = $class->newInstance();
594 1
        $class->setIdentifierValue($document, $identifier);
595 1
        $this->unitOfWork->registerManaged($document, $identifier, []);
596
597 1
        return $document;
598
    }
599
600
    /**
601
     * Finds a Document by its identifier.
602
     *
603
     * This is just a convenient shortcut for getRepository($documentName)->find($id).
604
     *
605
     * @param string $documentName
606
     * @param mixed  $identifier
607
     * @param int    $lockMode
608
     * @param int    $lockVersion
609
     * @return object $document
610
     */
611 182
    public function find($documentName, $identifier, $lockMode = LockMode::NONE, $lockVersion = null)
612
    {
613 182
        return $this->getRepository($documentName)->find($identifier, $lockMode, $lockVersion);
614
    }
615
616
    /**
617
     * Clears the DocumentManager.
618
     *
619
     * All documents that are currently managed by this DocumentManager become
620
     * detached.
621
     *
622
     * @param string|null $documentName if given, only documents of this type will get detached
623
     */
624 370
    public function clear($documentName = null)
625
    {
626 370
        $this->unitOfWork->clear($documentName);
627 370
    }
628
629
    /**
630
     * Closes the DocumentManager. All documents that are currently managed
631
     * by this DocumentManager become detached. The DocumentManager may no longer
632
     * be used after it is closed.
633
     */
634 6
    public function close()
635
    {
636 6
        $this->clear();
637 6
        $this->closed = true;
638 6
    }
639
640
    /**
641
     * Determines whether a document instance is managed in this DocumentManager.
642
     *
643
     * @param object $document
644
     * @throws \InvalidArgumentException When the $document param is not an object.
645
     * @return bool TRUE if this DocumentManager currently manages the given document, FALSE otherwise.
646
     */
647 3
    public function contains($document)
648
    {
649 3
        if (! is_object($document)) {
650
            throw new \InvalidArgumentException(gettype($document));
651
        }
652 3
        return $this->unitOfWork->isScheduledForInsert($document) ||
653 3
            $this->unitOfWork->isInIdentityMap($document) &&
654 3
            ! $this->unitOfWork->isScheduledForDelete($document);
655
    }
656
657
    /**
658
     * Gets the Configuration used by the DocumentManager.
659
     */
660 753
    public function getConfiguration(): Configuration
661
    {
662 753
        return $this->config;
663
    }
664
665
    /**
666
     * Returns a reference to the supplied document.
667
     *
668
     * @throws MappingException
669
     * @return mixed The reference for the document in question, according to the desired mapping
670
     */
671 225
    public function createReference(object $document, array $referenceMapping)
672
    {
673 225
        $class = $this->getClassMetadata(get_class($document));
674 225
        $id = $this->unitOfWork->getDocumentIdentifier($document);
675
676 225
        if ($id === null) {
677 1
            throw new \RuntimeException(
678 1
                sprintf('Cannot create a DBRef for class %s without an identifier. Have you forgotten to persist/merge the document first?', $class->name)
679
            );
680
        }
681
682 224
        $storeAs = $referenceMapping['storeAs'] ?? null;
683 224
        $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...
684 224
        switch ($storeAs) {
685
            case ClassMetadata::REFERENCE_STORE_AS_ID:
686 46
                if ($class->inheritanceType === ClassMetadata::INHERITANCE_TYPE_SINGLE_COLLECTION) {
687 1
                    throw MappingException::simpleReferenceMustNotTargetDiscriminatedDocument($referenceMapping['targetDocument']);
688
                }
689
690 45
                return $class->getDatabaseIdentifierValue($id);
691
                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...
692
693
            case ClassMetadata::REFERENCE_STORE_AS_REF:
694 20
                $reference = ['id' => $class->getDatabaseIdentifierValue($id)];
695 20
                break;
696
697
            case ClassMetadata::REFERENCE_STORE_AS_DB_REF:
698
                $reference = [
699 180
                    '$ref' => $class->getCollection(),
700 180
                    '$id'  => $class->getDatabaseIdentifierValue($id),
701
                ];
702 180
                break;
703
704
            case ClassMetadata::REFERENCE_STORE_AS_DB_REF_WITH_DB:
705
                $reference = [
706 17
                    '$ref' => $class->getCollection(),
707 17
                    '$id'  => $class->getDatabaseIdentifierValue($id),
708 17
                    '$db'  => $this->getDocumentDatabase($class->name)->getDatabaseName(),
709
                ];
710 17
                break;
711
712
            default:
713
                throw new \InvalidArgumentException(sprintf('Reference type %s is invalid.', $storeAs));
714
        }
715
716
        /* If the class has a discriminator (field and value), use it. A child
717
         * class that is not defined in the discriminator map may only have a
718
         * discriminator field and no value, so default to the full class name.
719
         */
720 202
        if (isset($class->discriminatorField)) {
721 18
            $reference[$class->discriminatorField] = $class->discriminatorValue ?? $class->name;
722
        }
723
724
        /* Add a discriminator value if the referenced document is not mapped
725
         * explicitly to a targetDocument class.
726
         */
727 202
        if (! isset($referenceMapping['targetDocument'])) {
728 33
            $discriminatorField = $referenceMapping['discriminatorField'];
729 33
            $discriminatorValue = isset($referenceMapping['discriminatorMap'])
730 8
                ? array_search($class->name, $referenceMapping['discriminatorMap'])
731 33
                : $class->name;
732
733
            /* If the discriminator value was not found in the map, use the full
734
             * class name. In the future, it may be preferable to throw an
735
             * exception here (perhaps based on some strictness option).
736
             *
737
             * @see PersistenceBuilder::prepareEmbeddedDocumentValue()
738
             */
739 33
            if ($discriminatorValue === false) {
740 3
                $discriminatorValue = $class->name;
741
            }
742
743 33
            $reference[$discriminatorField] = $discriminatorValue;
744
        }
745
746 202
        return $reference;
747
    }
748
749
    /**
750
     * Throws an exception if the DocumentManager is closed or currently not active.
751
     *
752
     * @throws MongoDBException If the DocumentManager is closed.
753
     */
754 590
    private function errorIfClosed(): void
755
    {
756 590
        if ($this->closed) {
757 5
            throw MongoDBException::documentManagerClosed();
758
        }
759 585
    }
760
761
    /**
762
     * Check if the Document manager is open or closed.
763
     *
764
     */
765 1
    public function isOpen(): bool
766
    {
767 1
        return ! $this->closed;
768
    }
769
770
    /**
771
     * Gets the filter collection.
772
     */
773 517
    public function getFilterCollection(): FilterCollection
774
    {
775 517
        if ($this->filterCollection === null) {
776 517
            $this->filterCollection = new FilterCollection($this);
777
        }
778
779 517
        return $this->filterCollection;
780
    }
781
}
782