Failed Conditions
Pull Request — master (#7123)
by Michael
12:02
created

EntityManager::detach()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\ORM;
6
7
use Doctrine\Common\EventManager;
8
use Doctrine\Common\Persistence\Mapping\MappingException;
9
use Doctrine\Common\Persistence\ObjectRepository;
10
use Doctrine\DBAL\Connection;
11
use Doctrine\DBAL\DriverManager;
0 ignored issues
show
introduced by
Type Doctrine\DBAL\DriverManager is not used in this file.
Loading history...
12
use Doctrine\DBAL\LockMode;
13
use Doctrine\ORM\Mapping\ClassMetadataFactory;
14
use Doctrine\ORM\Proxy\Factory\ProxyFactory;
15
use Doctrine\ORM\Proxy\Factory\StaticProxyFactory;
16
use Doctrine\ORM\Query\Expr;
17
use Doctrine\ORM\Query\FilterCollection;
18
use Doctrine\ORM\Query\ResultSetMapping;
19
use Doctrine\ORM\Repository\RepositoryFactory;
20
use Doctrine\ORM\Utility\IdentifierFlattener;
21
use Doctrine\ORM\Utility\StaticClassNameConverter;
22
use function array_keys;
23
use function get_class;
0 ignored issues
show
introduced by
Type get_class is not used in this file.
Loading history...
24
use function gettype;
0 ignored issues
show
introduced by
Type gettype is not used in this file.
Loading history...
25
use function is_array;
26
use function is_object;
27
use function ltrim;
28
use function sprintf;
0 ignored issues
show
introduced by
Type sprintf is not used in this file.
Loading history...
29
30
/**
31
 * The EntityManager is the central access point to ORM functionality.
32
 *
33
 * It is a facade to all different ORM subsystems such as UnitOfWork,
34
 * Query Language and Repository API. Instantiation is done through
35
 * the static create() method. The quickest way to obtain a fully
36
 * configured EntityManager is:
37
 *
38
 *     use Doctrine\DBAL\DriverManager;
39
 *     use Doctrine\ORM\EntityManager;
40
 *     use Doctrine\ORM\Tools\Setup;
41
 *
42
 *     $paths = ['/path/to/entity/mapping/files'];
43
 *
44
 *     $configuration = Setup::createAnnotationMetadataConfiguration($paths);
45
 *     $connection = DriverManager::getConnection(['driver' => 'pdo_sqlite', 'memory' => true]);
46
 *     $entityManager = EntityManager::create($connection, $configuration);
47
 *
48
 * For more information see
49
 * {@link http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/configuration.html}
50
 *
51
 * You should never attempt to inherit from the EntityManager: Inheritance
52
 * is not a valid extension point for the EntityManager. Instead you
53
 * should take a look at the {@see \Doctrine\ORM\Decorator\EntityManagerDecorator}
54
 * and wrap your entity manager in a decorator.
55
 */
56
final class EntityManager implements EntityManagerInterface
57
{
58
    /**
59
     * The used Configuration.
60
     *
61
     * @var Configuration
62
     */
63
    private $config;
64
65
    /**
66
     * The database connection used by the EntityManager.
67
     *
68
     * @var Connection
69
     */
70
    private $conn;
71
72
    /**
73
     * The metadata factory, used to retrieve the ORM metadata of entity classes.
74
     *
75
     * @var ClassMetadataFactory
76
     */
77
    private $metadataFactory;
78
79
    /**
80
     * The UnitOfWork used to coordinate object-level transactions.
81
     *
82
     * @var UnitOfWork
83
     */
84
    private $unitOfWork;
85
86
    /**
87
     * The event manager that is the central point of the event system.
88
     *
89
     * @var EventManager
90
     */
91
    private $eventManager;
92
93
    /**
94
     * The proxy factory used to create dynamic proxies.
95
     *
96
     * @var ProxyFactory
97
     */
98
    private $proxyFactory;
99
100
    /**
101
     * The repository factory used to create dynamic repositories.
102
     *
103
     * @var RepositoryFactory
104
     */
105
    private $repositoryFactory;
106
107
    /**
108
     * The expression builder instance used to generate query expressions.
109
     *
110
     * @var Expr
111
     */
112
    private $expressionBuilder;
113
114
    /**
115
     * The IdentifierFlattener used for manipulating identifiers
116
     *
117
     * @var IdentifierFlattener
118
     */
119
    private $identifierFlattener;
120
121
    /**
122
     * Whether the EntityManager is closed or not.
123
     *
124
     * @var bool
125
     */
126
    private $closed = false;
127
128
    /**
129
     * Collection of query filters.
130
     *
131
     * @var FilterCollection
132
     */
133
    private $filterCollection;
134
135
    /** @var Cache The second level cache regions API. */
136
    private $cache;
137
138
    /**
139
     * Creates a new EntityManager that operates on the given database connection
140
     * and uses the given Configuration and EventManager implementations.
141
     *
142
     */
143 2246
    protected function __construct(Connection $conn, Configuration $config, EventManager $eventManager)
144
    {
145 2246
        $this->conn         = $conn;
146 2246
        $this->config       = $config;
147 2246
        $this->eventManager = $eventManager;
148
149 2246
        $metadataFactoryClassName = $config->getClassMetadataFactoryName();
150
151 2246
        $this->metadataFactory = new $metadataFactoryClassName();
152
153 2246
        $this->metadataFactory->setEntityManager($this);
154 2246
        $this->metadataFactory->setCacheDriver($this->config->getMetadataCacheImpl());
155
156 2246
        $this->repositoryFactory   = $config->getRepositoryFactory();
157 2246
        $this->unitOfWork          = new UnitOfWork($this);
158 2246
        $this->proxyFactory        = new StaticProxyFactory($this, $this->config->buildGhostObjectFactory());
159 2246
        $this->identifierFlattener = new IdentifierFlattener($this->unitOfWork, $this->metadataFactory);
160
161 2246
        if ($config->isSecondLevelCacheEnabled()) {
162 280
            $cacheConfig  = $config->getSecondLevelCacheConfiguration();
163 280
            $cacheFactory = $cacheConfig->getCacheFactory();
164 280
            $this->cache  = $cacheFactory->createCache($this);
165
        }
166 2246
    }
167
168
    /**
169
     * {@inheritDoc}
170
     */
171 1781
    public function getConnection()
172
    {
173 1781
        return $this->conn;
174
    }
175
176
    /**
177
     * Gets the metadata factory used to gather the metadata of classes.
178
     *
179
     * @return ClassMetadataFactory
180
     */
181 812
    public function getMetadataFactory()
182
    {
183 812
        return $this->metadataFactory;
184
    }
185
186
    /**
187
     * {@inheritDoc}
188
     */
189 17
    public function getExpressionBuilder()
190
    {
191 17
        if ($this->expressionBuilder === null) {
192 17
            $this->expressionBuilder = new Query\Expr();
193
        }
194
195 17
        return $this->expressionBuilder;
196
    }
197
198 1094
    public function getIdentifierFlattener() : IdentifierFlattener
199
    {
200 1094
        return $this->identifierFlattener;
201
    }
202
203
    /**
204
     * {@inheritDoc}
205
     */
206 1
    public function beginTransaction()
207
    {
208 1
        $this->conn->beginTransaction();
209 1
    }
210
211
    /**
212
     * {@inheritDoc}
213
     */
214 212
    public function getCache()
215
    {
216 212
        return $this->cache;
217
    }
218
219
    /**
220
     * {@inheritDoc}
221
     */
222 6
    public function transactional(callable $func)
223
    {
224 6
        $this->conn->beginTransaction();
225
226
        try {
227 6
            $return = $func($this);
228
229 5
            $this->flush();
230 5
            $this->conn->commit();
231
232 5
            return $return;
233 1
        } catch (\Throwable $e) {
234 1
            $this->close();
235 1
            $this->conn->rollBack();
236
237 1
            throw $e;
238
        }
239
    }
240
241
    /**
242
     * {@inheritDoc}
243
     */
244 1
    public function commit()
245
    {
246 1
        $this->conn->commit();
247 1
    }
248
249
    /**
250
     * {@inheritDoc}
251
     */
252
    public function rollback()
253
    {
254
        $this->conn->rollBack();
255
    }
256
257
    /**
258
     * Returns the ORM metadata descriptor for a class.
259
     *
260
     * The class name must be the fully-qualified class name without a leading backslash
261
     * (as it is returned by get_class($obj)) or an aliased class name.
262
     *
263
     * Examples:
264
     * MyProject\Domain\User
265
     * sales:PriceRequest
266
     *
267
     * {@internal Performance-sensitive method. }}
268
     *
269
     * @param string $className
270
     *
271
     * @throws \ReflectionException
272
     * @throws \InvalidArgumentException
273
     * @throws MappingException
274
     */
275 1894
    public function getClassMetadata($className) : Mapping\ClassMetadata
276
    {
277 1894
        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\Common\Persiste...ger::getClassMetadata() of Doctrine\Common\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...
278
    }
279
280
    /**
281
     * {@inheritDoc}
282
     */
283 954
    public function createQuery($dql = '')
284
    {
285 954
        $query = new Query($this);
286
287 954
        if (! empty($dql)) {
288 949
            $query->setDQL($dql);
289
        }
290
291 954
        return $query;
292
    }
293
294
    /**
295
     * {@inheritDoc}
296
     */
297 13
    public function createNativeQuery($sql, ResultSetMapping $rsm)
298
    {
299 13
        $query = new NativeQuery($this);
300
301 13
        $query->setSQL($sql);
302 13
        $query->setResultSetMapping($rsm);
303
304 13
        return $query;
305
    }
306
307
    /**
308
     * {@inheritDoc}
309
     */
310 128
    public function createQueryBuilder()
311
    {
312 128
        return new QueryBuilder($this);
313
    }
314
315
    /**
316
     * {@inheritDoc}
317
     *
318
     * @deprecated
319
     */
320
    public function merge($object)
321
    {
322
        throw new \BadMethodCallException('@TODO method disabled - will be removed in 3.0 with a release of doctrine/common');
323
    }
324
325
    /**
326
     * {@inheritDoc}
327
     *
328
     * @deprecated
329
     */
330
    public function detach($object)
331
    {
332
        throw new \BadMethodCallException('@TODO method disabled - will be removed in 3.0 with a release of doctrine/common');
333
    }
334
335
    /**
336
     * Flushes all changes to objects that have been queued up to now to the database.
337
     * This effectively synchronizes the in-memory state of managed objects with the
338
     * database.
339
     *
340
     * If an entity is explicitly passed to this method only this entity and
341
     * the cascade-persist semantics + scheduled inserts/removals are synchronized.
342
     *
343
     * @throws OptimisticLockException If a version check on an entity that
344
     *         makes use of optimistic locking fails.
345
     * @throws ORMException
346
     */
347 1005
    public function flush()
348
    {
349 1005
        $this->errorIfClosed();
350
351 1004
        $this->unitOfWork->commit();
352 994
    }
353
354
    /**
355
     * Finds an Entity by its identifier.
356
     *
357
     * @param string   $entityName  The class name of the entity to find.
358
     * @param mixed    $id          The identity of the entity to find.
359
     * @param int|null $lockMode    One of the \Doctrine\DBAL\LockMode::* constants
360
     *                              or NULL if no specific lock mode should be used
361
     *                              during the search.
362
     * @param int|null $lockVersion The version of the entity to find when using
363
     *                              optimistic locking.
364
     *
365
     * @return object|null The entity instance or NULL if the entity can not be found.
366
     *
367
     * @throws OptimisticLockException
368
     * @throws ORMInvalidArgumentException
369
     * @throws TransactionRequiredException
370
     * @throws ORMException
371
     */
372 403
    public function find($entityName, $id, $lockMode = null, $lockVersion = null)
373
    {
374 403
        $class     = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\'));
375 403
        $className = $class->getClassName();
376
377 403
        if (! is_array($id)) {
378 371
            if ($class->isIdentifierComposite()) {
379
                throw ORMInvalidArgumentException::invalidCompositeIdentifier();
380
            }
381
382 371
            $id = [$class->identifier[0] => $id];
383
        }
384
385 403
        foreach ($id as $i => $value) {
386 403
            if (is_object($value) && $this->metadataFactory->hasMetadataFor(StaticClassNameConverter::getClass($value))) {
387 10
                $id[$i] = $this->unitOfWork->getSingleIdentifierValue($value);
388
389 10
                if ($id[$i] === null) {
390 403
                    throw ORMInvalidArgumentException::invalidIdentifierBindingEntity();
391
                }
392
            }
393
        }
394
395 402
        $sortedId = [];
396
397 402
        foreach ($class->identifier as $identifier) {
398 402
            if (! isset($id[$identifier])) {
399 1
                throw ORMException::missingIdentifierField($className, $identifier);
400
            }
401
402 401
            $sortedId[$identifier] = $id[$identifier];
403 401
            unset($id[$identifier]);
404
        }
405
406 401
        if ($id) {
407 1
            throw ORMException::unrecognizedIdentifierFields($className, array_keys($id));
408
        }
409
410 400
        $unitOfWork = $this->getUnitOfWork();
411
412
        // Check identity map first
413 400
        $entity = $unitOfWork->tryGetById($sortedId, $class->getRootClassName());
414 400
        if ($entity !== false) {
415 45
            if (! ($entity instanceof $className)) {
416 1
                return null;
417
            }
418
419
            switch (true) {
420 44
                case $lockMode === LockMode::OPTIMISTIC:
421 1
                    $this->lock($entity, $lockMode, $lockVersion);
422
                    break;
423
424 44
                case $lockMode === LockMode::NONE:
425 44
                case $lockMode === LockMode::PESSIMISTIC_READ:
426 44
                case $lockMode === LockMode::PESSIMISTIC_WRITE:
427
                    $persister = $unitOfWork->getEntityPersister($className);
428
                    $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

428
                    $persister->refresh($sortedId, /** @scrutinizer ignore-type */ $entity, $lockMode);
Loading history...
429
                    break;
430
            }
431
432 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 object|null.
Loading history...
433
        }
434
435 379
        $persister = $unitOfWork->getEntityPersister($className);
436
437
        switch (true) {
438 379
            case $lockMode === LockMode::OPTIMISTIC:
439 1
                if (! $class->isVersioned()) {
440 1
                    throw OptimisticLockException::notVersioned($className);
441
                }
442
443
                $entity = $persister->load($sortedId);
444
445
                $unitOfWork->lock($entity, $lockMode, $lockVersion);
446
447
                return $entity;
448
449 378
            case $lockMode === LockMode::PESSIMISTIC_READ:
450 377
            case $lockMode === LockMode::PESSIMISTIC_WRITE:
451 2
                if (! $this->getConnection()->isTransactionActive()) {
452 2
                    throw TransactionRequiredException::transactionRequired();
453
                }
454
455
                return $persister->load($sortedId, null, null, [], $lockMode);
456
457
            default:
458 376
                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 90
                    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 ORMException::missingIdentifierField($className, $identifier);
497
            }
498
499 90
            $sortedId[$identifier] = $scalarId[$identifier];
500 90
            unset($scalarId[$identifier]);
501
        }
502
503 90
        if ($scalarId) {
504 1
            throw ORMException::unrecognizedIdentifierFields($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 object|null|ProxyManager...xy\GhostObjectInterface.
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 4
                    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 ORMException::missingIdentifierField($className, $identifier);
559
            }
560
561 4
            $sortedId[$identifier] = $id[$identifier];
562 4
            unset($id[$identifier]);
563
        }
564
565 4
        if ($id) {
566
            throw ORMException::unrecognizedIdentifierFields($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 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
     */
593 1197
    public function clear($entityName = null)
594
    {
595 1197
        $this->unitOfWork->clear();
596
597 1197
        $this->unitOfWork = new UnitOfWork($this);
598
599 1197
        if ($this->eventManager->hasListeners(Events::onClear)) {
600 9
            $this->eventManager->dispatchEvent(Events::onClear, new Event\OnClearEventArgs($this));
601
        }
602 1197
    }
603
604
    /**
605
     * {@inheritDoc}
606
     */
607 18
    public function close()
608
    {
609 18
        $this->clear();
610
611 18
        $this->closed = true;
612 18
    }
613
614
    /**
615
     * Tells the EntityManager to make an instance managed and persistent.
616
     *
617
     * The entity will be entered into the database at or before transaction
618
     * commit or as a result of the flush operation.
619
     *
620
     * NOTE: The persist operation always considers entities that are not yet known to
621
     * this EntityManager as NEW. Do not pass detached entities to the persist operation.
622
     *
623
     * @param object $entity The instance to make managed and persistent.
624
     *
625
     * @throws ORMInvalidArgumentException
626
     * @throws ORMException
627
     */
628 999
    public function persist($entity)
629
    {
630 999
        if (! is_object($entity)) {
631 1
            throw ORMInvalidArgumentException::invalidObject('EntityManager#persist()', $entity);
632
        }
633
634 998
        $this->errorIfClosed();
635
636 997
        $this->unitOfWork->persist($entity);
637 996
    }
638
639
    /**
640
     * Removes an entity instance.
641
     *
642
     * A removed entity will be removed from the database at or before transaction commit
643
     * or as a result of the flush operation.
644
     *
645
     * @param object $entity The entity instance to remove.
646
     *
647
     * @throws ORMInvalidArgumentException
648
     * @throws ORMException
649
     */
650 49
    public function remove($entity)
651
    {
652 49
        if (! is_object($entity)) {
653 1
            throw ORMInvalidArgumentException::invalidObject('EntityManager#remove()', $entity);
654
        }
655
656 48
        $this->errorIfClosed();
657
658 47
        $this->unitOfWork->remove($entity);
659 47
    }
660
661
    /**
662
     * Refreshes the persistent state of an entity from the database,
663
     * overriding any local changes that have not yet been persisted.
664
     *
665
     * @param object $entity The entity to refresh.
666
     *
667
     * @throws ORMInvalidArgumentException
668
     * @throws ORMException
669
     */
670 17
    public function refresh($entity)
671
    {
672 17
        if (! is_object($entity)) {
673 1
            throw ORMInvalidArgumentException::invalidObject('EntityManager#refresh()', $entity);
674
        }
675
676 16
        $this->errorIfClosed();
677
678 15
        $this->unitOfWork->refresh($entity);
679 15
    }
680
681
    /**
682
     * {@inheritDoc}
683
     */
684 9
    public function lock($entity, $lockMode, $lockVersion = null)
685
    {
686 9
        $this->unitOfWork->lock($entity, $lockMode, $lockVersion);
687 2
    }
688
689
    /**
690
     * Gets the repository for an entity class.
691
     *
692
     * @param string $entityName The name of the entity.
693
     *
694
     * @return ObjectRepository|EntityRepository The repository class.
695
     */
696 137
    public function getRepository($entityName)
697
    {
698 137
        return $this->repositoryFactory->getRepository($this, $entityName);
699
    }
700
701
    /**
702
     * Determines whether an entity instance is managed in this EntityManager.
703
     *
704
     * @param object $entity
705
     *
706
     * @return bool TRUE if this EntityManager currently manages the given entity, FALSE otherwise.
707
     */
708 15
    public function contains($entity)
709
    {
710 15
        return $this->unitOfWork->isScheduledForInsert($entity)
711 15
            || ($this->unitOfWork->isInIdentityMap($entity) && ! $this->unitOfWork->isScheduledForDelete($entity));
712
    }
713
714
    /**
715
     * {@inheritDoc}
716
     */
717 2246
    public function getEventManager()
718
    {
719 2246
        return $this->eventManager;
720
    }
721
722
    /**
723
     * {@inheritDoc}
724
     */
725 2246
    public function getConfiguration()
726
    {
727 2246
        return $this->config;
728
    }
729
730
    /**
731
     * Throws an exception if the EntityManager is closed or currently not active.
732
     *
733
     * @throws ORMException If the EntityManager is closed.
734
     */
735 1010
    private function errorIfClosed()
736
    {
737 1010
        if ($this->closed) {
738 4
            throw ORMException::entityManagerClosed();
739
        }
740 1006
    }
741
742
    /**
743
     * {@inheritDoc}
744
     */
745 2
    public function isOpen()
746
    {
747 2
        return ! $this->closed;
748
    }
749
750
    /**
751
     * {@inheritDoc}
752
     */
753 1350
    public function getUnitOfWork()
754
    {
755 1350
        return $this->unitOfWork;
756
    }
757
758
    /**
759
     * {@inheritDoc}
760
     */
761
    public function getHydrator($hydrationMode)
762
    {
763
        return $this->newHydrator($hydrationMode);
764
    }
765
766
    /**
767
     * {@inheritDoc}
768
     */
769 883
    public function newHydrator($hydrationMode)
770
    {
771 883
        switch ($hydrationMode) {
772
            case Query::HYDRATE_OBJECT:
773 614
                return new Internal\Hydration\ObjectHydrator($this);
774
775
            case Query::HYDRATE_ARRAY:
776 45
                return new Internal\Hydration\ArrayHydrator($this);
777
778
            case Query::HYDRATE_SCALAR:
779 85
                return new Internal\Hydration\ScalarHydrator($this);
780
781
            case Query::HYDRATE_SINGLE_SCALAR:
782 13
                return new Internal\Hydration\SingleScalarHydrator($this);
783
784
            case Query::HYDRATE_SIMPLEOBJECT:
785 407
                return new Internal\Hydration\SimpleObjectHydrator($this);
786
787
            default:
788 1
                $class = $this->config->getCustomHydrationMode($hydrationMode);
789 1
                if ($class !== null) {
790 1
                    return new $class($this);
791
                }
792
        }
793
794
        throw ORMException::invalidHydrationMode($hydrationMode);
795
    }
796
797
    /**
798
     * {@inheritDoc}
799
     */
800 166
    public function getProxyFactory()
801
    {
802 166
        return $this->proxyFactory;
803
    }
804
805
    /**
806
     * {@inheritDoc}
807
     */
808
    public function initializeObject($obj)
809
    {
810
        $this->unitOfWork->initializeObject($obj);
811
    }
812
813
    /**
814
     * Factory method to create EntityManager instances.
815
     *
816
     * @param Connection    $connection   The Connection instance to use.
817
     * @param Configuration $config       The Configuration instance to use.
818
     * @param EventManager  $eventManager The EventManager instance to use.
819
     *
820
     * @return EntityManager The created EntityManager.
821
     *
822
     * @throws \InvalidArgumentException
823
     * @throws ORMException
824
     */
825 2246
    public static function create(Connection $connection, Configuration $config, ?EventManager $eventManager = null)
826
    {
827 2246
        if (! $config->getMetadataDriverImpl()) {
828
            throw ORMException::missingMappingDriverImpl();
829
        }
830
831 2246
        if ($eventManager !== null && $connection->getEventManager() !== $eventManager) {
832
            throw ORMException::mismatchedEventManager();
833
        }
834
835 2246
        return new EntityManager($connection, $config, $eventManager ?? $connection->getEventManager());
836
    }
837
838
    /**
839
     * {@inheritDoc}
840
     */
841 588
    public function getFilters()
842
    {
843 588
        if ($this->filterCollection === null) {
844 588
            $this->filterCollection = new FilterCollection($this);
845
        }
846
847 588
        return $this->filterCollection;
848
    }
849
850
    /**
851
     * {@inheritDoc}
852
     */
853 40
    public function isFiltersStateClean()
854
    {
855 40
        return $this->filterCollection === null || $this->filterCollection->isClean();
856
    }
857
858
    /**
859
     * {@inheritDoc}
860
     */
861 765
    public function hasFilters()
862
    {
863 765
        return $this->filterCollection !== null;
864
    }
865
}
866