Completed
Push — master ( a8fe50...bce26f )
by Maciej
13s
created

lib/Doctrine/ODM/MongoDB/DocumentManager.php (4 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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 1605
    protected function __construct(?Client $client = null, ?Configuration $config = null, ?EventManager $eventManager = null)
145
    {
146 1605
        $this->config = $config ?: new Configuration();
147 1605
        $this->eventManager = $eventManager ?: new EventManager();
148 1605
        $this->client = $client ?: new Client('mongodb://127.0.0.1', [], ['typeMap' => ['root' => 'array', 'document' => 'array']]);
149
150 1605
        $metadataFactoryClassName = $this->config->getClassMetadataFactoryName();
151 1605
        $this->metadataFactory = new $metadataFactoryClassName();
152 1605
        $this->metadataFactory->setDocumentManager($this);
153 1605
        $this->metadataFactory->setConfiguration($this->config);
154
155 1605
        $cacheDriver = $this->config->getMetadataCacheImpl();
156 1605
        if ($cacheDriver) {
157
            $this->metadataFactory->setCacheDriver($cacheDriver);
158
        }
159
160 1605
        $hydratorDir = $this->config->getHydratorDir();
161 1605
        $hydratorNs = $this->config->getHydratorNamespace();
162 1605
        $this->hydratorFactory = new HydratorFactory(
163 1605
            $this,
164 1605
            $this->eventManager,
165 1605
            $hydratorDir,
166 1605
            $hydratorNs,
167 1605
            $this->config->getAutoGenerateHydratorClasses()
168
        );
169
170 1605
        $this->unitOfWork = new UnitOfWork($this, $this->eventManager, $this->hydratorFactory);
171 1605
        $this->hydratorFactory->setUnitOfWork($this->unitOfWork);
172 1605
        $this->schemaManager = new SchemaManager($this, $this->metadataFactory);
173 1605
        $this->proxyFactory = new ProxyFactory(
174 1605
            $this,
175 1605
            $this->config->getProxyDir(),
176 1605
            $this->config->getProxyNamespace(),
177 1605
            $this->config->getAutoGenerateProxyClasses()
178
        );
179 1605
        $this->repositoryFactory = $this->config->getRepositoryFactory();
180 1605
    }
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 1605
    public static function create(?Client $client = null, ?Configuration $config = null, ?EventManager $eventManager = null): DocumentManager
195
    {
196 1605
        return new static($client, $config, $eventManager);
197
    }
198
199
    /**
200
     * Gets the EventManager used by the DocumentManager.
201
     */
202 1668
    public function getEventManager(): EventManager
203
    {
204 1668
        return $this->eventManager;
205
    }
206
207
    /**
208
     * Gets the MongoDB client instance that this DocumentManager wraps.
209
     */
210 1605
    public function getClient(): Client
211
    {
212 1605
        return $this->client;
213
    }
214
215
    /**
216
     * Gets the metadata factory used to gather the metadata of classes.
217
     *
218
     * @return ClassMetadataFactory
219
     */
220 1605
    public function getMetadataFactory()
221
    {
222 1605
        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 1612
    public function getUnitOfWork(): UnitOfWork
241
    {
242 1612
        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 22
    public function getSchemaManager(): SchemaManager
258
    {
259 22
        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 1343
    public function getClassMetadata($className)
270
    {
271 1343
        return $this->metadataFactory->getMetadataFor(ltrim($className, '\\'));
272
    }
273
274
    /**
275
     * Returns the MongoDB instance for a class.
276
     */
277 1272
    public function getDocumentDatabase(string $className): Database
278
    {
279 1272
        $className = ltrim($className, '\\');
280
281 1272
        if (isset($this->documentDatabases[$className])) {
282 40
            return $this->documentDatabases[$className];
283
        }
284
285 1267
        $metadata = $this->metadataFactory->getMetadataFor($className);
286 1267
        $db = $metadata->getDatabase();
287 1267
        $db = $db ?: $this->config->getDefaultDB();
288 1267
        $db = $db ?: 'doctrine';
289 1267
        $this->documentDatabases[$className] = $this->client->selectDatabase($db);
290
291 1267
        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 1275
    public function getDocumentCollection(string $className): Collection
310
    {
311 1275
        $className = ltrim($className, '\\');
312
313
        /** @var ClassMetadata $metadata */
314 1275
        $metadata = $this->metadataFactory->getMetadataFor($className);
315 1275
        if ($metadata->isFile) {
316 16
            return $this->getDocumentBucket($className)->getFilesCollection();
317
        }
318
319 1264
        $collectionName = $metadata->getCollection();
320
321 1264
        if (! $collectionName) {
322
            throw MongoDBException::documentNotMappedToCollection($className);
323
        }
324
325 1264
        if (! isset($this->documentCollections[$className])) {
326 1254
            $db = $this->getDocumentDatabase($className);
327
328 1254
            $options = [];
329 1254
            if ($metadata->readPreference !== null) {
330 3
                $options['readPreference'] = new ReadPreference($metadata->readPreference, $metadata->readPreferenceTags);
331
            }
332
333 1254
            $this->documentCollections[$className] = $db->selectCollection($collectionName, $options);
334
        }
335
336 1264
        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) {
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 587
    public function persist($document)
415
    {
416 587
        if (! is_object($document)) {
417 1
            throw new \InvalidArgumentException(gettype($document));
418
        }
419 586
        $this->errorIfClosed();
420 585
        $this->unitOfWork->persist($document);
421 581
    }
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 MongoDBException
531
     */
532 559
    public function flush(array $options = [])
533
    {
534 559
        $this->errorIfClosed();
535 558
        $this->unitOfWork->commit($options);
536 555
    }
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
     */
548 133
    public function getReference(string $documentName, $identifier): object
549
    {
550
        /** @var ClassMetadata $class */
551 133
        $class = $this->metadataFactory->getMetadataFor(ltrim($documentName, '\\'));
552 133
        $document = $this->unitOfWork->tryGetById($identifier, $class);
553
554
        // Check identity map first, if its already in there just return it.
555 133
        if ($document) {
556 56
            return $document;
557
        }
558
559 104
        $document = $this->proxyFactory->getProxy($class->name, [$class->identifier => $identifier]);
560 104
        $this->unitOfWork->registerManaged($document, $identifier, []);
0 ignored issues
show
$document is of type object<Doctrine\Common\Proxy\Proxy>, but the function expects a object<Doctrine\ODM\MongoDB\object>.

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...
561
562 104
        return $document;
563
    }
564
565
    /**
566
     * Gets a partial reference to the document identified by the given type and identifier
567
     * without actually loading it, if the document is not yet loaded.
568
     *
569
     * The returned reference may be a partial object if the document is not yet loaded/managed.
570
     * If it is a partial object it will not initialize the rest of the document state on access.
571
     * Thus you can only ever safely access the identifier of a document obtained through
572
     * this method.
573
     *
574
     * The use-cases for partial references involve maintaining bidirectional associations
575
     * without loading one side of the association or to update a document without loading it.
576
     * Note, however, that in the latter case the original (persistent) document data will
577
     * never be visible to the application (especially not event listeners) as it will
578
     * never be loaded in the first place.
579
     *
580
     * @param mixed $identifier The document identifier.
581
     */
582 1
    public function getPartialReference(string $documentName, $identifier): object
583
    {
584 1
        $class = $this->metadataFactory->getMetadataFor(ltrim($documentName, '\\'));
585 1
        $document = $this->unitOfWork->tryGetById($identifier, $class);
0 ignored issues
show
$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...
586
587
        // Check identity map first, if its already in there just return it.
588 1
        if ($document) {
589
            return $document;
590
        }
591 1
        $document = $class->newInstance();
592 1
        $class->setIdentifierValue($document, $identifier);
0 ignored issues
show
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...
593 1
        $this->unitOfWork->registerManaged($document, $identifier, []);
594
595 1
        return $document;
596
    }
597
598
    /**
599
     * Finds a Document by its identifier.
600
     *
601
     * This is just a convenient shortcut for getRepository($documentName)->find($id).
602
     *
603
     * @param string $documentName
604
     * @param mixed  $identifier
605
     * @param int    $lockMode
606
     * @param int    $lockVersion
607
     * @return object $document
608
     */
609 182
    public function find($documentName, $identifier, $lockMode = LockMode::NONE, $lockVersion = null)
610
    {
611 182
        return $this->getRepository($documentName)->find($identifier, $lockMode, $lockVersion);
0 ignored issues
show
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...
612
    }
613
614
    /**
615
     * Clears the DocumentManager.
616
     *
617
     * All documents that are currently managed by this DocumentManager become
618
     * detached.
619
     *
620
     * @param string|null $documentName if given, only documents of this type will get detached
621
     */
622 370
    public function clear($documentName = null)
623
    {
624 370
        $this->unitOfWork->clear($documentName);
625 370
    }
626
627
    /**
628
     * Closes the DocumentManager. All documents that are currently managed
629
     * by this DocumentManager become detached. The DocumentManager may no longer
630
     * be used after it is closed.
631
     */
632 6
    public function close()
633
    {
634 6
        $this->clear();
635 6
        $this->closed = true;
636 6
    }
637
638
    /**
639
     * Determines whether a document instance is managed in this DocumentManager.
640
     *
641
     * @param object $document
642
     * @throws \InvalidArgumentException When the $document param is not an object.
643
     * @return bool TRUE if this DocumentManager currently manages the given document, FALSE otherwise.
644
     */
645 3
    public function contains($document)
646
    {
647 3
        if (! is_object($document)) {
648
            throw new \InvalidArgumentException(gettype($document));
649
        }
650 3
        return $this->unitOfWork->isScheduledForInsert($document) ||
651 3
            $this->unitOfWork->isInIdentityMap($document) &&
652 3
            ! $this->unitOfWork->isScheduledForDelete($document);
653
    }
654
655
    /**
656
     * Gets the Configuration used by the DocumentManager.
657
     */
658 755
    public function getConfiguration(): Configuration
659
    {
660 755
        return $this->config;
661
    }
662
663
    /**
664
     * Returns a reference to the supplied document.
665
     *
666
     * @throws MappingException
667
     * @throws \RuntimeException
668
     * @return mixed The reference for the document in question, according to the desired mapping
669
     */
670 225
    public function createReference(object $document, array $referenceMapping)
671
    {
672 225
        $class = $this->getClassMetadata(get_class($document));
673 225
        $id = $this->unitOfWork->getDocumentIdentifier($document);
674
675 225
        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 224
        $storeAs = $referenceMapping['storeAs'] ?? null;
682 224
        $reference = [];
683 224
        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;
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 180
                    '$ref' => $class->getCollection(),
699 180
                    '$id'  => $class->getDatabaseIdentifierValue($id),
700
                ];
701 180
                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 202
        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 202
        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 3
                $discriminatorValue = $class->name;
740
            }
741
742 33
            $reference[$discriminatorField] = $discriminatorValue;
743
        }
744
745 202
        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 592
    private function errorIfClosed(): void
754
    {
755 592
        if ($this->closed) {
756 5
            throw MongoDBException::documentManagerClosed();
757
        }
758 587
    }
759
760
    /**
761
     * Check if the Document manager is open or closed.
762
     *
763
     */
764 1
    public function isOpen(): bool
765
    {
766 1
        return ! $this->closed;
767
    }
768
769
    /**
770
     * Gets the filter collection.
771
     */
772 517
    public function getFilterCollection(): FilterCollection
773
    {
774 517
        if ($this->filterCollection === null) {
775 517
            $this->filterCollection = new FilterCollection($this);
776
        }
777
778 517
        return $this->filterCollection;
779
    }
780
}
781