EntityManager::find()   F
last analyzed

Complexity

Conditions 20
Paths 282

Size

Total Lines 81
Code Lines 48

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 39
CRAP Score 21.4102

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
ccs 39
cts 46
cp 0.8478
crap 21.4102
rs 2.3083

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 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