Completed
Push — master ( 4b5952...b8f7dd )
by Andreas
11s
created

DocumentManager::__construct()   A

Complexity

Conditions 5
Paths 16

Size

Total Lines 35

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 26
CRAP Score 5.0012

Importance

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