Completed
Pull Request — master (#7143)
by Mike
08:17
created

EntityManager::getProxyFactory()   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 0
dl 0
loc 3
ccs 0
cts 0
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 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\Mapping\ClassMetadataFactory;
14
use Doctrine\ORM\Mapping\MetadataCollection;
15
use Doctrine\ORM\Proxy\Factory\ProxyFactory;
16
use Doctrine\ORM\Proxy\Factory\StaticProxyFactory;
17
use Doctrine\ORM\Query\Expr;
18
use Doctrine\ORM\Query\FilterCollection;
19
use Doctrine\ORM\Query\ResultSetMapping;
20
use Doctrine\ORM\Repository\RepositoryFactory;
21
use Doctrine\ORM\Utility\IdentifierFlattener;
22
use Doctrine\ORM\Utility\StaticClassNameConverter;
23
use function array_keys;
24
use function get_class;
25
use function gettype;
26
use function is_array;
27
use function is_object;
28
use function ltrim;
29
use function sprintf;
30
31
/**
32
 * The EntityManager is the central access point to ORM functionality.
33
 *
34
 * It is a facade to all different ORM subsystems such as UnitOfWork,
35
 * Query Language and Repository API. Instantiation is done through
36
 * the static create() method. The quickest way to obtain a fully
37
 * configured EntityManager is:
38
 *
39
 *     use Doctrine\ORM\Tools\Setup;
40
 *     use Doctrine\ORM\EntityManager;
41
 *
42
 *     $paths = array('/path/to/entity/mapping/files');
43
 *
44
 *     $config = Setup::createAnnotationMetadataConfiguration($paths);
45
 *     $dbParams = array('driver' => 'pdo_sqlite', 'memory' => true);
46
 *     $entityManager = EntityManager::create($dbParams, $config);
47
 *
48
 * For more information see
49
 * {@link http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/configuration.html}
50
 *
51
 * You should never attempt to inherit from the EntityManager: Inheritance
52
 * is not a valid extension point for the EntityManager. Instead you
53
 * should take a look at the {@see \Doctrine\ORM\Decorator\EntityManagerDecorator}
54
 * and wrap your entity manager in a decorator.
55
 */
56
final class EntityManager implements EntityManagerInterface
57
{
58
    /**
59
     * The used Configuration.
60
     *
61
     * @var Configuration
62
     */
63
    private $config;
64
65
    /**
66
     * The database connection used by the EntityManager.
67
     *
68
     * @var Connection
69
     */
70
    private $conn;
71
72
    /**
73
     * The UnitOfWork used to coordinate object-level transactions.
74
     *
75
     * @var UnitOfWork
76
     */
77
    private $unitOfWork;
78
79
    /**
80
     * The event manager that is the central point of the event system.
81
     *
82
     * @var EventManager
83
     */
84
    private $eventManager;
85
86
    /**
87
     * The proxy factory used to create dynamic proxies.
88
     *
89
     * @var ProxyFactory
90
     */
91
    private $proxyFactory;
92
93
    /**
94
     * The repository factory used to create dynamic repositories.
95
     *
96
     * @var RepositoryFactory
97
     */
98
    private $repositoryFactory;
99
100
    /**
101
     * The expression builder instance used to generate query expressions.
102
     *
103
     * @var Expr
104
     */
105
    private $expressionBuilder;
106
107
    /**
108
     * The IdentifierFlattener used for manipulating identifiers
109
     *
110
     * @var IdentifierFlattener
111
     */
112
    private $identifierFlattener;
113
114
    /**
115
     * Whether the EntityManager is closed or not.
116
     *
117
     * @var bool
118
     */
119
    private $closed = false;
120
121
    /**
122
     * Collection of query filters.
123
     *
124
     * @var FilterCollection
125
     */
126
    private $filterCollection;
127
128
    /** @var Cache The second level cache regions API. */
129
    private $cache;
130
131
    /** @var MetadataCollection */
132
    private $metadatas;
133
134
    /**
135
     * Creates a new EntityManager that operates on the given database connection
136
     * and uses the given Configuration and EventManager implementations.
137
     *
138
     */
139
    protected function __construct(Connection $conn, Configuration $config, EventManager $eventManager, MetadataCollection $metadataCollection)
140
    {
141
        $this->conn            = $conn;
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 9 spaces but found 12 spaces

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...
142 2247
        $this->config          = $config;
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 7 spaces but found 10 spaces

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...
143
        $this->eventManager    = $eventManager;
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 1 space but found 4 spaces

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...
144 2247
        $this->metadatas       = $metadataCollection;
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 4 spaces but found 7 spaces

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...
145 2247
146 2247
        $this->repositoryFactory   = $config->getRepositoryFactory();
147
        $this->unitOfWork          = new UnitOfWork($this);
148 2247
        $this->proxyFactory        = new StaticProxyFactory($this, $this->config->buildGhostObjectFactory());
149
        $this->identifierFlattener = new IdentifierFlattener($this->unitOfWork, $this->metadatas);
150 2247
151
        if ($config->isSecondLevelCacheEnabled()) {
152 2247
            $cacheConfig  = $config->getSecondLevelCacheConfiguration();
0 ignored issues
show
Unused Code introduced by
The assignment to $cacheConfig is dead and can be removed.
Loading history...
Coding Style introduced by
Equals sign not aligned correctly; expected 1 space but found 2 spaces
Loading history...
153 2247
            //$cacheFactory = $cacheConfig->getCacheFactory();
154
            //$this->cache  = $cacheFactory->createCache($this);
155 2247
        }
156 2247
    }
157 2247
158 2247
    /**
159
     * @return MetadataCollection
160 2247
     */
161 280
    public function getMetadatas(): MetadataCollection
0 ignored issues
show
introduced by
Method \Doctrine\ORM\EntityManager::getMetadatas() does not need documentation comment.
Loading history...
introduced by
There must be exactly 1 whitespace between closing parenthesis and return type colon.
Loading history...
162 280
    {
163 280
        return $this->metadatas;
164
    }
165 2247
166
    /**
167
     * {@inheritDoc}
168
     */
169
    public function getConnection()
170 1781
    {
171
        return $this->conn;
172 1781
    }
173
174
    /**
175
     * {@inheritDoc}
176
     */
177
    public function getExpressionBuilder()
178
    {
179
        if ($this->expressionBuilder === null) {
180 812
            $this->expressionBuilder = new Query\Expr();
181
        }
182 812
183
        return $this->expressionBuilder;
184
    }
185
186
    public function getIdentifierFlattener() : IdentifierFlattener
187
    {
188 17
        return $this->identifierFlattener;
189
    }
190 17
191 17
    /**
192
     * {@inheritDoc}
193
     */
194 17
    public function beginTransaction()
195
    {
196
        $this->conn->beginTransaction();
197 1094
    }
198
199 1094
    /**
200
     * {@inheritDoc}
201
     */
202
    public function getCache()
203
    {
204
        return $this->cache;
205 1
    }
206
207 1
    /**
208 1
     * {@inheritDoc}
209
     */
210
    public function transactional(callable $func)
211
    {
212
        $this->conn->beginTransaction();
213 212
214
        try {
215 212
            $return = $func($this);
216
217
            $this->flush();
218
            $this->conn->commit();
219
220
            return $return;
221 6
        } catch (\Throwable $e) {
222
            $this->close();
223 6
            $this->conn->rollBack();
224
225
            throw $e;
226 6
        }
227
    }
228 5
229 5
    /**
230
     * {@inheritDoc}
231 5
     */
232 1
    public function commit()
233 1
    {
234 1
        $this->conn->commit();
235
    }
236 1
237
    /**
238
     * {@inheritDoc}
239
     */
240
    public function rollback()
241
    {
242
        $this->conn->rollBack();
243 1
    }
244
245 1
    /**
246 1
     * Returns the ORM metadata descriptor for a class.
247
     *
248
     * The class name must be the fully-qualified class name without a leading backslash
249
     * (as it is returned by get_class($obj)) or an aliased class name.
250
     *
251
     * Examples:
252
     * MyProject\Domain\User
253
     * sales:PriceRequest
254
     *
255
     * {@internal Performance-sensitive method. }}
256
     *
257
     * @param string $className
258
     *
259
     * @throws \ReflectionException
260
     * @throws \InvalidArgumentException
261
     * @throws MappingException
262
     */
263
    public function getClassMetadata($className) : Mapping\ClassMetadata
264
    {
265
        $className = StaticClassNameConverter::getRealClass($className);
266
        return $this->metadatas->get($className);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->metadatas->get($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...
267
    }
268
269
    /**
270
     * {@inheritDoc}
271
     */
272
    public function createQuery($dql = '')
273
    {
274 1894
        $query = new Query($this);
275
276 1894
        if (! empty($dql)) {
277
            $query->setDQL($dql);
278
        }
279
280
        return $query;
281
    }
282 954
283
    /**
284 954
     * {@inheritDoc}
285
     */
286 954
    public function createNativeQuery($sql, ResultSetMapping $rsm)
287 949
    {
288
        $query = new NativeQuery($this);
289
290 954
        $query->setSQL($sql);
291
        $query->setResultSetMapping($rsm);
292
293
        return $query;
294
    }
295
296 13
    /**
297
     * {@inheritDoc}
298 13
     */
299
    public function createQueryBuilder()
300 13
    {
301 13
        return new QueryBuilder($this);
302
    }
303 13
304
    /**
305
     * {@inheritDoc}
306
     *
307
     * @deprecated
308
     */
309 128
    public function merge($object)
310
    {
311 128
        throw new \BadMethodCallException('@TODO method disabled - will be removed in 3.0 with a release of doctrine/common');
312
    }
313
314
    /**
315
     * {@inheritDoc}
316
     *
317
     * @deprecated
318
     */
319
    public function detach($object)
320
    {
321
        throw new \BadMethodCallException('@TODO method disabled - will be removed in 3.0 with a release of doctrine/common');
322
    }
323
324
    /**
325
     * Flushes all changes to objects that have been queued up to now to the database.
326
     * This effectively synchronizes the in-memory state of managed objects with the
327
     * database.
328
     *
329
     * If an entity is explicitly passed to this method only this entity and
330
     * the cascade-persist semantics + scheduled inserts/removals are synchronized.
331
     *
332
     * @throws OptimisticLockException If a version check on an entity that
333
     *         makes use of optimistic locking fails.
334
     * @throws ORMException
335
     */
336
    public function flush()
337
    {
338
        $this->errorIfClosed();
339
340
        $this->unitOfWork->commit();
341
    }
342
343
    /**
344
     * Finds an Entity by its identifier.
345
     *
346 1005
     * @param string   $entityName  The class name of the entity to find.
347
     * @param mixed    $id          The identity of the entity to find.
348 1005
     * @param int|null $lockMode    One of the \Doctrine\DBAL\LockMode::* constants
349
     *                              or NULL if no specific lock mode should be used
350 1004
     *                              during the search.
351 994
     * @param int|null $lockVersion The version of the entity to find when using
352
     *                              optimistic locking.
353
     *
354
     * @return object|null The entity instance or NULL if the entity can not be found.
355
     *
356
     * @throws OptimisticLockException
357
     * @throws ORMInvalidArgumentException
358
     * @throws TransactionRequiredException
359
     * @throws ORMException
360
     */
361
    public function find($entityName, $id, $lockMode = null, $lockVersion = null)
362
    {
363
        $class     = $this->metadatas->get(ltrim($entityName, '\\'));
364
        $className = $class->getClassName();
365
366
        if (! is_array($id)) {
367
            if ($class->isIdentifierComposite()) {
368
                throw ORMInvalidArgumentException::invalidCompositeIdentifier();
369
            }
370
371 403
            $id = [$class->identifier[0] => $id];
372
        }
373 403
374 403
        foreach ($id as $i => $value) {
375
            if (is_object($value)) {
376 403
                try{
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after TRY keyword; 0 found
Loading history...
377 371
                    $this->metadatas->get(StaticClassNameConverter::getClass($value));
378
                    $id[$i] = $this->unitOfWork->getSingleIdentifierValue($value);
379
                } catch (\Exception $e) {
380
                    $id[$i] = null;
381 371
                }
382
383
                if ($id[$i] === null) {
384 403
                    throw ORMInvalidArgumentException::invalidIdentifierBindingEntity();
385 403
                }
386 10
            }
387
        }
388 10
389 403
        $sortedId = [];
390
391
        foreach ($class->identifier as $identifier) {
392
            if (! isset($id[$identifier])) {
393
                throw ORMException::missingIdentifierField($className, $identifier);
394 402
            }
395
396 402
            $sortedId[$identifier] = $id[$identifier];
397 402
            unset($id[$identifier]);
398 1
        }
399
400
        if ($id) {
401 401
            throw ORMException::unrecognizedIdentifierFields($className, array_keys($id));
402 401
        }
403
404
        $unitOfWork = $this->getUnitOfWork();
405 401
406 1
        // Check identity map first
407
        $entity = $unitOfWork->tryGetById($sortedId, $class->getRootClassName());
408
        if ($entity !== false) {
409 400
            if (! ($entity instanceof $className)) {
410
                return null;
411
            }
412 400
413 400
            switch (true) {
414 45
                case $lockMode === LockMode::OPTIMISTIC:
415 1
                    $this->lock($entity, $lockMode, $lockVersion);
416
                    break;
417
418
                case $lockMode === LockMode::NONE:
419 44
                case $lockMode === LockMode::PESSIMISTIC_READ:
420 1
                case $lockMode === LockMode::PESSIMISTIC_WRITE:
421
                    $persister = $unitOfWork->getEntityPersister($className);
422
                    $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

422
                    $persister->refresh($sortedId, /** @scrutinizer ignore-type */ $entity, $lockMode);
Loading history...
423 44
                    break;
424 44
            }
425 44
426
            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...
427
        }
428
429
        $persister = $unitOfWork->getEntityPersister($className);
430
431 44
        switch (true) {
432
            case $lockMode === LockMode::OPTIMISTIC:
433
                if (! $class->isVersioned()) {
434 379
                    throw OptimisticLockException::notVersioned($className);
435
                }
436
437 379
                $entity = $persister->load($sortedId);
438 1
439 1
                $unitOfWork->lock($entity, $lockMode, $lockVersion);
440
441
                return $entity;
442
443
            case $lockMode === LockMode::PESSIMISTIC_READ:
444
            case $lockMode === LockMode::PESSIMISTIC_WRITE:
445
                if (! $this->getConnection()->isTransactionActive()) {
446
                    throw TransactionRequiredException::transactionRequired();
447
                }
448 378
449 377
                return $persister->load($sortedId, null, null, [], $lockMode);
450 2
451 2
            default:
452
                return $persister->loadById($sortedId);
453
        }
454
    }
455
456
    /**
457 376
     * {@inheritDoc}
458
     */
459
    public function getReference($entityName, $id)
460
    {
461
        $class     = $this->metadatas->get(ltrim($entityName, '\\'));
462
        $className = $class->getClassName();
463
464 90
        if (! is_array($id)) {
465
            if ($class->isIdentifierComposite()) {
466 90
                throw ORMInvalidArgumentException::invalidCompositeIdentifier();
467 90
            }
468
469 90
            $id = [$class->identifier[0] => $id];
470 39
        }
471
472
        $scalarId = [];
473
474 39
        foreach ($id as $i => $value) {
475
            $scalarId[$i] = $value;
476
477 90
            if (is_object($value)) {
478
                try{
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after TRY keyword; 0 found
Loading history...
479 90
                    $this->metadatas->get(StaticClassNameConverter::getClass($value));
480 90
                    $scalarId[$i] = $this->unitOfWork->getSingleIdentifierValue($value);
481
                } catch (\Exception $e) {
482 90
                    $scalarId[$i] = null;
483 2
                }
484
485 2
                if ($scalarId[$i] === null) {
486 90
                    throw ORMInvalidArgumentException::invalidIdentifierBindingEntity();
487
                }
488
            }
489
        }
490
491 90
        $sortedId = [];
492
493 90
        foreach ($class->identifier as $identifier) {
494 90
            if (! isset($scalarId[$identifier])) {
495
                throw ORMException::missingIdentifierField($className, $identifier);
496
            }
497
498 90
            $sortedId[$identifier] = $scalarId[$identifier];
499 90
            unset($scalarId[$identifier]);
500
        }
501
502 90
        if ($scalarId) {
503 1
            throw ORMException::unrecognizedIdentifierFields($className, array_keys($scalarId));
504
        }
505
506
        // Check identity map first, if its already in there just return it.
507 89
        $entity = $this->unitOfWork->tryGetById($sortedId, $class->getRootClassName());
508 89
        if ($entity !== false) {
509 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...
510
        }
511
512 84
        if ($class->getSubClasses()) {
513 2
            return $this->find($entityName, $sortedId);
514
        }
515
516 84
        $entity = $this->proxyFactory->getProxy($class, $id);
517
518 84
        $this->unitOfWork->registerManaged($entity, $sortedId, []);
519
520 84
        if ($entity instanceof EntityManagerAware) {
521 3
            $entity->injectEntityManager($this, $class);
522
        }
523
524 84
        return $entity;
525
    }
526
527
    /**
528
     * {@inheritDoc}
529
     */
530 4
    public function getPartialReference($entityName, $id)
531
    {
532 4
        $class     = $this->metadatas->get(ltrim($entityName, '\\'));
533 4
        $className = $class->getClassName();
534
535 4
        if (! is_array($id)) {
536 4
            if ($class->isIdentifierComposite()) {
537
                throw ORMInvalidArgumentException::invalidCompositeIdentifier();
538
            }
539
540 4
            $id = [$class->identifier[0] => $id];
541
        }
542
543 4
        foreach ($id as $i => $value) {
544 4
            if (is_object($value)) {
545
                try{
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after TRY keyword; 0 found
Loading history...
546
                    $this->metadatas->get(StaticClassNameConverter::getClass($value));
547
                    $id[$i] = $this->unitOfWork->getSingleIdentifierValue($value);
548 4
                } catch (\Exception $e) {
549
                    $id[$i] = null;
550
                }
551
552
                if ($id[$i] === null) {
553 4
                    throw ORMInvalidArgumentException::invalidIdentifierBindingEntity();
554
                }
555 4
            }
556 4
        }
557
558
        $sortedId = [];
559
560 4
        foreach ($class->identifier as $identifier) {
561 4
            if (! isset($id[$identifier])) {
562
                throw ORMException::missingIdentifierField($className, $identifier);
563
            }
564 4
565
            $sortedId[$identifier] = $id[$identifier];
566
            unset($id[$identifier]);
567
        }
568
569 4
        if ($id) {
570 4
            throw ORMException::unrecognizedIdentifierFields($className, array_keys($id));
571 1
        }
572
573
        // Check identity map first, if its already in there just return it.
574 3
        $entity = $this->unitOfWork->tryGetById($sortedId, $class->getRootClassName());
575 3
        if ($entity !== false) {
576
            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...
577 3
        }
578
579 3
        $persister = $this->unitOfWork->getEntityPersister($class->getClassName());
580 3
        $entity    = $this->unitOfWork->newInstance($class);
581
582 3
        $persister->setIdentifier($entity, $sortedId);
583
584
        $this->unitOfWork->registerManaged($entity, $sortedId, []);
585
        $this->unitOfWork->markReadOnly($entity);
586
587
        return $entity;
588
    }
589
590
    /**
591
     * Clears the EntityManager. All entities that are currently managed
592 1197
     * by this EntityManager become detached.
593
     *
594 1197
     * @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...
595
     *
596 1197
     */
597
    public function clear($entityName = null)
598 1197
    {
599 9
        $this->unitOfWork->clear();
600
601 1197
        $this->unitOfWork = new UnitOfWork($this);
602
603
        if ($this->eventManager->hasListeners(Events::onClear)) {
604
            $this->eventManager->dispatchEvent(Events::onClear, new Event\OnClearEventArgs($this));
605
        }
606 18
    }
607
608 18
    /**
609
     * {@inheritDoc}
610 18
     */
611 18
    public function close()
612
    {
613
        $this->clear();
614
615
        $this->closed = true;
616
    }
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 999
     * @param object $entity The instance to make managed and persistent.
628
     *
629 999
     * @throws ORMInvalidArgumentException
630 1
     * @throws ORMException
631
     */
632
    public function persist($entity)
633 998
    {
634
        if (! is_object($entity)) {
635 997
            throw ORMInvalidArgumentException::invalidObject('EntityManager#persist()', $entity);
636 996
        }
637
638
        $this->errorIfClosed();
639
640
        $this->unitOfWork->persist($entity);
641
    }
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 49
     * @param object $entity The entity instance to remove.
650
     *
651 49
     * @throws ORMInvalidArgumentException
652 1
     * @throws ORMException
653
     */
654
    public function remove($entity)
655 48
    {
656
        if (! is_object($entity)) {
657 47
            throw ORMInvalidArgumentException::invalidObject('EntityManager#remove()', $entity);
658 47
        }
659
660
        $this->errorIfClosed();
661
662
        $this->unitOfWork->remove($entity);
663
    }
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 17
     * @param object $entity The entity to refresh.
670
     *
671 17
     * @throws ORMInvalidArgumentException
672 1
     * @throws ORMException
673
     */
674
    public function refresh($entity)
675 16
    {
676
        if (! is_object($entity)) {
677 15
            throw ORMInvalidArgumentException::invalidObject('EntityManager#refresh()', $entity);
678 15
        }
679
680
        $this->errorIfClosed();
681
682
        $this->unitOfWork->refresh($entity);
683 9
    }
684
685 9
    /**
686 2
     * {@inheritDoc}
687
     */
688
    public function lock($entity, $lockMode, $lockVersion = null)
689
    {
690
        $this->unitOfWork->lock($entity, $lockMode, $lockVersion);
691
    }
692
693
    /**
694
     * Gets the repository for an entity class.
695 137
     *
696
     * @param string $entityName The name of the entity.
697 137
     *
698
     * @return ObjectRepository|EntityRepository The repository class.
699
     */
700
    public function getRepository($entityName)
701
    {
702
        return $this->repositoryFactory->getRepository($this, $entityName);
703
    }
704
705
    /**
706
     * Determines whether an entity instance is managed in this EntityManager.
707 15
     *
708
     * @param object $entity
709 15
     *
710 15
     * @return bool TRUE if this EntityManager currently manages the given entity, FALSE otherwise.
711
     */
712
    public function contains($entity)
713
    {
714
        return $this->unitOfWork->isScheduledForInsert($entity)
715
            || ($this->unitOfWork->isInIdentityMap($entity) && ! $this->unitOfWork->isScheduledForDelete($entity));
716 2247
    }
717
718 2247
    /**
719
     * {@inheritDoc}
720
     */
721
    public function getEventManager()
722
    {
723
        return $this->eventManager;
724 2247
    }
725
726 2247
    /**
727
     * {@inheritDoc}
728
     */
729
    public function getConfiguration()
730
    {
731
        return $this->config;
732
    }
733
734 1010
    /**
735
     * Throws an exception if the EntityManager is closed or currently not active.
736 1010
     *
737 4
     * @throws ORMException If the EntityManager is closed.
738
     */
739 1006
    private function errorIfClosed()
740
    {
741
        if ($this->closed) {
742
            throw ORMException::entityManagerClosed();
743
        }
744 2
    }
745
746 2
    /**
747
     * {@inheritDoc}
748
     */
749
    public function isOpen()
750
    {
751
        return ! $this->closed;
752 1350
    }
753
754 1350
    /**
755
     * {@inheritDoc}
756
     */
757
    public function getUnitOfWork()
758
    {
759
        return $this->unitOfWork;
760
    }
761
762
    /**
763
     * {@inheritDoc}
764
     */
765
    public function getHydrator($hydrationMode)
766
    {
767
        return $this->newHydrator($hydrationMode);
768 883
    }
769
770 883
    /**
771
     * {@inheritDoc}
772 614
     */
773
    public function newHydrator($hydrationMode)
774
    {
775 45
        switch ($hydrationMode) {
776
            case Query::HYDRATE_OBJECT:
777
                return new Internal\Hydration\ObjectHydrator($this);
778 85
779
            case Query::HYDRATE_ARRAY:
780
                return new Internal\Hydration\ArrayHydrator($this);
781 13
782
            case Query::HYDRATE_SCALAR:
783
                return new Internal\Hydration\ScalarHydrator($this);
784 407
785
            case Query::HYDRATE_SINGLE_SCALAR:
786
                return new Internal\Hydration\SingleScalarHydrator($this);
787 1
788 1
            case Query::HYDRATE_SIMPLEOBJECT:
789 1
                return new Internal\Hydration\SimpleObjectHydrator($this);
790
791
            default:
792
                $class = $this->config->getCustomHydrationMode((string) $hydrationMode);
793
                if ($class !== null) {
794
                    return new $class($this);
795
                }
796
        }
797
798
        throw ORMException::invalidHydrationMode($hydrationMode);
799 166
    }
800
801 166
    /**
802
     * {@inheritDoc}
803
     */
804
    public function getProxyFactory()
805
    {
806
        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 2247
     * @return EntityManager The created EntityManager.
825
     *
826 2247
     * @throws \InvalidArgumentException
827
     * @throws ORMException
828
     */
829
    public static function create($connection, Configuration $config, ?EventManager $eventManager = null)
830 2247
    {
831
        if (! $config->getMetadataDriverImpl()) {
832 2247
            throw ORMException::missingMappingDriverImpl();
833
        }
834
835
        $connection = static::createConnection($connection, $config, $eventManager);
836
837
        $metadataFactoryClassName = $config->getClassMetadataFactoryName();
838
        /** @var \Doctrine\Common\Persistence\Mapping\ClassMetadataFactory $metadataFactory */
839
        $metadataFactory          = new $metadataFactoryClassName($config, $connection, $eventManager);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned correctly; expected 1 space but found 10 spaces
Loading history...
840
841
        return self::createWithClassMetadataFactory($connection, $config, $eventManager, $metadataFactory);
0 ignored issues
show
Bug introduced by
It seems like $eventManager can also be of type null; however, parameter $eventManager of Doctrine\ORM\EntityManag...hClassMetadataFactory() does only seem to accept Doctrine\Common\EventManager, 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

841
        return self::createWithClassMetadataFactory($connection, $config, /** @scrutinizer ignore-type */ $eventManager, $metadataFactory);
Loading history...
842
    }
843
844
    public static function createWithClassMetadataFactory(Connection $connection, Configuration $config, EventManager $eventManager, ClassMetadataFactory $metadataFactory)
845
    {
846
        $metadatas = MetadataCollection::fromClassMetadatas($metadataFactory->getAllMetadata());
847 2247
848
        return new self($connection, $config, $eventManager, $metadatas);
849 2247
    }
850
851
    public static function createWithClassMetadata(Connection $conn, Configuration $config, EventManager $eventManager, MetadataCollection $metadatas)
852
    {
853 2247
        return new self($conn, $config, $eventManager, $metadatas);
854 1
    }
855 1
856 1
    /**
857 1
     * Factory method to create Connection instances.
858 1
     *
859
     * @param Connection|mixed[] $connection   An array with the connection parameters or an existing Connection instance.
860
     * @param Configuration      $config       The Configuration instance to use.
861
     * @param EventManager       $eventManager The EventManager instance to use.
862
     *
863 2247
     * @return Connection
864
     *
865
     * @throws \InvalidArgumentException
866
     * @throws ORMException
867 2247
     */
868
    protected static function createConnection($connection, Configuration $config, ?EventManager $eventManager = null)
869
    {
870
        if (is_array($connection)) {
871
            return DriverManager::getConnection($connection, $config, $eventManager ?: new EventManager());
872
        }
873 588
874
        if (! $connection instanceof Connection) {
0 ignored issues
show
introduced by
$connection is always a sub-type of Doctrine\DBAL\Connection.
Loading history...
875 588
            throw new \InvalidArgumentException(
876 588
                sprintf(
877
                    'Invalid $connection argument of type %s given%s.',
878
                    is_object($connection) ? get_class($connection) : gettype($connection),
879 588
                    is_object($connection) ? '' : ': "' . $connection . '"'
880
                )
881
            );
882
        }
883
884
        if ($eventManager !== null && $connection->getEventManager() !== $eventManager) {
885 40
            throw ORMException::mismatchedEventManager();
886
        }
887 40
888
        return $connection;
889
    }
890
891
    /**
892
     * {@inheritDoc}
893 765
     */
894
    public function getFilters()
895 765
    {
896
        if ($this->filterCollection === null) {
897
            $this->filterCollection = new FilterCollection($this);
898
        }
899
900
        return $this->filterCollection;
901
    }
902
903
    /**
904
     * {@inheritDoc}
905
     */
906
    public function isFiltersStateClean()
907
    {
908
        return $this->filterCollection === null || $this->filterCollection->isClean();
909
    }
910
911
    /**
912
     * {@inheritDoc}
913
     */
914
    public function hasFilters()
915
    {
916
        return $this->filterCollection !== null;
917
    }
918
}
919