Completed
Pull Request — master (#7448)
by Ilya
11:12
created

EntityManager::createQuery()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

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

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