Completed
Push — master ( 984641...035d9e )
by Marco
36:13 queued 25:30
created

EntityManager::createNativeQuery()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 2
dl 0
loc 8
ccs 5
cts 5
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\ORM;
6
7
use 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
     * Creates a new EntityManager that operates on the given database connection
146
     * and uses the given Configuration and EventManager implementations.
147
     *
148
     */
149 2248
    protected function __construct(Connection $conn, Configuration $config, EventManager $eventManager)
150
    {
151 2248
        $this->conn         = $conn;
152 2248
        $this->config       = $config;
153 2248
        $this->eventManager = $eventManager;
154
155 2248
        $metadataFactoryClassName = $config->getClassMetadataFactoryName();
156
157 2248
        $this->metadataFactory = new $metadataFactoryClassName();
158
159 2248
        $this->metadataFactory->setEntityManager($this);
160 2248
        $this->metadataFactory->setCacheDriver($this->config->getMetadataCacheImpl());
161
162 2248
        $this->repositoryFactory   = $config->getRepositoryFactory();
163 2248
        $this->unitOfWork          = new UnitOfWork($this);
164 2248
        $this->proxyFactory        = new StaticProxyFactory($this, $this->config->buildGhostObjectFactory());
165 2248
        $this->identifierFlattener = new IdentifierFlattener($this->unitOfWork, $this->metadataFactory);
166
167 2248
        if ($config->isSecondLevelCacheEnabled()) {
168 280
            $cacheConfig  = $config->getSecondLevelCacheConfiguration();
169 280
            $cacheFactory = $cacheConfig->getCacheFactory();
170 280
            $this->cache  = $cacheFactory->createCache($this);
171
        }
172 2248
    }
173
174
    /**
175
     * {@inheritDoc}
176
     */
177 1782
    public function getConnection()
178
    {
179 1782
        return $this->conn;
180
    }
181
182
    /**
183
     * Gets the metadata factory used to gather the metadata of classes.
184
     *
185
     * @return ClassMetadataFactory
186
     */
187 813
    public function getMetadataFactory()
188
    {
189 813
        return $this->metadataFactory;
190
    }
191
192
    /**
193
     * {@inheritDoc}
194
     */
195 17
    public function getExpressionBuilder()
196
    {
197 17
        if ($this->expressionBuilder === null) {
198 17
            $this->expressionBuilder = new Query\Expr();
199
        }
200
201 17
        return $this->expressionBuilder;
202
    }
203
204 1095
    public function getIdentifierFlattener() : IdentifierFlattener
205
    {
206 1095
        return $this->identifierFlattener;
207
    }
208
209
    /**
210
     * {@inheritDoc}
211
     */
212 1
    public function beginTransaction()
213
    {
214 1
        $this->conn->beginTransaction();
215 1
    }
216
217
    /**
218
     * {@inheritDoc}
219
     */
220 212
    public function getCache()
221
    {
222 212
        return $this->cache;
223
    }
224
225
    /**
226
     * {@inheritDoc}
227
     */
228 6
    public function transactional(callable $func)
229
    {
230 6
        $this->conn->beginTransaction();
231
232
        try {
233 6
            $return = $func($this);
234
235 5
            $this->flush();
236 5
            $this->conn->commit();
237
238 5
            return $return;
239 1
        } catch (\Throwable $e) {
240 1
            $this->close();
241 1
            $this->conn->rollBack();
242
243 1
            throw $e;
244
        }
245
    }
246
247
    /**
248
     * {@inheritDoc}
249
     */
250 1
    public function commit()
251
    {
252 1
        $this->conn->commit();
253 1
    }
254
255
    /**
256
     * {@inheritDoc}
257
     */
258
    public function rollback()
259
    {
260
        $this->conn->rollBack();
261
    }
262
263
    /**
264
     * Returns the ORM metadata descriptor for a class.
265
     *
266
     * The class name must be the fully-qualified class name without a leading backslash
267
     * (as it is returned by get_class($obj)) or an aliased class name.
268
     *
269
     * Examples:
270
     * MyProject\Domain\User
271
     * sales:PriceRequest
272
     *
273
     * {@internal Performance-sensitive method. }}
274
     *
275
     * @param string $className
276
     *
277
     * @throws \ReflectionException
278
     * @throws \InvalidArgumentException
279
     * @throws MappingException
280
     */
281 1895
    public function getClassMetadata($className) : Mapping\ClassMetadata
282
    {
283 1895
        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...
284
    }
285
286
    /**
287
     * {@inheritDoc}
288
     */
289 954
    public function createQuery($dql = '')
290
    {
291 954
        $query = new Query($this);
292
293 954
        if (! empty($dql)) {
294 949
            $query->setDQL($dql);
295
        }
296
297 954
        return $query;
298
    }
299
300
    /**
301
     * {@inheritDoc}
302
     */
303 13
    public function createNativeQuery($sql, ResultSetMapping $rsm)
304
    {
305 13
        $query = new NativeQuery($this);
306
307 13
        $query->setSQL($sql);
308 13
        $query->setResultSetMapping($rsm);
309
310 13
        return $query;
311
    }
312
313
    /**
314
     * {@inheritDoc}
315
     */
316 128
    public function createQueryBuilder()
317
    {
318 128
        return new QueryBuilder($this);
319
    }
320
321
    /**
322
     * {@inheritDoc}
323
     *
324
     * @deprecated
325
     */
326
    public function merge($object)
327
    {
328
        throw new \BadMethodCallException('@TODO method disabled - will be removed in 3.0 with a release of doctrine/common');
329
    }
330
331
    /**
332
     * {@inheritDoc}
333
     *
334
     * @deprecated
335
     */
336
    public function detach($object)
337
    {
338
        throw new \BadMethodCallException('@TODO method disabled - will be removed in 3.0 with a release of doctrine/common');
339
    }
340
341
    /**
342
     * Flushes all changes to objects that have been queued up to now to the database.
343
     * This effectively synchronizes the in-memory state of managed objects with the
344
     * database.
345
     *
346
     * @throws OptimisticLockException If a version check on an entity that
347
     *         makes use of optimistic locking fails.
348
     * @throws ORMException
349
     */
350 1006
    public function flush()
351
    {
352 1006
        $this->errorIfClosed();
353
354 1005
        $this->unitOfWork->commit();
355 995
    }
356
357
    /**
358
     * Finds an Entity by its identifier.
359
     *
360
     * @param string   $entityName  The class name of the entity to find.
361
     * @param mixed    $id          The identity of the entity to find.
362
     * @param int|null $lockMode    One of the \Doctrine\DBAL\LockMode::* constants
363
     *                              or NULL if no specific lock mode should be used
364
     *                              during the search.
365
     * @param int|null $lockVersion The version of the entity to find when using
366
     *                              optimistic locking.
367
     *
368
     * @return object|null The entity instance or NULL if the entity can not be found.
369
     *
370
     * @throws OptimisticLockException
371
     * @throws ORMInvalidArgumentException
372
     * @throws TransactionRequiredException
373
     * @throws ORMException
374
     */
375 404
    public function find($entityName, $id, $lockMode = null, $lockVersion = null)
376
    {
377 404
        $class     = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\'));
378 404
        $className = $class->getClassName();
379
380 404
        if ($lockMode !== null) {
381 5
            $this->checkLockRequirements($lockMode, $class);
382
        }
383
384 401
        if (! is_array($id)) {
385 369
            if ($class->isIdentifierComposite()) {
386
                throw ORMInvalidArgumentException::invalidCompositeIdentifier();
387
            }
388
389 369
            $id = [$class->identifier[0] => $id];
390
        }
391
392 401
        foreach ($id as $i => $value) {
393 401
            if (is_object($value) && $this->metadataFactory->hasMetadataFor(StaticClassNameConverter::getClass($value))) {
394 10
                $id[$i] = $this->unitOfWork->getSingleIdentifierValue($value);
395
396 10
                if ($id[$i] === null) {
397 401
                    throw ORMInvalidArgumentException::invalidIdentifierBindingEntity();
398
                }
399
            }
400
        }
401
402 400
        $sortedId = [];
403
404 400
        foreach ($class->identifier as $identifier) {
405 400
            if (! isset($id[$identifier])) {
406 1
                throw MissingIdentifierField::fromFieldAndClass($identifier, $className);
407
            }
408
409 399
            $sortedId[$identifier] = $id[$identifier];
410 399
            unset($id[$identifier]);
411
        }
412
413 399
        if ($id) {
414 1
            throw UnrecognizedIdentifierFields::fromClassAndFieldNames($className, array_keys($id));
415
        }
416
417 398
        $unitOfWork = $this->getUnitOfWork();
418
419
        // Check identity map first
420 398
        $entity = $unitOfWork->tryGetById($sortedId, $class->getRootClassName());
421 398
        if ($entity !== false) {
422 45
            if (! ($entity instanceof $className)) {
423 1
                return null;
424
            }
425
426
            switch (true) {
427 44
                case $lockMode === LockMode::OPTIMISTIC:
428
                    $this->lock($entity, $lockMode, $lockVersion);
429
                    break;
430
431 44
                case $lockMode === LockMode::NONE:
432 44
                case $lockMode === LockMode::PESSIMISTIC_READ:
433 44
                case $lockMode === LockMode::PESSIMISTIC_WRITE:
434
                    $persister = $unitOfWork->getEntityPersister($className);
435
                    $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

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