EntityManager::getUnitOfWork()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\ORM;
6
7
use BadMethodCallException;
8
use Doctrine\Common\EventManager;
9
use Doctrine\Common\Persistence\Mapping\MappingException;
10
use Doctrine\Common\Persistence\ObjectRepository;
11
use Doctrine\DBAL\Connection;
12
use Doctrine\DBAL\DriverManager;
13
use Doctrine\DBAL\LockMode;
14
use Doctrine\ORM\Exception\EntityManagerClosed;
15
use Doctrine\ORM\Exception\InvalidHydrationMode;
16
use Doctrine\ORM\Exception\MismatchedEventManager;
17
use Doctrine\ORM\Exception\MissingIdentifierField;
18
use Doctrine\ORM\Exception\MissingMappingDriverImplementation;
19
use Doctrine\ORM\Exception\UnrecognizedIdentifierFields;
20
use Doctrine\ORM\Mapping\ClassMetadata;
21
use Doctrine\ORM\Mapping\ClassMetadataFactory;
22
use Doctrine\ORM\Proxy\Factory\ProxyFactory;
23
use Doctrine\ORM\Proxy\Factory\StaticProxyFactory;
24
use Doctrine\ORM\Query\Expr;
25
use Doctrine\ORM\Query\FilterCollection;
26
use Doctrine\ORM\Query\ResultSetMapping;
27
use Doctrine\ORM\Repository\RepositoryFactory;
28
use Doctrine\ORM\Utility\IdentifierFlattener;
29
use Doctrine\ORM\Utility\StaticClassNameConverter;
30
use InvalidArgumentException;
31
use ReflectionException;
32
use Throwable;
33
use function array_keys;
34
use function get_class;
35
use function gettype;
36
use function is_array;
37
use function is_object;
38
use function ltrim;
39
use function sprintf;
40
41
/**
42
 * The EntityManager is the central access point to ORM functionality.
43
 *
44
 * It is a facade to all different ORM subsystems such as UnitOfWork,
45
 * Query Language and Repository API. Instantiation is done through
46
 * the static create() method. The quickest way to obtain a fully
47
 * configured EntityManager is:
48
 *
49
 *     use Doctrine\ORM\Tools\Setup;
50
 *     use Doctrine\ORM\EntityManager;
51
 *
52
 *     $paths = array('/path/to/entity/mapping/files');
53
 *
54
 *     $config = Setup::createAnnotationMetadataConfiguration($paths);
55
 *     $dbParams = array('driver' => 'pdo_sqlite', 'memory' => true);
56
 *     $entityManager = EntityManager::create($dbParams, $config);
57
 *
58
 * For more information see
59
 * {@link http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/configuration.html}
60
 *
61
 * You should never attempt to inherit from the EntityManager: Inheritance
62
 * is not a valid extension point for the EntityManager. Instead you
63
 * should take a look at the {@see \Doctrine\ORM\Decorator\EntityManagerDecorator}
64
 * and wrap your entity manager in a decorator.
65
 */
66
final class EntityManager implements EntityManagerInterface
67
{
68
    /**
69
     * The used Configuration.
70
     *
71
     * @var Configuration
72
     */
73
    private $config;
74
75
    /**
76
     * The database connection used by the EntityManager.
77
     *
78
     * @var Connection
79
     */
80
    private $conn;
81
82
    /**
83
     * The metadata factory, used to retrieve the ORM metadata of entity classes.
84
     *
85
     * @var ClassMetadataFactory
86
     */
87
    private $metadataFactory;
88
89
    /**
90
     * The UnitOfWork used to coordinate object-level transactions.
91
     *
92
     * @var UnitOfWork
93
     */
94
    private $unitOfWork;
95
96
    /**
97
     * The event manager that is the central point of the event system.
98
     *
99
     * @var EventManager
100
     */
101
    private $eventManager;
102
103
    /**
104
     * The proxy factory used to create dynamic proxies.
105
     *
106
     * @var ProxyFactory
107
     */
108
    private $proxyFactory;
109
110
    /**
111
     * The repository factory used to create dynamic repositories.
112
     *
113
     * @var RepositoryFactory
114
     */
115
    private $repositoryFactory;
116
117
    /**
118
     * The expression builder instance used to generate query expressions.
119
     *
120
     * @var Expr
121
     */
122
    private $expressionBuilder;
123
124
    /**
125
     * The IdentifierFlattener used for manipulating identifiers
126
     *
127
     * @var IdentifierFlattener
128
     */
129
    private $identifierFlattener;
130
131
    /**
132
     * Whether the EntityManager is closed or not.
133
     *
134
     * @var bool
135
     */
136
    private $closed = false;
137
138
    /**
139
     * Collection of query filters.
140
     *
141
     * @var FilterCollection
142
     */
143
    private $filterCollection;
144
145
    /** @var Cache The second level cache regions API. */
146
    private $cache;
147
148
    /**
149
     * Creates a new EntityManager that operates on the given database connection
150
     * and uses the given Configuration and EventManager implementations.
151
     */
152 2278
    protected function __construct(Connection $conn, Configuration $config, EventManager $eventManager)
153
    {
154 2278
        $this->conn         = $conn;
155 2278
        $this->config       = $config;
156 2278
        $this->eventManager = $eventManager;
157
158 2278
        $metadataFactoryClassName = $config->getClassMetadataFactoryName();
159
160 2278
        $this->metadataFactory = new $metadataFactoryClassName();
161
162 2278
        $this->metadataFactory->setEntityManager($this);
163 2278
        $this->metadataFactory->setCacheDriver($this->config->getMetadataCacheImpl());
164
165 2278
        $this->repositoryFactory   = $config->getRepositoryFactory();
166 2278
        $this->unitOfWork          = new UnitOfWork($this);
167 2278
        $this->proxyFactory        = new StaticProxyFactory($this, $this->config->buildGhostObjectFactory());
168 2278
        $this->identifierFlattener = new IdentifierFlattener($this->unitOfWork, $this->metadataFactory);
169
170 2278
        if ($config->isSecondLevelCacheEnabled()) {
171 280
            $cacheConfig  = $config->getSecondLevelCacheConfiguration();
172 280
            $cacheFactory = $cacheConfig->getCacheFactory();
173 280
            $this->cache  = $cacheFactory->createCache($this);
174
        }
175 2278
    }
176
177
    /**
178
     * {@inheritDoc}
179
     */
180 2045
    public function getConnection()
181
    {
182 2045
        return $this->conn;
183
    }
184
185
    /**
186
     * Gets the metadata factory used to gather the metadata of classes.
187
     *
188
     * @return ClassMetadataFactory
189
     */
190 804
    public function getMetadataFactory()
191
    {
192 804
        return $this->metadataFactory;
193
    }
194
195
    /**
196
     * {@inheritDoc}
197
     */
198 17
    public function getExpressionBuilder()
199
    {
200 17
        if ($this->expressionBuilder === null) {
201 17
            $this->expressionBuilder = new Query\Expr();
202
        }
203
204 17
        return $this->expressionBuilder;
205
    }
206
207 1102
    public function getIdentifierFlattener() : IdentifierFlattener
208
    {
209 1102
        return $this->identifierFlattener;
210
    }
211
212
    /**
213
     * {@inheritDoc}
214
     */
215 1
    public function beginTransaction()
216
    {
217 1
        $this->conn->beginTransaction();
218 1
    }
219
220
    /**
221
     * {@inheritDoc}
222
     */
223 212
    public function getCache()
224
    {
225 212
        return $this->cache;
226
    }
227
228
    /**
229
     * {@inheritDoc}
230
     */
231 6
    public function transactional(callable $func)
232
    {
233 6
        $this->conn->beginTransaction();
234
235
        try {
236 6
            $return = $func($this);
237
238 5
            $this->flush();
239 5
            $this->conn->commit();
240
241 5
            return $return;
242 1
        } catch (Throwable $e) {
243 1
            $this->close();
244 1
            $this->conn->rollBack();
245
246 1
            throw $e;
247
        }
248
    }
249
250
    /**
251
     * {@inheritDoc}
252
     */
253 1
    public function commit()
254
    {
255 1
        $this->conn->commit();
256 1
    }
257
258
    /**
259
     * {@inheritDoc}
260
     */
261
    public function rollback()
262
    {
263
        $this->conn->rollBack();
264
    }
265
266
    /**
267
     * Returns the ORM metadata descriptor for a class.
268
     *
269
     * The class name must be the fully-qualified class name without a leading backslash
270
     * (as it is returned by get_class($obj)) or an aliased class name.
271
     *
272
     * Examples:
273
     * MyProject\Domain\User
274
     * sales:PriceRequest
275
     *
276
     * {@internal Performance-sensitive method. }}
277
     *
278
     * @param string $className
279
     *
280
     * @throws ReflectionException
281
     * @throws InvalidArgumentException
282
     * @throws MappingException
283
     */
284 1916
    public function getClassMetadata($className) : Mapping\ClassMetadata
285
    {
286 1916
        return $this->metadataFactory->getMetadataFor($className);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->metadataFa...MetadataFor($className) returns the type Doctrine\ORM\Mapping\ClassMetadata which is incompatible with the return type mandated by Doctrine\Persistence\Obj...ger::getClassMetadata() of Doctrine\Persistence\Mapping\ClassMetadata.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
287
    }
288
289
    /**
290
     * {@inheritDoc}
291
     */
292 964
    public function createQuery($dql = '')
293
    {
294 964
        $query = new Query($this);
295
296 964
        if (! empty($dql)) {
297 959
            $query->setDQL($dql);
298
        }
299
300 964
        return $query;
301
    }
302
303
    /**
304
     * {@inheritDoc}
305
     */
306 13
    public function createNativeQuery($sql, ResultSetMapping $rsm)
307
    {
308 13
        $query = new NativeQuery($this);
309
310 13
        $query->setSQL($sql);
311 13
        $query->setResultSetMapping($rsm);
312
313 13
        return $query;
314
    }
315
316
    /**
317
     * {@inheritDoc}
318
     */
319 128
    public function createQueryBuilder()
320
    {
321 128
        return new QueryBuilder($this);
322
    }
323
324
    /**
325
     * {@inheritDoc}
326
     *
327
     * @deprecated
328
     */
329
    public function merge($object)
330
    {
331
        throw new BadMethodCallException('@TODO method disabled - will be removed in 3.0 with a release of doctrine/common');
332
    }
333
334
    /**
335
     * {@inheritDoc}
336
     *
337
     * @deprecated
338
     */
339
    public function detach($object)
340
    {
341
        throw new BadMethodCallException('@TODO method disabled - will be removed in 3.0 with a release of doctrine/common');
342
    }
343
344
    /**
345
     * Flushes all changes to objects that have been queued up to now to the database.
346
     * This effectively synchronizes the in-memory state of managed objects with the
347
     * database.
348
     *
349
     * @throws OptimisticLockException If a version check on an entity that
350
     *         makes use of optimistic locking fails.
351
     * @throws ORMException
352
     */
353 1013
    public function flush()
354
    {
355 1013
        $this->errorIfClosed();
356
357 1012
        $this->unitOfWork->commit();
358 1002
    }
359
360
    /**
361
     * Finds an Entity by its identifier.
362
     *
363
     * @param string   $entityName  The class name of the entity to find.
364
     * @param mixed    $id          The identity of the entity to find.
365
     * @param int|null $lockMode    One of the \Doctrine\DBAL\LockMode::* constants
366
     *                              or NULL if no specific lock mode should be used
367
     *                              during the search.
368
     * @param int|null $lockVersion The version of the entity to find when using
369
     *                              optimistic locking.
370
     *
371
     * @return object|null The entity instance or NULL if the entity can not be found.
372
     *
373
     * @throws OptimisticLockException
374
     * @throws ORMInvalidArgumentException
375
     * @throws TransactionRequiredException
376
     * @throws ORMException
377
     */
378 407
    public function find($entityName, $id, $lockMode = null, $lockVersion = null)
379
    {
380 407
        $class     = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\'));
381 407
        $className = $class->getClassName();
382
383 407
        if ($lockMode !== null) {
384 6
            $this->checkLockRequirements($lockMode, $class);
385
        }
386
387 404
        if (! is_array($id)) {
388 372
            if ($class->isIdentifierComposite()) {
389
                throw ORMInvalidArgumentException::invalidCompositeIdentifier();
390
            }
391
392 372
            $id = [$class->identifier[0] => $id];
393
        }
394
395 404
        foreach ($id as $i => $value) {
396 404
            if (is_object($value) && $this->metadataFactory->hasMetadataFor(StaticClassNameConverter::getClass($value))) {
397 10
                $id[$i] = $this->unitOfWork->getSingleIdentifierValue($value);
398
399 10
                if ($id[$i] === null) {
400 1
                    throw ORMInvalidArgumentException::invalidIdentifierBindingEntity();
401
                }
402
            }
403
        }
404
405 403
        $sortedId = [];
406
407 403
        foreach ($class->identifier as $identifier) {
408 403
            if (! isset($id[$identifier])) {
409 1
                throw MissingIdentifierField::fromFieldAndClass($identifier, $className);
410
            }
411
412 402
            $sortedId[$identifier] = $id[$identifier];
413 402
            unset($id[$identifier]);
414
        }
415
416 402
        if ($id) {
417 1
            throw UnrecognizedIdentifierFields::fromClassAndFieldNames($className, array_keys($id));
418
        }
419
420 401
        $unitOfWork = $this->getUnitOfWork();
421
422
        // Check identity map first
423 401
        $entity = $unitOfWork->tryGetById($sortedId, $class->getRootClassName());
424 401
        if ($entity !== false) {
425 45
            if (! ($entity instanceof $className)) {
426 1
                return null;
427
            }
428
429
            switch (true) {
430 44
                case $lockMode === LockMode::OPTIMISTIC:
431
                    $this->lock($entity, $lockMode, $lockVersion);
432
                    break;
433
434 44
                case $lockMode === LockMode::NONE:
435 44
                case $lockMode === LockMode::PESSIMISTIC_READ:
436 44
                case $lockMode === LockMode::PESSIMISTIC_WRITE:
437
                    $persister = $unitOfWork->getEntityPersister($className);
438
                    $persister->refresh($sortedId, $entity, $lockMode);
0 ignored issues
show
Bug introduced by
It seems like $entity can also be of type true; however, parameter $entity of Doctrine\ORM\Persisters\...ityPersister::refresh() does only seem to accept object, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

438
                    $persister->refresh($sortedId, /** @scrutinizer ignore-type */ $entity, $lockMode);
Loading history...
439
                    break;
440
            }
441
442 44
            return $entity; // Hit!
0 ignored issues
show
Bug Best Practice introduced by
The expression return $entity also could return the type true which is incompatible with the documented return type null|object.
Loading history...
443
        }
444
445 380
        $persister = $unitOfWork->getEntityPersister($className);
446
447
        switch (true) {
448 380
            case $lockMode === LockMode::OPTIMISTIC:
449 1
                $entity = $persister->load($sortedId);
450
451 1
                $unitOfWork->lock($entity, $lockMode, $lockVersion);
452
453 1
                return $entity;
454 379
            case $lockMode === LockMode::PESSIMISTIC_READ:
455 379
            case $lockMode === LockMode::PESSIMISTIC_WRITE:
456
                return $persister->load($sortedId, null, null, [], $lockMode);
457
            default:
458 379
                return $persister->loadById($sortedId);
459
        }
460
    }
461
462
    /**
463
     * {@inheritDoc}
464
     */
465 90
    public function getReference($entityName, $id)
466
    {
467 90
        $class     = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\'));
468 90
        $className = $class->getClassName();
469
470 90
        if (! is_array($id)) {
471 39
            if ($class->isIdentifierComposite()) {
472
                throw ORMInvalidArgumentException::invalidCompositeIdentifier();
473
            }
474
475 39
            $id = [$class->identifier[0] => $id];
476
        }
477
478 90
        $scalarId = [];
479
480 90
        foreach ($id as $i => $value) {
481 90
            $scalarId[$i] = $value;
482
483 90
            if (is_object($value) && $this->metadataFactory->hasMetadataFor(StaticClassNameConverter::getClass($value))) {
484 2
                $scalarId[$i] = $this->unitOfWork->getSingleIdentifierValue($value);
485
486 2
                if ($scalarId[$i] === null) {
487
                    throw ORMInvalidArgumentException::invalidIdentifierBindingEntity();
488
                }
489
            }
490
        }
491
492 90
        $sortedId = [];
493
494 90
        foreach ($class->identifier as $identifier) {
495 90
            if (! isset($scalarId[$identifier])) {
496
                throw MissingIdentifierField::fromFieldAndClass($identifier, $className);
497
            }
498
499 90
            $sortedId[$identifier] = $scalarId[$identifier];
500 90
            unset($scalarId[$identifier]);
501
        }
502
503 90
        if ($scalarId) {
504 1
            throw UnrecognizedIdentifierFields::fromClassAndFieldNames($className, array_keys($scalarId));
505
        }
506
507
        // Check identity map first, if its already in there just return it.
508 89
        $entity = $this->unitOfWork->tryGetById($sortedId, $class->getRootClassName());
509 89
        if ($entity !== false) {
510 29
            return $entity instanceof $className ? $entity : null;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $entity instanceo...ssName ? $entity : null also could return the type true which is incompatible with the return type mandated by Doctrine\ORM\EntityManag...terface::getReference() of ProxyManager\Proxy\Ghost...ctInterface|null|object.
Loading history...
511
        }
512
513 84
        if ($class->getSubClasses()) {
514 2
            return $this->find($entityName, $sortedId);
515
        }
516
517 84
        $entity = $this->proxyFactory->getProxy($class, $id);
518
519 84
        $this->unitOfWork->registerManaged($entity, $sortedId, []);
520
521 84
        if ($entity instanceof EntityManagerAware) {
522 3
            $entity->injectEntityManager($this, $class);
523
        }
524
525 84
        return $entity;
526
    }
527
528
    /**
529
     * {@inheritDoc}
530
     */
531 4
    public function getPartialReference($entityName, $id)
532
    {
533 4
        $class     = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\'));
534 4
        $className = $class->getClassName();
535
536 4
        if (! is_array($id)) {
537 4
            if ($class->isIdentifierComposite()) {
538
                throw ORMInvalidArgumentException::invalidCompositeIdentifier();
539
            }
540
541 4
            $id = [$class->identifier[0] => $id];
542
        }
543
544 4
        foreach ($id as $i => $value) {
545 4
            if (is_object($value) && $this->metadataFactory->hasMetadataFor(StaticClassNameConverter::getClass($value))) {
546
                $id[$i] = $this->unitOfWork->getSingleIdentifierValue($value);
547
548
                if ($id[$i] === null) {
549
                    throw ORMInvalidArgumentException::invalidIdentifierBindingEntity();
550
                }
551
            }
552
        }
553
554 4
        $sortedId = [];
555
556 4
        foreach ($class->identifier as $identifier) {
557 4
            if (! isset($id[$identifier])) {
558
                throw MissingIdentifierField::fromFieldAndClass($identifier, $className);
559
            }
560
561 4
            $sortedId[$identifier] = $id[$identifier];
562 4
            unset($id[$identifier]);
563
        }
564
565 4
        if ($id) {
566
            throw UnrecognizedIdentifierFields::fromClassAndFieldNames($className, array_keys($id));
567
        }
568
569
        // Check identity map first, if its already in there just return it.
570 4
        $entity = $this->unitOfWork->tryGetById($sortedId, $class->getRootClassName());
571 4
        if ($entity !== false) {
572 1
            return $entity instanceof $className ? $entity : null;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $entity instanceo...ssName ? $entity : null also could return the type true which is incompatible with the return type mandated by Doctrine\ORM\EntityManag...::getPartialReference() of null|object.
Loading history...
573
        }
574
575 3
        $persister = $this->unitOfWork->getEntityPersister($class->getClassName());
576 3
        $entity    = $this->unitOfWork->newInstance($class);
577
578 3
        $persister->setIdentifier($entity, $sortedId);
579
580 3
        $this->unitOfWork->registerManaged($entity, $sortedId, []);
581 3
        $this->unitOfWork->markReadOnly($entity);
582
583 3
        return $entity;
584
    }
585
586
    /**
587
     * Clears the EntityManager. All entities that are currently managed
588
     * by this EntityManager become detached.
589
     *
590
     * @param null $entityName Unused. @todo Remove from ObjectManager.
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $entityName is correct as it would always require null to be passed?
Loading history...
591
     */
592 1217
    public function clear($entityName = null)
593
    {
594 1217
        $this->unitOfWork->clear();
595
596 1217
        $this->unitOfWork = new UnitOfWork($this);
597
598 1217
        if ($this->eventManager->hasListeners(Events::onClear)) {
599 9
            $this->eventManager->dispatchEvent(Events::onClear, new Event\OnClearEventArgs($this));
600
        }
601 1217
    }
602
603
    /**
604
     * {@inheritDoc}
605
     */
606 18
    public function close()
607
    {
608 18
        $this->clear();
609
610 18
        $this->closed = true;
611 18
    }
612
613
    /**
614
     * Tells the EntityManager to make an instance managed and persistent.
615
     *
616
     * The entity will be entered into the database at or before transaction
617
     * commit or as a result of the flush operation.
618
     *
619
     * NOTE: The persist operation always considers entities that are not yet known to
620
     * this EntityManager as NEW. Do not pass detached entities to the persist operation.
621
     *
622
     * @param object $entity The instance to make managed and persistent.
623
     *
624
     * @throws ORMInvalidArgumentException
625
     * @throws ORMException
626
     */
627 1007
    public function persist($entity)
628
    {
629 1007
        if (! is_object($entity)) {
630 1
            throw ORMInvalidArgumentException::invalidObject('EntityManager#persist()', $entity);
631
        }
632
633 1006
        $this->errorIfClosed();
634
635 1005
        $this->unitOfWork->persist($entity);
636 1004
    }
637
638
    /**
639
     * Removes an entity instance.
640
     *
641
     * A removed entity will be removed from the database at or before transaction commit
642
     * or as a result of the flush operation.
643
     *
644
     * @param object $entity The entity instance to remove.
645
     *
646
     * @throws ORMInvalidArgumentException
647
     * @throws ORMException
648
     */
649 48
    public function remove($entity)
650
    {
651 48
        if (! is_object($entity)) {
652 1
            throw ORMInvalidArgumentException::invalidObject('EntityManager#remove()', $entity);
653
        }
654
655 47
        $this->errorIfClosed();
656
657 46
        $this->unitOfWork->remove($entity);
658 46
    }
659
660
    /**
661
     * Refreshes the persistent state of an entity from the database,
662
     * overriding any local changes that have not yet been persisted.
663
     *
664
     * @param object $entity The entity to refresh.
665
     *
666
     * @throws ORMInvalidArgumentException
667
     * @throws ORMException
668
     */
669 17
    public function refresh($entity)
670
    {
671 17
        if (! is_object($entity)) {
672 1
            throw ORMInvalidArgumentException::invalidObject('EntityManager#refresh()', $entity);
673
        }
674
675 16
        $this->errorIfClosed();
676
677 15
        $this->unitOfWork->refresh($entity);
678 15
    }
679
680
    /**
681
     * {@inheritDoc}
682
     */
683 8
    public function lock($entity, $lockMode, $lockVersion = null)
684
    {
685 8
        $this->unitOfWork->lock($entity, $lockMode, $lockVersion);
686 2
    }
687
688
    /**
689
     * Gets the repository for an entity class.
690
     *
691
     * @param string $entityName The name of the entity.
692
     *
693
     * @return ObjectRepository|EntityRepository The repository class.
694
     */
695 138
    public function getRepository($entityName)
696
    {
697 138
        return $this->repositoryFactory->getRepository($this, $entityName);
698
    }
699
700
    /**
701
     * Determines whether an entity instance is managed in this EntityManager.
702
     *
703
     * @param object $entity
704
     *
705
     * @return bool TRUE if this EntityManager currently manages the given entity, FALSE otherwise.
706
     */
707 15
    public function contains($entity)
708
    {
709 15
        return $this->unitOfWork->isScheduledForInsert($entity)
710 15
            || ($this->unitOfWork->isInIdentityMap($entity) && ! $this->unitOfWork->isScheduledForDelete($entity));
711
    }
712
713
    /**
714
     * {@inheritDoc}
715
     */
716 2278
    public function getEventManager()
717
    {
718 2278
        return $this->eventManager;
719
    }
720
721
    /**
722
     * {@inheritDoc}
723
     */
724 2278
    public function getConfiguration()
725
    {
726 2278
        return $this->config;
727
    }
728
729
    /**
730
     * Throws an exception if the EntityManager is closed or currently not active.
731
     *
732
     * @throws ORMException If the EntityManager is closed.
733
     */
734 1018
    private function errorIfClosed()
735
    {
736 1018
        if ($this->closed) {
737 4
            throw EntityManagerClosed::create();
738
        }
739 1014
    }
740
741
    /**
742
     * {@inheritDoc}
743
     */
744 2
    public function isOpen()
745
    {
746 2
        return ! $this->closed;
747
    }
748
749
    /**
750
     * {@inheritDoc}
751
     */
752 1361
    public function getUnitOfWork()
753
    {
754 1361
        return $this->unitOfWork;
755
    }
756
757
    /**
758
     * {@inheritDoc}
759
     */
760
    public function getHydrator($hydrationMode)
761
    {
762
        return $this->newHydrator($hydrationMode);
763
    }
764
765
    /**
766
     * {@inheritDoc}
767
     */
768 893
    public function newHydrator($hydrationMode)
769
    {
770 893
        switch ($hydrationMode) {
771
            case Query::HYDRATE_OBJECT:
772 618
                return new Internal\Hydration\ObjectHydrator($this);
773
            case Query::HYDRATE_ARRAY:
774 47
                return new Internal\Hydration\ArrayHydrator($this);
775
            case Query::HYDRATE_SCALAR:
776 85
                return new Internal\Hydration\ScalarHydrator($this);
777
            case Query::HYDRATE_SINGLE_SCALAR:
778 13
                return new Internal\Hydration\SingleScalarHydrator($this);
779
            case Query::HYDRATE_SIMPLEOBJECT:
780 410
                return new Internal\Hydration\SimpleObjectHydrator($this);
781
            default:
782 1
                $class = $this->config->getCustomHydrationMode($hydrationMode);
783 1
                if ($class !== null) {
784 1
                    return new $class($this);
785
                }
786
        }
787
788
        throw InvalidHydrationMode::fromMode($hydrationMode);
789
    }
790
791
    /**
792
     * {@inheritDoc}
793
     */
794 166
    public function getProxyFactory()
795
    {
796 166
        return $this->proxyFactory;
797
    }
798
799
    /**
800
     * {@inheritDoc}
801
     */
802
    public function initializeObject($obj)
803
    {
804
        $this->unitOfWork->initializeObject($obj);
805
    }
806
807
    /**
808
     * Factory method to create EntityManager instances.
809
     *
810
     * @param Connection|mixed[] $connection   An array with the connection parameters or an existing Connection instance.
811
     * @param Configuration      $config       The Configuration instance to use.
812
     * @param EventManager       $eventManager The EventManager instance to use.
813
     *
814
     * @return EntityManager The created EntityManager.
815
     *
816
     * @throws InvalidArgumentException
817
     * @throws ORMException
818
     */
819 2278
    public static function create($connection, Configuration $config, ?EventManager $eventManager = null)
820
    {
821 2278
        if (! $config->getMetadataDriverImpl()) {
822
            throw MissingMappingDriverImplementation::create();
823
        }
824
825 2278
        $connection = static::createConnection($connection, $config, $eventManager);
826
827 2278
        return new EntityManager($connection, $config, $connection->getEventManager());
828
    }
829
830
    /**
831
     * Factory method to create Connection instances.
832
     *
833
     * @param Connection|mixed[] $connection   An array with the connection parameters or an existing Connection instance.
834
     * @param Configuration      $config       The Configuration instance to use.
835
     * @param EventManager       $eventManager The EventManager instance to use.
836
     *
837
     * @return Connection
838
     *
839
     * @throws InvalidArgumentException
840
     * @throws ORMException
841
     */
842 2278
    protected static function createConnection($connection, Configuration $config, ?EventManager $eventManager = null)
843
    {
844 2278
        if (is_array($connection)) {
845
            return DriverManager::getConnection($connection, $config, $eventManager ?: new EventManager());
846
        }
847
848 2278
        if (! $connection instanceof Connection) {
0 ignored issues
show
introduced by
$connection is always a sub-type of Doctrine\DBAL\Connection.
Loading history...
849 1
            throw new InvalidArgumentException(
850 1
                sprintf(
851 1
                    'Invalid $connection argument of type %s given%s.',
852 1
                    is_object($connection) ? get_class($connection) : gettype($connection),
853 1
                    is_object($connection) ? '' : ': "' . $connection . '"'
854
                )
855
            );
856
        }
857
858 2278
        if ($eventManager !== null && $connection->getEventManager() !== $eventManager) {
859
            throw MismatchedEventManager::create();
860
        }
861
862 2278
        return $connection;
863
    }
864
865
    /**
866
     * {@inheritDoc}
867
     */
868 592
    public function getFilters()
869
    {
870 592
        if ($this->filterCollection === null) {
871 592
            $this->filterCollection = new FilterCollection($this);
872
        }
873
874 592
        return $this->filterCollection;
875
    }
876
877
    /**
878
     * {@inheritDoc}
879
     */
880 40
    public function isFiltersStateClean()
881
    {
882 40
        return $this->filterCollection === null || $this->filterCollection->isClean();
883
    }
884
885
    /**
886
     * {@inheritDoc}
887
     */
888 776
    public function hasFilters()
889
    {
890 776
        return $this->filterCollection !== null;
891
    }
892
893
    /**
894
     * @throws OptimisticLockException
895
     * @throws TransactionRequiredException
896
     */
897 6
    private function checkLockRequirements(int $lockMode, ClassMetadata $class) : void
898
    {
899
        switch ($lockMode) {
900 6
            case LockMode::OPTIMISTIC:
901 3
                if (! $class->isVersioned()) {
902 2
                    throw OptimisticLockException::notVersioned($class->getClassName());
903
                }
904 1
                break;
905 3
            case LockMode::PESSIMISTIC_READ:
906 2
            case LockMode::PESSIMISTIC_WRITE:
907 3
                if (! $this->getConnection()->isTransactionActive()) {
908 3
                    throw TransactionRequiredException::transactionRequired();
909
                }
910
        }
911 1
    }
912
}
913