Failed Conditions
Pull Request — master (#7501)
by Chris
11:22
created

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

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