Failed Conditions
Pull Request — master (#7546)
by Šimon
09:39
created

EntityManager::initializeObject()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 0
cts 2
cp 0
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 ReflectionClass;
32
use ReflectionException;
33
use Throwable;
34
use function array_keys;
35
use function get_class;
36
use function gettype;
37
use function is_array;
38
use function is_object;
39
use function ltrim;
40
use function sprintf;
41
42
/**
43
 * The EntityManager is the central access point to ORM functionality.
44
 *
45
 * It is a facade to all different ORM subsystems such as UnitOfWork,
46
 * Query Language and Repository API. Instantiation is done through
47
 * the static create() method. The quickest way to obtain a fully
48
 * configured EntityManager is:
49
 *
50
 *     use Doctrine\ORM\Tools\Setup;
51
 *     use Doctrine\ORM\EntityManager;
52
 *
53
 *     $paths = array('/path/to/entity/mapping/files');
54
 *
55
 *     $config = Setup::createAnnotationMetadataConfiguration($paths);
56
 *     $dbParams = array('driver' => 'pdo_sqlite', 'memory' => true);
57
 *     $entityManager = EntityManager::create($dbParams, $config);
58
 *
59
 * For more information see
60
 * {@link http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/configuration.html}
61
 *
62
 * You should never attempt to inherit from the EntityManager: Inheritance
63
 * is not a valid extension point for the EntityManager. Instead you
64
 * should take a look at the {@see \Doctrine\ORM\Decorator\EntityManagerDecorator}
65
 * and wrap your entity manager in a decorator.
66
 */
67
final class EntityManager implements EntityManagerInterface
68
{
69
    /**
70
     * The used Configuration.
71
     *
72
     * @var Configuration
73
     */
74
    private $config;
75
76
    /**
77
     * The database connection used by the EntityManager.
78
     *
79
     * @var Connection
80
     */
81
    private $conn;
82
83
    /**
84
     * The metadata factory, used to retrieve the ORM metadata of entity classes.
85
     *
86
     * @var ClassMetadataFactory
87
     */
88
    private $metadataFactory;
89
90
    /**
91
     * The UnitOfWork used to coordinate object-level transactions.
92
     *
93
     * @var UnitOfWork
94
     */
95
    private $unitOfWork;
96
97
    /**
98
     * The event manager that is the central point of the event system.
99
     *
100
     * @var EventManager
101
     */
102
    private $eventManager;
103
104
    /**
105
     * The proxy factory used to create dynamic proxies.
106
     *
107
     * @var ProxyFactory
108
     */
109
    private $proxyFactory;
110
111
    /**
112
     * The repository factory used to create dynamic repositories.
113
     *
114
     * @var RepositoryFactory
115
     */
116
    private $repositoryFactory;
117
118
    /**
119
     * The expression builder instance used to generate query expressions.
120
     *
121
     * @var Expr
122
     */
123
    private $expressionBuilder;
124
125
    /**
126
     * The IdentifierFlattener used for manipulating identifiers
127
     *
128
     * @var IdentifierFlattener
129
     */
130
    private $identifierFlattener;
131
132
    /**
133
     * Whether the EntityManager is closed or not.
134
     *
135
     * @var bool
136
     */
137
    private $closed = false;
138
139
    /**
140
     * Collection of query filters.
141
     *
142
     * @var FilterCollection
143
     */
144
    private $filterCollection;
145
146
    /** @var Cache The second level cache regions API. */
147
    private $cache;
148
149
    /**
150
     * Creates a new EntityManager that operates on the given database connection
151
     * and uses the given Configuration and EventManager implementations.
152
     */
153 2272
    protected function __construct(Connection $conn, Configuration $config, EventManager $eventManager)
154
    {
155 2272
        $this->conn         = $conn;
156 2272
        $this->config       = $config;
157 2272
        $this->eventManager = $eventManager;
158
159 2272
        $metadataFactoryClassName = $config->getClassMetadataFactoryName();
160
161 2272
        $this->metadataFactory = new $metadataFactoryClassName();
162
163 2272
        $this->metadataFactory->setEntityManager($this);
164 2272
        $this->metadataFactory->setCacheDriver($this->config->getMetadataCacheImpl());
165
166 2272
        $this->repositoryFactory   = $config->getRepositoryFactory();
167 2272
        $this->unitOfWork          = new UnitOfWork($this);
168 2272
        $this->proxyFactory        = new StaticProxyFactory($this, $this->config->buildGhostObjectFactory());
169 2272
        $this->identifierFlattener = new IdentifierFlattener($this->unitOfWork, $this->metadataFactory);
170
171 2272
        if ($config->isSecondLevelCacheEnabled()) {
172 280
            $cacheConfig  = $config->getSecondLevelCacheConfiguration();
173 280
            $cacheFactory = $cacheConfig->getCacheFactory();
174 280
            $this->cache  = $cacheFactory->createCache($this);
175
        }
176 2272
    }
177
178
    /**
179
     * {@inheritDoc}
180
     */
181 1804
    public function getConnection()
182
    {
183 1804
        return $this->conn;
184
    }
185
186
    /**
187
     * Gets the metadata factory used to gather the metadata of classes.
188
     *
189
     * @return ClassMetadataFactory
190
     */
191 814
    public function getMetadataFactory()
192
    {
193 814
        return $this->metadataFactory;
194
    }
195
196
    /**
197
     * {@inheritDoc}
198
     */
199 17
    public function getExpressionBuilder()
200
    {
201 17
        if ($this->expressionBuilder === null) {
202 17
            $this->expressionBuilder = new Query\Expr();
203
        }
204
205 17
        return $this->expressionBuilder;
206
    }
207
208 1100
    public function getIdentifierFlattener() : IdentifierFlattener
209
    {
210 1100
        return $this->identifierFlattener;
211
    }
212
213
    /**
214
     * {@inheritDoc}
215
     */
216 1
    public function beginTransaction()
217
    {
218 1
        $this->conn->beginTransaction();
219 1
    }
220
221
    /**
222
     * {@inheritDoc}
223
     */
224 212
    public function getCache()
225
    {
226 212
        return $this->cache;
227
    }
228
229
    /**
230
     * {@inheritDoc}
231
     */
232 6
    public function transactional(callable $func)
233
    {
234 6
        $this->conn->beginTransaction();
235
236
        try {
237 6
            $return = $func($this);
238
239 5
            $this->flush();
240 5
            $this->conn->commit();
241
242 5
            return $return;
243 1
        } catch (Throwable $exception) {
244 1
            $this->close();
245
            try {
246 1
                $this->conn->rollBack();
247
            } catch (Throwable $rollbackException) {
248
                $reflection = new ReflectionClass($rollbackException);
249
                $prop = $reflection->getProperty('previous');
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 7 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
250
                $prop->setAccessible('true');
0 ignored issues
show
Bug introduced by
'true' of type string is incompatible with the type boolean expected by parameter $accessible of ReflectionProperty::setAccessible(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

250
                $prop->setAccessible(/** @scrutinizer ignore-type */ 'true');
Loading history...
251
                $prop->setValue($rollbackException, $exception);
252
                $prop->setAccessible('false');
253
254
                throw $rollbackException;
255
            }
256
257 1
            throw $exception;
258
        }
259
    }
260
261
    /**
262
     * {@inheritDoc}
263
     */
264 1
    public function commit()
265
    {
266 1
        $this->conn->commit();
267 1
    }
268
269
    /**
270
     * {@inheritDoc}
271
     */
272
    public function rollback()
273
    {
274
        $this->conn->rollBack();
275
    }
276
277
    /**
278
     * Returns the ORM metadata descriptor for a class.
279
     *
280
     * The class name must be the fully-qualified class name without a leading backslash
281
     * (as it is returned by get_class($obj)) or an aliased class name.
282
     *
283
     * Examples:
284
     * MyProject\Domain\User
285
     * sales:PriceRequest
286
     *
287
     * {@internal Performance-sensitive method. }}
288
     *
289
     * @param string $className
290
     *
291
     * @throws ReflectionException
292
     * @throws InvalidArgumentException
293
     * @throws MappingException
294
     */
295 1907
    public function getClassMetadata($className) : Mapping\ClassMetadata
296
    {
297 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...
298
    }
299
300
    /**
301
     * {@inheritDoc}
302
     */
303 957
    public function createQuery($dql = '')
304
    {
305 957
        $query = new Query($this);
306
307 957
        if (! empty($dql)) {
308 952
            $query->setDQL($dql);
309
        }
310
311 957
        return $query;
312
    }
313
314
    /**
315
     * {@inheritDoc}
316
     */
317 13
    public function createNativeQuery($sql, ResultSetMapping $rsm)
318
    {
319 13
        $query = new NativeQuery($this);
320
321 13
        $query->setSQL($sql);
322 13
        $query->setResultSetMapping($rsm);
323
324 13
        return $query;
325
    }
326
327
    /**
328
     * {@inheritDoc}
329
     */
330 128
    public function createQueryBuilder()
331
    {
332 128
        return new QueryBuilder($this);
333
    }
334
335
    /**
336
     * {@inheritDoc}
337
     *
338
     * @deprecated
339
     */
340
    public function merge($object)
341
    {
342
        throw new BadMethodCallException('@TODO method disabled - will be removed in 3.0 with a release of doctrine/common');
343
    }
344
345
    /**
346
     * {@inheritDoc}
347
     *
348
     * @deprecated
349
     */
350
    public function detach($object)
351
    {
352
        throw new BadMethodCallException('@TODO method disabled - will be removed in 3.0 with a release of doctrine/common');
353
    }
354
355
    /**
356
     * Flushes all changes to objects that have been queued up to now to the database.
357
     * This effectively synchronizes the in-memory state of managed objects with the
358
     * database.
359
     *
360
     * @throws OptimisticLockException If a version check on an entity that
361
     *         makes use of optimistic locking fails.
362
     * @throws ORMException
363
     */
364 1011
    public function flush()
365
    {
366 1011
        $this->errorIfClosed();
367
368 1010
        $this->unitOfWork->commit();
369 1000
    }
370
371
    /**
372
     * Finds an Entity by its identifier.
373
     *
374
     * @param string   $entityName  The class name of the entity to find.
375
     * @param mixed    $id          The identity of the entity to find.
376
     * @param int|null $lockMode    One of the \Doctrine\DBAL\LockMode::* constants
377
     *                              or NULL if no specific lock mode should be used
378
     *                              during the search.
379
     * @param int|null $lockVersion The version of the entity to find when using
380
     *                              optimistic locking.
381
     *
382
     * @return object|null The entity instance or NULL if the entity can not be found.
383
     *
384
     * @throws OptimisticLockException
385
     * @throws ORMInvalidArgumentException
386
     * @throws TransactionRequiredException
387
     * @throws ORMException
388
     */
389 406
    public function find($entityName, $id, $lockMode = null, $lockVersion = null)
390
    {
391 406
        $class     = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\'));
392 406
        $className = $class->getClassName();
393
394 406
        if ($lockMode !== null) {
395 6
            $this->checkLockRequirements($lockMode, $class);
396
        }
397
398 403
        if (! is_array($id)) {
399 371
            if ($class->isIdentifierComposite()) {
400
                throw ORMInvalidArgumentException::invalidCompositeIdentifier();
401
            }
402
403 371
            $id = [$class->identifier[0] => $id];
404
        }
405
406 403
        foreach ($id as $i => $value) {
407 403
            if (is_object($value) && $this->metadataFactory->hasMetadataFor(StaticClassNameConverter::getClass($value))) {
408 10
                $id[$i] = $this->unitOfWork->getSingleIdentifierValue($value);
409
410 10
                if ($id[$i] === null) {
411 403
                    throw ORMInvalidArgumentException::invalidIdentifierBindingEntity();
412
                }
413
            }
414
        }
415
416 402
        $sortedId = [];
417
418 402
        foreach ($class->identifier as $identifier) {
419 402
            if (! isset($id[$identifier])) {
420 1
                throw MissingIdentifierField::fromFieldAndClass($identifier, $className);
421
            }
422
423 401
            $sortedId[$identifier] = $id[$identifier];
424 401
            unset($id[$identifier]);
425
        }
426
427 401
        if ($id) {
428 1
            throw UnrecognizedIdentifierFields::fromClassAndFieldNames($className, array_keys($id));
429
        }
430
431 400
        $unitOfWork = $this->getUnitOfWork();
432
433
        // Check identity map first
434 400
        $entity = $unitOfWork->tryGetById($sortedId, $class->getRootClassName());
435 400
        if ($entity !== false) {
436 45
            if (! ($entity instanceof $className)) {
437 1
                return null;
438
            }
439
440
            switch (true) {
441 44
                case $lockMode === LockMode::OPTIMISTIC:
442
                    $this->lock($entity, $lockMode, $lockVersion);
443
                    break;
444
445 44
                case $lockMode === LockMode::NONE:
446 44
                case $lockMode === LockMode::PESSIMISTIC_READ:
447 44
                case $lockMode === LockMode::PESSIMISTIC_WRITE:
448
                    $persister = $unitOfWork->getEntityPersister($className);
449
                    $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

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