Failed Conditions
Pull Request — master (#7842)
by
unknown
12:00
created

EntityManager::find()   F

Complexity

Conditions 20
Paths 282

Size

Total Lines 81
Code Lines 48

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 38
CRAP Score 22.1035

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 20
eloc 48
c 1
b 0
f 0
nc 282
nop 4
dl 0
loc 81
rs 2.3083
ccs 38
cts 46
cp 0.8261
crap 22.1035

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 2180
    protected function __construct(Connection $conn, Configuration $config, EventManager $eventManager)
153
    {
154 2180
        $this->conn         = $conn;
155 2180
        $this->config       = $config;
156 2180
        $this->eventManager = $eventManager;
157
158 2180
        $metadataFactoryClassName = $config->getClassMetadataFactoryName();
159
160 2180
        $this->metadataFactory = new $metadataFactoryClassName();
161
162 2180
        $this->metadataFactory->setEntityManager($this);
163 2180
        $this->metadataFactory->setCacheDriver($this->config->getMetadataCacheImpl());
164
165 2180
        $this->repositoryFactory   = $config->getRepositoryFactory();
166 2180
        $this->unitOfWork          = new UnitOfWork($this);
167 2180
        $this->proxyFactory        = new StaticProxyFactory($this, $this->config->buildGhostObjectFactory());
168 2180
        $this->identifierFlattener = new IdentifierFlattener($this->unitOfWork, $this->metadataFactory);
169
170 2180
        if ($config->isSecondLevelCacheEnabled()) {
171 279
            $cacheConfig  = $config->getSecondLevelCacheConfiguration();
172 279
            $cacheFactory = $cacheConfig->getCacheFactory();
173 279
            $this->cache  = $cacheFactory->createCache($this);
174
        }
175 2180
    }
176
177
    /**
178
     * {@inheritDoc}
179
     */
180 1947
    public function getConnection()
181
    {
182 1947
        return $this->conn;
183
    }
184
185
    /**
186
     * Gets the metadata factory used to gather the metadata of classes.
187
     *
188
     * @return ClassMetadataFactory
189
     */
190 747
    public function getMetadataFactory()
191
    {
192 747
        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 1012
    public function getIdentifierFlattener() : IdentifierFlattener
208
    {
209 1012
        return $this->identifierFlattener;
210
    }
211
212
    /**
213
     * {@inheritDoc}
214
     */
215
    public function beginTransaction()
216
    {
217
        $this->conn->beginTransaction();
218
    }
219
220
    /**
221
     * {@inheritDoc}
222
     */
223 211
    public function getCache()
224
    {
225 211
        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
    public function commit()
254
    {
255
        $this->conn->commit();
256
    }
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 1819
    public function getClassMetadata($className) : Mapping\ClassMetadata
285
    {
286 1819
        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...
287
    }
288
289
    /**
290
     * {@inheritDoc}
291
     */
292 917
    public function createQuery($dql = '')
293
    {
294 917
        $query = new Query($this);
295
296 917
        if (! empty($dql)) {
297 912
            $query->setDQL($dql);
298
        }
299
300 917
        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 123
    public function createQueryBuilder()
320
    {
321 123
        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 923
    public function flush()
354
    {
355 923
        $this->errorIfClosed();
356
357 922
        $this->unitOfWork->commit();
358 912
    }
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 367
    public function find($entityName, $id, $lockMode = null, $lockVersion = null)
379
    {
380 367
        $class     = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\'));
381 367
        $className = $class->getClassName();
382
383 367
        if ($lockMode !== null) {
384 6
            $this->checkLockRequirements($lockMode, $class);
385
        }
386
387 364
        if (! is_array($id)) {
388 335
            if ($class->isIdentifierComposite()) {
389
                throw ORMInvalidArgumentException::invalidCompositeIdentifier();
390
            }
391
392 335
            $id = [$class->identifier[0] => $id];
393
        }
394
395 364
        foreach ($id as $i => $value) {
396 364
            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 363
        $sortedId = [];
406
407 363
        foreach ($class->identifier as $identifier) {
408 363
            if (! isset($id[$identifier])) {
409 1
                throw MissingIdentifierField::fromFieldAndClass($identifier, $className);
410
            }
411
412 362
            $sortedId[$identifier] = $id[$identifier];
413 362
            unset($id[$identifier]);
414
        }
415
416 362
        if ($id) {
417 1
            throw UnrecognizedIdentifierFields::fromClassAndFieldNames($className, array_keys($id));
418
        }
419
420 361
        $unitOfWork = $this->getUnitOfWork();
421
422
        // Check identity map first
423 361
        $entity = $unitOfWork->tryGetById($sortedId, $class->getRootClassName());
424 361
        if ($entity !== false) {
425 44
            if (! ($entity instanceof $className)) {
426
                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 341
        $persister = $unitOfWork->getEntityPersister($className);
446
447
        switch (true) {
448 341
            case $lockMode === LockMode::OPTIMISTIC:
449 1
                $entity = $persister->load($sortedId);
450
451 1
                $unitOfWork->lock($entity, $lockMode, $lockVersion);
452
453 1
                return $entity;
454 340
            case $lockMode === LockMode::PESSIMISTIC_READ:
455 340
            case $lockMode === LockMode::PESSIMISTIC_WRITE:
456
                return $persister->load($sortedId, null, null, [], $lockMode);
457
            default:
458 340
                return $persister->loadById($sortedId);
459
        }
460
    }
461
462
    /**
463
     * {@inheritDoc}
464
     */
465 87
    public function getReference($entityName, $id)
466
    {
467 87
        $class     = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\'));
468 87
        $className = $class->getClassName();
469
470 87
        if (! is_array($id)) {
471 37
            if ($class->isIdentifierComposite()) {
472
                throw ORMInvalidArgumentException::invalidCompositeIdentifier();
473
            }
474
475 37
            $id = [$class->identifier[0] => $id];
476
        }
477
478 87
        $scalarId = [];
479
480 87
        foreach ($id as $i => $value) {
481 87
            $scalarId[$i] = $value;
482
483 87
            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 87
        $sortedId = [];
493
494 87
        foreach ($class->identifier as $identifier) {
495 87
            if (! isset($scalarId[$identifier])) {
496
                throw MissingIdentifierField::fromFieldAndClass($identifier, $className);
497
            }
498
499 87
            $sortedId[$identifier] = $scalarId[$identifier];
500 87
            unset($scalarId[$identifier]);
501
        }
502
503 87
        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 86
        $entity = $this->unitOfWork->tryGetById($sortedId, $class->getRootClassName());
509 86
        if ($entity !== false) {
510 27
            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 82
        if ($class->getSubClasses()) {
514 1
            return $this->find($entityName, $sortedId);
515
        }
516
517 82
        $entity = $this->proxyFactory->getProxy($class, $id);
518
519 82
        $this->unitOfWork->registerManaged($entity, $sortedId, []);
520
521 82
        if ($entity instanceof EntityManagerAware) {
522 3
            $entity->injectEntityManager($this, $class);
523
        }
524
525 82
        return $entity;
526
    }
527
528
    /**
529
     * {@inheritDoc}
530
     */
531 3
    public function getPartialReference($entityName, $id)
532
    {
533 3
        $class     = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\'));
534 3
        $className = $class->getClassName();
535
536 3
        if (! is_array($id)) {
537 3
            if ($class->isIdentifierComposite()) {
538
                throw ORMInvalidArgumentException::invalidCompositeIdentifier();
539
            }
540
541 3
            $id = [$class->identifier[0] => $id];
542
        }
543
544 3
        foreach ($id as $i => $value) {
545 3
            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 3
        $sortedId = [];
555
556 3
        foreach ($class->identifier as $identifier) {
557 3
            if (! isset($id[$identifier])) {
558
                throw MissingIdentifierField::fromFieldAndClass($identifier, $className);
559
            }
560
561 3
            $sortedId[$identifier] = $id[$identifier];
562 3
            unset($id[$identifier]);
563
        }
564
565 3
        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 3
        $entity = $this->unitOfWork->tryGetById($sortedId, $class->getRootClassName());
571 3
        if ($entity !== false) {
572
            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 1118
    public function clear($entityName = null)
593
    {
594 1118
        $this->unitOfWork->clear();
595
596 1118
        $this->unitOfWork = new UnitOfWork($this);
597
598 1118
        if ($this->eventManager->hasListeners(Events::onClear)) {
599 8
            $this->eventManager->dispatchEvent(Events::onClear, new Event\OnClearEventArgs($this));
600
        }
601 1118
    }
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 917
    public function persist($entity)
628
    {
629 917
        if (! is_object($entity)) {
630 1
            throw ORMInvalidArgumentException::invalidObject('EntityManager#persist()', $entity);
631
        }
632
633 916
        $this->errorIfClosed();
634
635 915
        $this->unitOfWork->persist($entity);
636 914
    }
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 43
    public function remove($entity)
650
    {
651 43
        if (! is_object($entity)) {
652 1
            throw ORMInvalidArgumentException::invalidObject('EntityManager#remove()', $entity);
653
        }
654
655 42
        $this->errorIfClosed();
656
657 41
        $this->unitOfWork->remove($entity);
658 41
    }
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 13
    public function refresh($entity)
670
    {
671 13
        if (! is_object($entity)) {
672 1
            throw ORMInvalidArgumentException::invalidObject('EntityManager#refresh()', $entity);
673
        }
674
675 12
        $this->errorIfClosed();
676
677 11
        $this->unitOfWork->refresh($entity);
678 11
    }
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 128
    public function getRepository($entityName)
696
    {
697 128
        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 14
    public function contains($entity)
708
    {
709 14
        return $this->unitOfWork->isScheduledForInsert($entity)
710 14
            || ($this->unitOfWork->isInIdentityMap($entity) && ! $this->unitOfWork->isScheduledForDelete($entity));
711
    }
712
713
    /**
714
     * {@inheritDoc}
715
     */
716 2180
    public function getEventManager()
717
    {
718 2180
        return $this->eventManager;
719
    }
720
721
    /**
722
     * {@inheritDoc}
723
     */
724 2180
    public function getConfiguration()
725
    {
726 2180
        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 928
    private function errorIfClosed()
735
    {
736 928
        if ($this->closed) {
737 4
            throw EntityManagerClosed::create();
738
        }
739 924
    }
740
741
    /**
742
     * {@inheritDoc}
743
     */
744 2
    public function isOpen()
745
    {
746 2
        return ! $this->closed;
747
    }
748
749
    /**
750
     * {@inheritDoc}
751
     */
752 1268
    public function getUnitOfWork()
753
    {
754 1268
        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 810
    public function newHydrator($hydrationMode)
769
    {
770 810
        switch ($hydrationMode) {
771
            case Query::HYDRATE_OBJECT:
772 561
                return new Internal\Hydration\ObjectHydrator($this);
773
            case Query::HYDRATE_ARRAY:
774 46
                return new Internal\Hydration\ArrayHydrator($this);
775
            case Query::HYDRATE_SCALAR:
776 81
                return new Internal\Hydration\ScalarHydrator($this);
777
            case Query::HYDRATE_SINGLE_SCALAR:
778 11
                return new Internal\Hydration\SingleScalarHydrator($this);
779
            case Query::HYDRATE_SIMPLEOBJECT:
780 365
                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 162
    public function getProxyFactory()
795
    {
796 162
        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 2180
    public static function create($connection, Configuration $config, ?EventManager $eventManager = null)
820
    {
821 2180
        if (! $config->getMetadataDriverImpl()) {
822
            throw MissingMappingDriverImplementation::create();
823
        }
824
825 2180
        $connection = static::createConnection($connection, $config, $eventManager);
826
827 2180
        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 2180
    protected static function createConnection($connection, Configuration $config, ?EventManager $eventManager = null)
843
    {
844 2180
        if (is_array($connection)) {
845
            return DriverManager::getConnection($connection, $config, $eventManager ?: new EventManager());
846
        }
847
848 2180
        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 2180
        if ($eventManager !== null && $connection->getEventManager() !== $eventManager) {
859
            throw MismatchedEventManager::create();
860
        }
861
862 2180
        return $connection;
863
    }
864
865
    /**
866
     * {@inheritDoc}
867
     */
868 534
    public function getFilters()
869
    {
870 534
        if ($this->filterCollection === null) {
871 534
            $this->filterCollection = new FilterCollection($this);
872
        }
873
874 534
        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 729
    public function hasFilters()
889
    {
890 729
        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