1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace Doctrine\ORM; |
6
|
|
|
|
7
|
|
|
use Doctrine\Common\EventManager; |
8
|
|
|
use Doctrine\DBAL\Connection; |
9
|
|
|
use Doctrine\DBAL\DriverManager; |
10
|
|
|
use Doctrine\DBAL\LockMode; |
11
|
|
|
use Doctrine\ORM\Proxy\Factory\StaticProxyFactory; |
12
|
|
|
use Doctrine\ORM\Query\FilterCollection; |
13
|
|
|
use Doctrine\ORM\Query\ResultSetMapping; |
14
|
|
|
use Doctrine\ORM\Utility\IdentifierFlattener; |
15
|
|
|
use Doctrine\ORM\Utility\StaticClassNameConverter; |
16
|
|
|
|
17
|
|
|
/** |
18
|
|
|
* The EntityManager is the central access point to ORM functionality. |
19
|
|
|
* |
20
|
|
|
* It is a facade to all different ORM subsystems such as UnitOfWork, |
21
|
|
|
* Query Language and Repository API. Instantiation is done through |
22
|
|
|
* the static create() method. The quickest way to obtain a fully |
23
|
|
|
* configured EntityManager is: |
24
|
|
|
* |
25
|
|
|
* use Doctrine\ORM\Tools\Setup; |
26
|
|
|
* use Doctrine\ORM\EntityManager; |
27
|
|
|
* |
28
|
|
|
* $paths = array('/path/to/entity/mapping/files'); |
29
|
|
|
* |
30
|
|
|
* $config = Setup::createAnnotationMetadataConfiguration($paths); |
31
|
|
|
* $dbParams = array('driver' => 'pdo_sqlite', 'memory' => true); |
32
|
|
|
* $entityManager = EntityManager::create($dbParams, $config); |
33
|
|
|
* |
34
|
|
|
* For more information see |
35
|
|
|
* {@link http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/configuration.html} |
36
|
|
|
* |
37
|
|
|
* You should never attempt to inherit from the EntityManager: Inheritance |
38
|
|
|
* is not a valid extension point for the EntityManager. Instead you |
39
|
|
|
* should take a look at the {@see \Doctrine\ORM\Decorator\EntityManagerDecorator} |
40
|
|
|
* and wrap your entity manager in a decorator. |
41
|
|
|
* |
42
|
|
|
* @since 2.0 |
43
|
|
|
* @author Benjamin Eberlei <[email protected]> |
44
|
|
|
* @author Guilherme Blanco <[email protected]> |
45
|
|
|
* @author Jonathan Wage <[email protected]> |
46
|
|
|
* @author Roman Borschel <[email protected]> |
47
|
|
|
*/ |
48
|
|
|
final class EntityManager implements EntityManagerInterface |
49
|
|
|
{ |
50
|
|
|
/** |
51
|
|
|
* The used Configuration. |
52
|
|
|
* |
53
|
|
|
* @var \Doctrine\ORM\Configuration |
54
|
|
|
*/ |
55
|
|
|
private $config; |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* The database connection used by the EntityManager. |
59
|
|
|
* |
60
|
|
|
* @var \Doctrine\DBAL\Connection |
61
|
|
|
*/ |
62
|
|
|
private $conn; |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* The metadata factory, used to retrieve the ORM metadata of entity classes. |
66
|
|
|
* |
67
|
|
|
* @var \Doctrine\ORM\Mapping\ClassMetadataFactory |
68
|
|
|
*/ |
69
|
|
|
private $metadataFactory; |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* The UnitOfWork used to coordinate object-level transactions. |
73
|
|
|
* |
74
|
|
|
* @var \Doctrine\ORM\UnitOfWork |
75
|
|
|
*/ |
76
|
|
|
private $unitOfWork; |
77
|
|
|
|
78
|
|
|
/** |
79
|
|
|
* The event manager that is the central point of the event system. |
80
|
|
|
* |
81
|
|
|
* @var \Doctrine\Common\EventManager |
82
|
|
|
*/ |
83
|
|
|
private $eventManager; |
84
|
|
|
|
85
|
|
|
/** |
86
|
|
|
* The proxy factory used to create dynamic proxies. |
87
|
|
|
* |
88
|
|
|
* @var \Doctrine\ORM\Proxy\Factory\ProxyFactory |
89
|
|
|
*/ |
90
|
|
|
private $proxyFactory; |
91
|
|
|
|
92
|
|
|
/** |
93
|
|
|
* The repository factory used to create dynamic repositories. |
94
|
|
|
* |
95
|
|
|
* @var \Doctrine\ORM\Repository\RepositoryFactory |
96
|
|
|
*/ |
97
|
|
|
private $repositoryFactory; |
98
|
|
|
|
99
|
|
|
/** |
100
|
|
|
* The expression builder instance used to generate query expressions. |
101
|
|
|
* |
102
|
|
|
* @var \Doctrine\ORM\Query\Expr |
103
|
|
|
*/ |
104
|
|
|
private $expressionBuilder; |
105
|
|
|
|
106
|
|
|
/** |
107
|
|
|
* The IdentifierFlattener used for manipulating identifiers |
108
|
|
|
* |
109
|
|
|
* @var \Doctrine\ORM\Utility\IdentifierFlattener |
110
|
|
|
*/ |
111
|
|
|
private $identifierFlattener; |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* Whether the EntityManager is closed or not. |
115
|
|
|
* |
116
|
|
|
* @var bool |
117
|
|
|
*/ |
118
|
|
|
private $closed = false; |
119
|
|
|
|
120
|
|
|
/** |
121
|
|
|
* Collection of query filters. |
122
|
|
|
* |
123
|
|
|
* @var \Doctrine\ORM\Query\FilterCollection |
124
|
|
|
*/ |
125
|
|
|
private $filterCollection; |
126
|
|
|
|
127
|
|
|
/** |
128
|
|
|
* @var \Doctrine\ORM\Cache The second level cache regions API. |
129
|
|
|
*/ |
130
|
|
|
private $cache; |
131
|
|
|
|
132
|
|
|
/** |
133
|
|
|
* Creates a new EntityManager that operates on the given database connection |
134
|
|
|
* and uses the given Configuration and EventManager implementations. |
135
|
|
|
* |
136
|
|
|
* @param \Doctrine\DBAL\Connection $conn |
137
|
|
|
* @param \Doctrine\ORM\Configuration $config |
138
|
|
|
* @param \Doctrine\Common\EventManager $eventManager |
139
|
|
|
*/ |
140
|
|
|
protected function __construct(Connection $conn, Configuration $config, EventManager $eventManager) |
141
|
|
|
{ |
142
|
|
|
$this->conn = $conn; |
143
|
|
|
$this->config = $config; |
144
|
|
|
$this->eventManager = $eventManager; |
145
|
|
|
|
146
|
|
|
$metadataFactoryClassName = $config->getClassMetadataFactoryName(); |
147
|
|
|
|
148
|
2290 |
|
$this->metadataFactory = new $metadataFactoryClassName; |
149
|
|
|
|
150
|
2290 |
|
$this->metadataFactory->setEntityManager($this); |
151
|
2290 |
|
$this->metadataFactory->setCacheDriver($this->config->getMetadataCacheImpl()); |
152
|
2290 |
|
|
153
|
|
|
$this->repositoryFactory = $config->getRepositoryFactory(); |
154
|
2290 |
|
$this->unitOfWork = new UnitOfWork($this); |
155
|
|
|
$this->proxyFactory = new StaticProxyFactory($this, $this->config->buildGhostObjectFactory()); |
156
|
2290 |
|
$this->identifierFlattener = new IdentifierFlattener($this->unitOfWork, $this->metadataFactory); |
157
|
2290 |
|
|
158
|
2290 |
|
if ($config->isSecondLevelCacheEnabled()) { |
159
|
|
|
$cacheConfig = $config->getSecondLevelCacheConfiguration(); |
160
|
2290 |
|
$cacheFactory = $cacheConfig->getCacheFactory(); |
161
|
2290 |
|
$this->cache = $cacheFactory->createCache($this); |
162
|
2290 |
|
} |
163
|
|
|
} |
164
|
2290 |
|
|
165
|
2290 |
|
/** |
166
|
2290 |
|
* {@inheritDoc} |
167
|
|
|
*/ |
168
|
|
|
public function getConnection() |
169
|
2290 |
|
{ |
170
|
277 |
|
return $this->conn; |
171
|
277 |
|
} |
172
|
277 |
|
|
173
|
|
|
/** |
174
|
2290 |
|
* Gets the metadata factory used to gather the metadata of classes. |
175
|
|
|
* |
176
|
|
|
* @return \Doctrine\ORM\Mapping\ClassMetadataFactory |
177
|
|
|
*/ |
178
|
|
|
public function getMetadataFactory() |
179
|
1837 |
|
{ |
180
|
|
|
return $this->metadataFactory; |
181
|
1837 |
|
} |
182
|
|
|
|
183
|
|
|
/** |
184
|
|
|
* {@inheritDoc} |
185
|
|
|
*/ |
186
|
|
|
public function getExpressionBuilder() |
187
|
|
|
{ |
188
|
|
|
if ($this->expressionBuilder === null) { |
189
|
2290 |
|
$this->expressionBuilder = new Query\Expr; |
190
|
|
|
} |
191
|
2290 |
|
|
192
|
|
|
return $this->expressionBuilder; |
193
|
|
|
} |
194
|
|
|
|
195
|
|
|
/** |
196
|
|
|
* @return IdentifierFlattener |
197
|
17 |
|
*/ |
198
|
|
|
public function getIdentifierFlattener() : IdentifierFlattener |
199
|
17 |
|
{ |
200
|
17 |
|
return $this->identifierFlattener; |
201
|
|
|
} |
202
|
|
|
|
203
|
17 |
|
/** |
204
|
|
|
* {@inheritDoc} |
205
|
|
|
*/ |
206
|
|
|
public function beginTransaction() |
207
|
|
|
{ |
208
|
|
|
$this->conn->beginTransaction(); |
209
|
2 |
|
} |
210
|
|
|
|
211
|
2 |
|
/** |
212
|
2 |
|
* {@inheritDoc} |
213
|
|
|
*/ |
214
|
|
|
public function getCache() |
215
|
|
|
{ |
216
|
|
|
return $this->cache; |
217
|
211 |
|
} |
218
|
|
|
|
219
|
211 |
|
/** |
220
|
|
|
* {@inheritDoc} |
221
|
|
|
*/ |
222
|
|
|
public function transactional(callable $func) |
223
|
|
|
{ |
224
|
|
|
$this->conn->beginTransaction(); |
225
|
4 |
|
|
226
|
|
|
try { |
227
|
4 |
|
$return = $func($this); |
228
|
1 |
|
|
229
|
|
|
$this->flush(); |
230
|
|
|
$this->conn->commit(); |
231
|
3 |
|
|
232
|
|
|
return $return; |
233
|
|
|
} catch (\Throwable $e) { |
234
|
3 |
|
$this->close(); |
235
|
|
|
$this->conn->rollBack(); |
236
|
3 |
|
|
237
|
3 |
|
throw $e; |
238
|
|
|
} |
239
|
3 |
|
} |
240
|
|
|
|
241
|
|
|
/** |
242
|
|
|
* {@inheritDoc} |
243
|
|
|
*/ |
244
|
|
|
public function commit() |
245
|
|
|
{ |
246
|
|
|
$this->conn->commit(); |
247
|
|
|
} |
248
|
|
|
|
249
|
|
|
/** |
250
|
|
|
* {@inheritDoc} |
251
|
1 |
|
*/ |
252
|
|
|
public function rollback() |
253
|
1 |
|
{ |
254
|
1 |
|
$this->conn->rollBack(); |
255
|
|
|
} |
256
|
|
|
|
257
|
|
|
/** |
258
|
|
|
* Returns the ORM metadata descriptor for a class. |
259
|
1 |
|
* |
260
|
|
|
* The class name must be the fully-qualified class name without a leading backslash |
261
|
1 |
|
* (as it is returned by get_class($obj)) or an aliased class name. |
262
|
1 |
|
* |
263
|
|
|
* Examples: |
264
|
|
|
* MyProject\Domain\User |
265
|
|
|
* sales:PriceRequest |
266
|
|
|
* |
267
|
|
|
* Internal note: Performance-sensitive method. |
268
|
|
|
* |
269
|
|
|
* @param string $className |
270
|
|
|
* |
271
|
|
|
* @return Mapping\ClassMetadata |
272
|
|
|
*/ |
273
|
|
|
public function getClassMetadata($className) : Mapping\ClassMetadata |
274
|
|
|
{ |
275
|
|
|
return $this->metadataFactory->getMetadataFor($className); |
276
|
|
|
} |
277
|
|
|
|
278
|
|
|
/** |
279
|
|
|
* {@inheritDoc} |
280
|
1898 |
|
*/ |
281
|
|
|
public function createQuery($dql = '') |
282
|
1898 |
|
{ |
283
|
|
|
$query = new Query($this); |
284
|
|
|
|
285
|
|
|
if ( ! empty($dql)) { |
286
|
|
|
$query->setDQL($dql); |
287
|
|
|
} |
288
|
924 |
|
|
289
|
|
|
return $query; |
290
|
924 |
|
} |
291
|
|
|
|
292
|
924 |
|
/** |
293
|
919 |
|
* {@inheritDoc} |
294
|
|
|
*/ |
295
|
|
|
public function createNamedQuery($name) |
296
|
924 |
|
{ |
297
|
|
|
return $this->createQuery($this->config->getNamedQuery($name)); |
298
|
|
|
} |
299
|
|
|
|
300
|
|
|
/** |
301
|
|
|
* {@inheritDoc} |
302
|
1 |
|
*/ |
303
|
|
|
public function createNativeQuery($sql, ResultSetMapping $rsm) |
304
|
1 |
|
{ |
305
|
|
|
$query = new NativeQuery($this); |
306
|
|
|
|
307
|
|
|
$query->setSQL($sql); |
308
|
|
|
$query->setResultSetMapping($rsm); |
309
|
|
|
|
310
|
21 |
|
return $query; |
311
|
|
|
} |
312
|
21 |
|
|
313
|
|
|
/** |
314
|
21 |
|
* {@inheritDoc} |
315
|
21 |
|
*/ |
316
|
|
|
public function createNamedNativeQuery($name) |
317
|
21 |
|
{ |
318
|
|
|
list($sql, $rsm) = $this->config->getNamedNativeQuery($name); |
319
|
|
|
|
320
|
|
|
return $this->createNativeQuery($sql, $rsm); |
321
|
|
|
} |
322
|
|
|
|
323
|
1 |
|
/** |
324
|
|
|
* {@inheritDoc} |
325
|
1 |
|
*/ |
326
|
|
|
public function createQueryBuilder() |
327
|
1 |
|
{ |
328
|
|
|
return new QueryBuilder($this); |
329
|
|
|
} |
330
|
|
|
|
331
|
|
|
/** |
332
|
|
|
* {@inheritDoc} |
333
|
112 |
|
* |
334
|
|
|
* @deprecated |
335
|
112 |
|
*/ |
336
|
|
|
public function merge($object) |
337
|
|
|
{ |
338
|
|
|
throw new \BadMethodCallException('@TODO method disabled - will be removed in 3.0 with a release of doctrine/common'); |
339
|
|
|
} |
340
|
|
|
|
341
|
|
|
/** |
342
|
|
|
* {@inheritDoc} |
343
|
|
|
* |
344
|
|
|
* @deprecated |
345
|
|
|
*/ |
346
|
|
|
public function detach($object) |
347
|
|
|
{ |
348
|
|
|
throw new \BadMethodCallException('@TODO method disabled - will be removed in 3.0 with a release of doctrine/common'); |
349
|
|
|
} |
350
|
|
|
|
351
|
|
|
/** |
352
|
|
|
* Flushes all changes to objects that have been queued up to now to the database. |
353
|
|
|
* This effectively synchronizes the in-memory state of managed objects with the |
354
|
1012 |
|
* database. |
355
|
|
|
* |
356
|
1012 |
|
* If an entity is explicitly passed to this method only this entity and |
357
|
|
|
* the cascade-persist semantics + scheduled inserts/removals are synchronized. |
358
|
1011 |
|
* |
359
|
1002 |
|
* @return void |
360
|
|
|
* |
361
|
|
|
* @throws \Doctrine\ORM\OptimisticLockException If a version check on an entity that |
362
|
|
|
* makes use of optimistic locking fails. |
363
|
|
|
* @throws ORMException |
364
|
|
|
*/ |
365
|
|
|
public function flush() |
366
|
|
|
{ |
367
|
|
|
$this->errorIfClosed(); |
368
|
|
|
|
369
|
|
|
$this->unitOfWork->commit(); |
370
|
|
|
} |
371
|
|
|
|
372
|
|
|
/** |
373
|
|
|
* Finds an Entity by its identifier. |
374
|
|
|
* |
375
|
|
|
* @param string $entityName The class name of the entity to find. |
376
|
|
|
* @param mixed $id The identity of the entity to find. |
377
|
|
|
* @param integer|null $lockMode One of the \Doctrine\DBAL\LockMode::* constants |
378
|
|
|
* or NULL if no specific lock mode should be used |
379
|
410 |
|
* during the search. |
380
|
|
|
* @param integer|null $lockVersion The version of the entity to find when using |
381
|
410 |
|
* optimistic locking. |
382
|
|
|
* |
383
|
410 |
|
* @return object|null The entity instance or NULL if the entity can not be found. |
384
|
365 |
|
* |
385
|
|
|
* @throws OptimisticLockException |
386
|
|
|
* @throws ORMInvalidArgumentException |
387
|
|
|
* @throws TransactionRequiredException |
388
|
365 |
|
* @throws ORMException |
389
|
|
|
*/ |
390
|
|
|
public function find($entityName, $id, $lockMode = null, $lockVersion = null) |
391
|
410 |
|
{ |
392
|
410 |
|
$class = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\')); |
393
|
6 |
|
$className = $class->getClassName(); |
394
|
|
|
|
395
|
6 |
View Code Duplication |
if (! is_array($id)) { |
|
|
|
|
396
|
410 |
|
if ($class->isIdentifierComposite()) { |
397
|
|
|
throw ORMInvalidArgumentException::invalidCompositeIdentifier(); |
398
|
|
|
} |
399
|
|
|
|
400
|
|
|
$id = [$class->identifier[0] => $id]; |
401
|
409 |
|
} |
402
|
|
|
|
403
|
409 |
View Code Duplication |
foreach ($id as $i => $value) { |
|
|
|
|
404
|
409 |
|
if (is_object($value) && $this->metadataFactory->hasMetadataFor(StaticClassNameConverter::getClass($value))) { |
405
|
1 |
|
$id[$i] = $this->unitOfWork->getSingleIdentifierValue($value); |
406
|
|
|
|
407
|
|
|
if ($id[$i] === null) { |
408
|
408 |
|
throw ORMInvalidArgumentException::invalidIdentifierBindingEntity(); |
409
|
408 |
|
} |
410
|
|
|
} |
411
|
|
|
} |
412
|
408 |
|
|
413
|
1 |
|
$sortedId = []; |
414
|
|
|
|
415
|
|
View Code Duplication |
foreach ($class->identifier as $identifier) { |
|
|
|
|
416
|
407 |
|
if ( ! isset($id[$identifier])) { |
417
|
|
|
throw ORMException::missingIdentifierField($className, $identifier); |
418
|
|
|
} |
419
|
407 |
|
|
420
|
41 |
|
$sortedId[$identifier] = $id[$identifier]; |
421
|
1 |
|
unset($id[$identifier]); |
422
|
|
|
} |
423
|
|
|
|
424
|
|
|
if ($id) { |
|
|
|
|
425
|
40 |
|
throw ORMException::unrecognizedIdentifierFields($className, array_keys($id)); |
426
|
1 |
|
} |
427
|
|
|
|
428
|
|
|
$unitOfWork = $this->getUnitOfWork(); |
429
|
40 |
|
|
430
|
40 |
|
// Check identity map first |
431
|
40 |
|
if (($entity = $unitOfWork->tryGetById($sortedId, $class->getRootClassName())) !== false) { |
432
|
|
|
if ( ! ($entity instanceof $className)) { |
433
|
|
|
return null; |
434
|
|
|
} |
435
|
|
|
|
436
|
|
|
switch (true) { |
437
|
40 |
|
case LockMode::OPTIMISTIC === $lockMode: |
438
|
|
|
$this->lock($entity, $lockMode, $lockVersion); |
439
|
|
|
break; |
440
|
392 |
|
|
441
|
|
|
case LockMode::NONE === $lockMode: |
442
|
|
|
case LockMode::PESSIMISTIC_READ === $lockMode: |
443
|
392 |
|
case LockMode::PESSIMISTIC_WRITE === $lockMode: |
444
|
1 |
|
$persister = $unitOfWork->getEntityPersister($className); |
445
|
1 |
|
$persister->refresh($sortedId, $entity, $lockMode); |
446
|
|
|
break; |
447
|
|
|
} |
448
|
|
|
|
449
|
|
|
return $entity; // Hit! |
450
|
|
|
} |
451
|
|
|
|
452
|
|
|
$persister = $unitOfWork->getEntityPersister($className); |
453
|
|
|
|
454
|
391 |
|
switch (true) { |
455
|
390 |
|
case LockMode::OPTIMISTIC === $lockMode: |
456
|
2 |
|
if ( ! $class->isVersioned()) { |
457
|
2 |
|
throw OptimisticLockException::notVersioned($className); |
458
|
|
|
} |
459
|
|
|
|
460
|
|
|
$entity = $persister->load($sortedId); |
461
|
|
|
|
462
|
|
|
$unitOfWork->lock($entity, $lockMode, $lockVersion); |
|
|
|
|
463
|
389 |
|
|
464
|
|
|
return $entity; |
465
|
|
|
|
466
|
|
|
case LockMode::PESSIMISTIC_READ === $lockMode: |
467
|
|
|
case LockMode::PESSIMISTIC_WRITE === $lockMode: |
468
|
|
|
if ( ! $this->getConnection()->isTransactionActive()) { |
469
|
|
|
throw TransactionRequiredException::transactionRequired(); |
470
|
91 |
|
} |
471
|
|
|
|
472
|
91 |
|
return $persister->load($sortedId, null, null, [], $lockMode); |
473
|
|
|
|
474
|
91 |
|
default: |
475
|
49 |
|
return $persister->loadById($sortedId); |
476
|
|
|
} |
477
|
|
|
} |
478
|
91 |
|
|
479
|
|
|
/** |
480
|
91 |
|
* {@inheritDoc} |
481
|
91 |
|
*/ |
482
|
|
|
public function getReference($entityName, $id) |
483
|
|
|
{ |
484
|
|
|
$class = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\')); |
485
|
91 |
|
$className = $class->getClassName(); |
486
|
91 |
|
|
487
|
|
View Code Duplication |
if ( ! is_array($id)) { |
|
|
|
|
488
|
|
|
if ($class->isIdentifierComposite()) { |
489
|
91 |
|
throw ORMInvalidArgumentException::invalidCompositeIdentifier(); |
490
|
1 |
|
} |
491
|
|
|
|
492
|
|
|
$id = [$class->identifier[0] => $id]; |
493
|
|
|
} |
494
|
90 |
|
|
495
|
27 |
|
$scalarId = []; |
496
|
|
|
|
497
|
|
|
foreach ($id as $i => $value) { |
498
|
86 |
|
$scalarId[$i] = $value; |
499
|
2 |
|
|
500
|
|
|
if (is_object($value) && $this->metadataFactory->hasMetadataFor(StaticClassNameConverter::getClass($value))) { |
501
|
|
|
$scalarId[$i] = $this->unitOfWork->getSingleIdentifierValue($value); |
502
|
86 |
|
|
503
|
|
|
if ($scalarId[$i] === null) { |
504
|
86 |
|
throw ORMInvalidArgumentException::invalidIdentifierBindingEntity(); |
505
|
|
|
} |
506
|
86 |
|
} |
507
|
|
|
} |
508
|
|
|
|
509
|
|
|
$sortedId = []; |
510
|
|
|
|
511
|
|
View Code Duplication |
foreach ($class->identifier as $identifier) { |
|
|
|
|
512
|
4 |
|
if ( ! isset($scalarId[$identifier])) { |
513
|
|
|
throw ORMException::missingIdentifierField($className, $identifier); |
514
|
4 |
|
} |
515
|
|
|
|
516
|
|
|
$sortedId[$identifier] = $scalarId[$identifier]; |
517
|
4 |
|
unset($scalarId[$identifier]); |
518
|
1 |
|
} |
519
|
|
|
|
520
|
|
|
if ($scalarId) { |
|
|
|
|
521
|
3 |
|
throw ORMException::unrecognizedIdentifierFields($className, array_keys($scalarId)); |
522
|
3 |
|
} |
523
|
|
|
|
524
|
|
|
// Check identity map first, if its already in there just return it. |
525
|
3 |
View Code Duplication |
if (($entity = $this->unitOfWork->tryGetById($sortedId, $class->getRootClassName())) !== false) { |
|
|
|
|
526
|
|
|
return ($entity instanceof $className) ? $entity : null; |
527
|
3 |
|
} |
528
|
|
|
|
529
|
3 |
|
if ($class->getSubClasses()) { |
530
|
3 |
|
return $this->find($entityName, $sortedId); |
531
|
|
|
} |
532
|
3 |
|
|
533
|
|
|
$entity = $this->proxyFactory->getProxy($class, $id); |
534
|
|
|
|
535
|
|
|
$this->unitOfWork->registerManaged($entity, $sortedId, []); |
536
|
|
|
|
537
|
|
|
if ($entity instanceof EntityManagerAware) { |
538
|
|
|
$entity->injectEntityManager($this, $class); |
539
|
|
|
} |
540
|
|
|
|
541
|
|
|
return $entity; |
542
|
|
|
} |
543
|
1217 |
|
|
544
|
|
|
/** |
545
|
1217 |
|
* {@inheritDoc} |
546
|
1217 |
|
*/ |
547
|
|
|
public function getPartialReference($entityName, $id) |
548
|
|
|
{ |
549
|
|
|
$class = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\')); |
550
|
|
|
$className = $class->getClassName(); |
551
|
19 |
|
|
552
|
|
View Code Duplication |
if ( ! is_array($id)) { |
|
|
|
|
553
|
19 |
|
if ($class->isIdentifierComposite()) { |
554
|
|
|
throw ORMInvalidArgumentException::invalidCompositeIdentifier(); |
555
|
19 |
|
} |
556
|
19 |
|
|
557
|
|
|
$id = [$class->identifier[0] => $id]; |
558
|
|
|
} |
559
|
|
|
|
560
|
|
View Code Duplication |
foreach ($id as $i => $value) { |
|
|
|
|
561
|
|
|
if (is_object($value) && $this->metadataFactory->hasMetadataFor(StaticClassNameConverter::getClass($value))) { |
562
|
|
|
$id[$i] = $this->unitOfWork->getSingleIdentifierValue($value); |
563
|
|
|
|
564
|
|
|
if ($id[$i] === null) { |
565
|
|
|
throw ORMInvalidArgumentException::invalidIdentifierBindingEntity(); |
566
|
|
|
} |
567
|
|
|
} |
568
|
|
|
} |
569
|
|
|
|
570
|
|
|
$sortedId = []; |
571
|
|
|
|
572
|
|
View Code Duplication |
foreach ($class->identifier as $identifier) { |
|
|
|
|
573
|
|
|
if ( ! isset($id[$identifier])) { |
574
|
1006 |
|
throw ORMException::missingIdentifierField($className, $identifier); |
575
|
|
|
} |
576
|
1006 |
|
|
577
|
1 |
|
$sortedId[$identifier] = $id[$identifier]; |
578
|
|
|
unset($id[$identifier]); |
579
|
|
|
} |
580
|
1005 |
|
|
581
|
|
|
if ($id) { |
|
|
|
|
582
|
1004 |
|
throw ORMException::unrecognizedIdentifierFields($className, array_keys($id)); |
583
|
1003 |
|
} |
584
|
|
|
|
585
|
|
|
// Check identity map first, if its already in there just return it. |
586
|
|
View Code Duplication |
if (($entity = $this->unitOfWork->tryGetById($sortedId, $class->getRootClassName())) !== false) { |
|
|
|
|
587
|
|
|
return ($entity instanceof $className) ? $entity : null; |
588
|
|
|
} |
589
|
|
|
|
590
|
|
|
$persister = $this->unitOfWork->getEntityPersister($class->getClassName()); |
591
|
|
|
$entity = $this->unitOfWork->newInstance($class); |
592
|
|
|
|
593
|
|
|
$persister->setIdentifier($entity, $sortedId); |
594
|
|
|
|
595
|
|
|
$this->unitOfWork->registerManaged($entity, $sortedId, []); |
596
|
|
|
$this->unitOfWork->markReadOnly($entity); |
597
|
|
|
|
598
|
50 |
|
return $entity; |
599
|
|
|
} |
600
|
50 |
|
|
601
|
1 |
|
/** |
602
|
|
|
* Clears the EntityManager. All entities that are currently managed |
603
|
|
|
* by this EntityManager become detached. |
604
|
49 |
|
* |
605
|
|
|
* @param null $entityName Unused. @todo Remove from ObjectManager. |
606
|
48 |
|
* |
607
|
48 |
|
* @return void |
608
|
|
|
*/ |
609
|
|
|
public function clear($entityName = null) |
610
|
|
|
{ |
611
|
|
|
$this->unitOfWork->clear(); |
612
|
|
|
} |
613
|
|
|
|
614
|
|
|
/** |
615
|
|
|
* {@inheritDoc} |
616
|
|
|
*/ |
617
|
|
|
public function close() |
618
|
|
|
{ |
619
|
|
|
$this->clear(); |
620
|
18 |
|
|
621
|
|
|
$this->closed = true; |
622
|
18 |
|
} |
623
|
1 |
|
|
624
|
|
|
/** |
625
|
|
|
* Tells the EntityManager to make an instance managed and persistent. |
626
|
17 |
|
* |
627
|
|
|
* The entity will be entered into the database at or before transaction |
628
|
16 |
|
* commit or as a result of the flush operation. |
629
|
16 |
|
* |
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
|
|
|
* @return void |
636
|
|
|
* |
637
|
|
|
* @throws ORMInvalidArgumentException |
638
|
|
|
* @throws ORMException |
639
|
|
|
*/ |
640
|
|
View Code Duplication |
public function persist($entity) |
641
|
|
|
{ |
642
|
|
|
if ( ! is_object($entity)) { |
643
|
|
|
throw ORMInvalidArgumentException::invalidObject('EntityManager#persist()', $entity); |
644
|
13 |
|
} |
645
|
|
|
|
646
|
13 |
|
$this->errorIfClosed(); |
647
|
1 |
|
|
648
|
|
|
$this->unitOfWork->persist($entity); |
649
|
|
|
} |
650
|
12 |
|
|
651
|
12 |
|
/** |
652
|
|
|
* Removes an entity instance. |
653
|
|
|
* |
654
|
|
|
* A removed entity will be removed from the database at or before transaction commit |
655
|
|
|
* or as a result of the flush operation. |
656
|
|
|
* |
657
|
|
|
* @param object $entity The entity instance to remove. |
658
|
|
|
* |
659
|
|
|
* @return void |
660
|
|
|
* |
661
|
|
|
* @throws ORMInvalidArgumentException |
662
|
|
|
* @throws ORMException |
663
|
|
|
*/ |
664
|
|
View Code Duplication |
public function remove($entity) |
665
|
42 |
|
{ |
666
|
|
|
if ( ! is_object($entity)) { |
667
|
42 |
|
throw ORMInvalidArgumentException::invalidObject('EntityManager#remove()', $entity); |
668
|
1 |
|
} |
669
|
|
|
|
670
|
|
|
$this->errorIfClosed(); |
671
|
41 |
|
|
672
|
|
|
$this->unitOfWork->remove($entity); |
673
|
40 |
|
} |
674
|
|
|
|
675
|
|
|
/** |
676
|
|
|
* Refreshes the persistent state of an entity from the database, |
677
|
|
|
* overriding any local changes that have not yet been persisted. |
678
|
|
|
* |
679
|
|
|
* @param object $entity The entity to refresh. |
680
|
|
|
* |
681
|
|
|
* @return void |
682
|
|
|
* |
683
|
|
|
* @throws ORMInvalidArgumentException |
684
|
|
|
* @throws ORMException |
685
|
|
|
*/ |
686
|
|
View Code Duplication |
public function refresh($entity) |
687
|
|
|
{ |
688
|
|
|
if ( ! is_object($entity)) { |
689
|
|
|
throw ORMInvalidArgumentException::invalidObject('EntityManager#refresh()', $entity); |
690
|
10 |
|
} |
691
|
|
|
|
692
|
10 |
|
$this->errorIfClosed(); |
693
|
3 |
|
|
694
|
|
|
$this->unitOfWork->refresh($entity); |
695
|
|
|
} |
696
|
|
|
|
697
|
|
|
/** |
698
|
|
|
* {@inheritDoc} |
699
|
|
|
*/ |
700
|
|
|
public function lock($entity, $lockMode, $lockVersion = null) |
701
|
|
|
{ |
702
|
142 |
|
$this->unitOfWork->lock($entity, $lockMode, $lockVersion); |
703
|
|
|
} |
704
|
142 |
|
|
705
|
|
|
/** |
706
|
|
|
* Gets the repository for an entity class. |
707
|
|
|
* |
708
|
|
|
* @param string $entityName The name of the entity. |
709
|
|
|
* |
710
|
|
|
* @return \Doctrine\ORM\EntityRepository The repository class. |
711
|
|
|
*/ |
712
|
|
|
public function getRepository($entityName) |
713
|
|
|
{ |
714
|
22 |
|
return $this->repositoryFactory->getRepository($this, $entityName); |
715
|
|
|
} |
716
|
22 |
|
|
717
|
20 |
|
/** |
718
|
22 |
|
* Determines whether an entity instance is managed in this EntityManager. |
719
|
|
|
* |
720
|
|
|
* @param object $entity |
721
|
|
|
* |
722
|
|
|
* @return boolean TRUE if this EntityManager currently manages the given entity, FALSE otherwise. |
723
|
|
|
*/ |
724
|
2290 |
|
public function contains($entity) |
725
|
|
|
{ |
726
|
2290 |
|
return $this->unitOfWork->isScheduledForInsert($entity) |
727
|
|
|
|| ($this->unitOfWork->isInIdentityMap($entity) && !$this->unitOfWork->isScheduledForDelete($entity)); |
728
|
|
|
} |
729
|
|
|
|
730
|
|
|
/** |
731
|
|
|
* {@inheritDoc} |
732
|
2290 |
|
*/ |
733
|
|
|
public function getEventManager() |
734
|
2290 |
|
{ |
735
|
|
|
return $this->eventManager; |
736
|
|
|
} |
737
|
|
|
|
738
|
|
|
/** |
739
|
|
|
* {@inheritDoc} |
740
|
|
|
*/ |
741
|
|
|
public function getConfiguration() |
742
|
|
|
{ |
743
|
|
|
return $this->config; |
744
|
1025 |
|
} |
745
|
|
|
|
746
|
1025 |
|
/** |
747
|
5 |
|
* Throws an exception if the EntityManager is closed or currently not active. |
748
|
|
|
* |
749
|
1020 |
|
* @return void |
750
|
|
|
* |
751
|
|
|
* @throws ORMException If the EntityManager is closed. |
752
|
|
|
*/ |
753
|
|
|
private function errorIfClosed() |
754
|
1 |
|
{ |
755
|
|
|
if ($this->closed) { |
756
|
1 |
|
throw ORMException::entityManagerClosed(); |
757
|
|
|
} |
758
|
|
|
} |
759
|
|
|
|
760
|
|
|
/** |
761
|
|
|
* {@inheritDoc} |
762
|
2290 |
|
*/ |
763
|
|
|
public function isOpen() |
764
|
2290 |
|
{ |
765
|
|
|
return (!$this->closed); |
766
|
|
|
} |
767
|
|
|
|
768
|
|
|
/** |
769
|
|
|
* {@inheritDoc} |
770
|
|
|
*/ |
771
|
|
|
public function getUnitOfWork() |
772
|
|
|
{ |
773
|
|
|
return $this->unitOfWork; |
774
|
|
|
} |
775
|
|
|
|
776
|
|
|
/** |
777
|
|
|
* {@inheritDoc} |
778
|
879 |
|
*/ |
779
|
|
|
public function getHydrator($hydrationMode) |
780
|
|
|
{ |
781
|
879 |
|
return $this->newHydrator($hydrationMode); |
782
|
611 |
|
} |
783
|
|
|
|
784
|
535 |
|
/** |
785
|
34 |
|
* {@inheritDoc} |
786
|
|
|
*/ |
787
|
506 |
|
public function newHydrator($hydrationMode) |
788
|
85 |
|
{ |
789
|
|
|
switch ($hydrationMode) { |
790
|
423 |
|
case Query::HYDRATE_OBJECT: |
791
|
12 |
|
return new Internal\Hydration\ObjectHydrator($this); |
792
|
|
|
|
793
|
415 |
|
case Query::HYDRATE_ARRAY: |
794
|
414 |
|
return new Internal\Hydration\ArrayHydrator($this); |
795
|
|
|
|
796
|
|
|
case Query::HYDRATE_SCALAR: |
797
|
1 |
|
return new Internal\Hydration\ScalarHydrator($this); |
798
|
1 |
|
|
799
|
|
|
case Query::HYDRATE_SINGLE_SCALAR: |
800
|
|
|
return new Internal\Hydration\SingleScalarHydrator($this); |
801
|
|
|
|
802
|
|
|
case Query::HYDRATE_SIMPLEOBJECT: |
803
|
|
|
return new Internal\Hydration\SimpleObjectHydrator($this); |
804
|
|
|
|
805
|
|
|
default: |
806
|
|
|
if (($class = $this->config->getCustomHydrationMode($hydrationMode)) !== null) { |
807
|
|
|
return new $class($this); |
808
|
166 |
|
} |
809
|
|
|
} |
810
|
166 |
|
|
811
|
|
|
throw ORMException::invalidHydrationMode($hydrationMode); |
812
|
|
|
} |
813
|
|
|
|
814
|
|
|
/** |
815
|
|
|
* {@inheritDoc} |
816
|
|
|
*/ |
817
|
|
|
public function getProxyFactory() |
818
|
|
|
{ |
819
|
|
|
return $this->proxyFactory; |
820
|
|
|
} |
821
|
|
|
|
822
|
|
|
/** |
823
|
|
|
* {@inheritDoc} |
824
|
|
|
*/ |
825
|
|
|
public function initializeObject($obj) |
826
|
|
|
{ |
827
|
|
|
$this->unitOfWork->initializeObject($obj); |
828
|
|
|
} |
829
|
|
|
|
830
|
|
|
/** |
831
|
|
|
* Factory method to create EntityManager instances. |
832
|
|
|
* |
833
|
1229 |
|
* @param array|Connection $connection An array with the connection parameters or an existing Connection instance. |
834
|
|
|
* @param Configuration $config The Configuration instance to use. |
835
|
1229 |
|
* @param EventManager $eventManager The EventManager instance to use. |
836
|
|
|
* |
837
|
|
|
* @return EntityManager The created EntityManager. |
838
|
|
|
* |
839
|
1229 |
|
* @throws \InvalidArgumentException |
840
|
|
|
* @throws ORMException |
841
|
1229 |
|
*/ |
842
|
|
|
public static function create($connection, Configuration $config, EventManager $eventManager = null) |
843
|
|
|
{ |
844
|
|
|
if ( ! $config->getMetadataDriverImpl()) { |
845
|
|
|
throw ORMException::missingMappingDriverImpl(); |
846
|
|
|
} |
847
|
|
|
|
848
|
|
|
$connection = static::createConnection($connection, $config, $eventManager); |
849
|
|
|
|
850
|
|
|
return new EntityManager($connection, $config, $connection->getEventManager()); |
851
|
|
|
} |
852
|
|
|
|
853
|
|
|
/** |
854
|
|
|
* Factory method to create Connection instances. |
855
|
|
|
* |
856
|
1229 |
|
* @param array|Connection $connection An array with the connection parameters or an existing Connection instance. |
857
|
|
|
* @param Configuration $config The Configuration instance to use. |
858
|
1229 |
|
* @param EventManager $eventManager The EventManager instance to use. |
859
|
|
|
* |
860
|
|
|
* @return Connection |
861
|
|
|
* |
862
|
1229 |
|
* @throws \InvalidArgumentException |
863
|
|
|
* @throws ORMException |
864
|
|
|
*/ |
865
|
|
|
protected static function createConnection($connection, Configuration $config, EventManager $eventManager = null) |
866
|
1229 |
|
{ |
867
|
|
|
if (is_array($connection)) { |
868
|
|
|
return DriverManager::getConnection($connection, $config, $eventManager ?: new EventManager()); |
869
|
|
|
} |
870
|
1229 |
|
|
871
|
|
|
if ( ! $connection instanceof Connection) { |
872
|
|
|
throw new \InvalidArgumentException( |
873
|
|
|
sprintf( |
874
|
|
|
'Invalid $connection argument of type %s given%s.', |
875
|
|
|
is_object($connection) ? get_class($connection) : gettype($connection), |
876
|
605 |
|
is_object($connection) ? '' : ': "' . $connection . '"' |
877
|
|
|
) |
878
|
605 |
|
); |
879
|
605 |
|
} |
880
|
|
|
|
881
|
|
|
if ($eventManager !== null && $connection->getEventManager() !== $eventManager) { |
882
|
605 |
|
throw ORMException::mismatchedEventManager(); |
883
|
|
|
} |
884
|
|
|
|
885
|
|
|
return $connection; |
886
|
|
|
} |
887
|
|
|
|
888
|
36 |
|
/** |
889
|
|
|
* {@inheritDoc} |
890
|
36 |
|
*/ |
891
|
|
|
public function getFilters() |
892
|
|
|
{ |
893
|
|
|
if (null === $this->filterCollection) { |
894
|
|
|
$this->filterCollection = new FilterCollection($this); |
895
|
|
|
} |
896
|
734 |
|
|
897
|
|
|
return $this->filterCollection; |
898
|
734 |
|
} |
899
|
|
|
|
900
|
|
|
/** |
901
|
|
|
* {@inheritDoc} |
902
|
|
|
*/ |
903
|
|
|
public function isFiltersStateClean() |
904
|
|
|
{ |
905
|
|
|
return null === $this->filterCollection || $this->filterCollection->isClean(); |
906
|
|
|
} |
907
|
|
|
|
908
|
|
|
/** |
909
|
|
|
* {@inheritDoc} |
910
|
|
|
*/ |
911
|
|
|
public function hasFilters() |
912
|
|
|
{ |
913
|
|
|
return null !== $this->filterCollection; |
914
|
|
|
} |
915
|
|
|
} |
916
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.