1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace Doctrine\ORM\Mapping; |
6
|
|
|
|
7
|
|
|
use Doctrine\DBAL\Platforms; |
8
|
|
|
use Doctrine\ORM\EntityManagerInterface; |
9
|
|
|
use Doctrine\ORM\Event\LoadClassMetadataEventArgs; |
10
|
|
|
use Doctrine\ORM\Event\OnClassMetadataNotFoundEventArgs; |
11
|
|
|
use Doctrine\ORM\Events; |
12
|
|
|
use Doctrine\ORM\ORMException; |
13
|
|
|
use Doctrine\ORM\Sequencing; |
14
|
|
|
use Doctrine\ORM\Sequencing\Planning\ColumnValueGeneratorExecutor; |
15
|
|
|
use Doctrine\ORM\Sequencing\Planning\CompositeValueGenerationPlan; |
16
|
|
|
use Doctrine\ORM\Sequencing\Planning\NoopValueGenerationPlan; |
17
|
|
|
use Doctrine\ORM\Sequencing\Planning\SingleValueGenerationPlan; |
18
|
|
|
use ReflectionException; |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* The ClassMetadataFactory is used to create ClassMetadata objects that contain all the |
22
|
|
|
* metadata mapping information of a class which describes how a class should be mapped |
23
|
|
|
* to a relational database. |
24
|
|
|
*/ |
25
|
|
|
class ClassMetadataFactory extends AbstractClassMetadataFactory |
26
|
|
|
{ |
27
|
|
|
/** |
28
|
|
|
* @var EntityManagerInterface|null |
29
|
|
|
*/ |
30
|
|
|
private $em; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* @var \Doctrine\DBAL\Platforms\AbstractPlatform |
34
|
|
|
*/ |
35
|
|
|
private $targetPlatform; |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* @var Driver\MappingDriver |
39
|
|
|
*/ |
40
|
|
|
private $driver; |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* @var \Doctrine\Common\EventManager |
44
|
|
|
*/ |
45
|
|
|
private $evm; |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* {@inheritdoc} |
49
|
|
|
*/ |
50
|
374 |
|
protected function loadMetadata(string $name, ClassMetadataBuildingContext $metadataBuildingContext) : array |
51
|
|
|
{ |
52
|
374 |
|
$loaded = parent::loadMetadata($name, $metadataBuildingContext); |
53
|
|
|
|
54
|
355 |
|
array_map([$this, 'resolveDiscriminatorValue'], $loaded); |
55
|
|
|
|
56
|
355 |
|
return $loaded; |
57
|
|
|
} |
58
|
|
|
|
59
|
2252 |
|
public function setEntityManager(EntityManagerInterface $em) |
60
|
|
|
{ |
61
|
2252 |
|
$this->em = $em; |
62
|
2252 |
|
} |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* {@inheritdoc} |
66
|
|
|
* |
67
|
|
|
* @throws ORMException |
68
|
|
|
*/ |
69
|
440 |
|
protected function initialize() : void |
70
|
|
|
{ |
71
|
440 |
|
$this->driver = $this->em->getConfiguration()->getMetadataDriverImpl(); |
72
|
440 |
|
$this->evm = $this->em->getEventManager(); |
73
|
440 |
|
$this->initialized = true; |
74
|
440 |
|
} |
75
|
|
|
|
76
|
|
|
/** |
77
|
|
|
* {@inheritdoc} |
78
|
|
|
*/ |
79
|
12 |
|
protected function onNotFoundMetadata( |
80
|
|
|
string $className, |
81
|
|
|
ClassMetadataBuildingContext $metadataBuildingContext |
82
|
|
|
) : ?ClassMetadata { |
83
|
12 |
|
if (! $this->evm->hasListeners(Events::onClassMetadataNotFound)) { |
84
|
10 |
|
return null; |
85
|
|
|
} |
86
|
|
|
|
87
|
2 |
|
$eventArgs = new OnClassMetadataNotFoundEventArgs($className, $metadataBuildingContext, $this->em); |
88
|
|
|
|
89
|
2 |
|
$this->evm->dispatchEvent(Events::onClassMetadataNotFound, $eventArgs); |
90
|
|
|
|
91
|
2 |
|
return $eventArgs->getFoundMetadata(); |
92
|
|
|
} |
93
|
|
|
|
94
|
|
|
/** |
95
|
|
|
* {@inheritdoc} |
96
|
|
|
* |
97
|
|
|
* @throws MappingException |
98
|
|
|
* @throws ORMException |
99
|
|
|
*/ |
100
|
362 |
|
protected function doLoadMetadata( |
101
|
|
|
string $className, |
102
|
|
|
?ClassMetadata $parent, |
103
|
|
|
ClassMetadataBuildingContext $metadataBuildingContext |
104
|
|
|
) : ClassMetadata { |
105
|
362 |
|
$classMetadata = new ClassMetadata($className, $metadataBuildingContext); |
106
|
|
|
|
107
|
362 |
|
if ($parent) { |
108
|
98 |
|
$classMetadata->setParent($parent); |
109
|
|
|
|
110
|
98 |
|
$this->addInheritedProperties($classMetadata, $parent); |
111
|
|
|
|
112
|
97 |
|
$classMetadata->setInheritanceType($parent->inheritanceType); |
113
|
97 |
|
$classMetadata->setIdentifier($parent->identifier); |
114
|
|
|
|
115
|
97 |
|
if ($parent->discriminatorColumn) { |
116
|
71 |
|
$classMetadata->setDiscriminatorColumn($parent->discriminatorColumn); |
117
|
71 |
|
$classMetadata->setDiscriminatorMap($parent->discriminatorMap); |
118
|
|
|
} |
119
|
|
|
|
120
|
97 |
|
$classMetadata->setLifecycleCallbacks($parent->lifecycleCallbacks); |
121
|
97 |
|
$classMetadata->setChangeTrackingPolicy($parent->changeTrackingPolicy); |
122
|
|
|
|
123
|
97 |
|
if ($parent->isMappedSuperclass) { |
124
|
28 |
|
$classMetadata->setCustomRepositoryClassName($parent->getCustomRepositoryClassName()); |
125
|
|
|
} |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
// Invoke driver |
129
|
|
|
try { |
130
|
362 |
|
$this->driver->loadMetadataForClass($classMetadata->getClassName(), $classMetadata, $metadataBuildingContext); |
131
|
2 |
|
} catch (ReflectionException $e) { |
132
|
|
|
throw MappingException::reflectionFailure($classMetadata->getClassName(), $e); |
133
|
|
|
} |
134
|
|
|
|
135
|
360 |
|
$this->completeIdentifierGeneratorMappings($classMetadata); |
136
|
|
|
|
137
|
360 |
|
if ($parent) { |
138
|
97 |
|
$parentCache = $parent->getCache(); |
139
|
|
|
|
140
|
97 |
|
if ($parentCache) { |
141
|
3 |
|
$classMetadata->setCache(clone $parentCache); |
142
|
|
|
} |
143
|
|
|
|
144
|
97 |
|
if (! empty($parent->namedNativeQueries)) { |
145
|
7 |
|
$this->addInheritedNamedNativeQueries($classMetadata, $parent); |
146
|
|
|
} |
147
|
|
|
|
148
|
97 |
|
if (! empty($parent->sqlResultSetMappings)) { |
149
|
7 |
|
$this->addInheritedSqlResultSetMappings($classMetadata, $parent); |
150
|
|
|
} |
151
|
|
|
|
152
|
97 |
|
if (! empty($parent->entityListeners) && empty($classMetadata->entityListeners)) { |
153
|
7 |
|
$classMetadata->entityListeners = $parent->entityListeners; |
154
|
|
|
} |
155
|
|
|
} |
156
|
|
|
|
157
|
360 |
|
if (! $classMetadata->discriminatorMap && $classMetadata->inheritanceType !== InheritanceType::NONE && $classMetadata->isRootEntity()) { |
158
|
1 |
|
$this->addDefaultDiscriminatorMap($classMetadata); |
159
|
|
|
} |
160
|
|
|
|
161
|
360 |
|
$this->completeRuntimeMetadata($classMetadata, $parent); |
162
|
|
|
|
163
|
360 |
|
if ($this->evm->hasListeners(Events::loadClassMetadata)) { |
164
|
6 |
|
$eventArgs = new LoadClassMetadataEventArgs($classMetadata, $this->em); |
165
|
|
|
|
166
|
6 |
|
$this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs); |
167
|
|
|
} |
168
|
|
|
|
169
|
359 |
|
$this->buildValueGenerationPlan($classMetadata); |
170
|
359 |
|
$this->validateRuntimeMetadata($classMetadata, $parent); |
171
|
|
|
|
172
|
357 |
|
return $classMetadata; |
173
|
|
|
} |
174
|
|
|
|
175
|
360 |
|
protected function completeRuntimeMetadata(ClassMetadata $class, ?ClassMetadata $parent = null) : void |
176
|
|
|
{ |
177
|
360 |
|
if (! $parent || ! $parent->isMappedSuperclass) { |
178
|
360 |
|
return; |
179
|
|
|
} |
180
|
|
|
|
181
|
28 |
|
if ($class->isMappedSuperclass) { |
182
|
1 |
|
return; |
183
|
|
|
} |
184
|
|
|
|
185
|
28 |
|
$tableName = $class->getTableName(); |
186
|
|
|
|
187
|
|
|
// Resolve column table names |
188
|
28 |
|
foreach ($class->getDeclaredPropertiesIterator() as $property) { |
189
|
28 |
|
if ($property instanceof FieldMetadata) { |
190
|
28 |
|
$property->setTableName($property->getTableName() ?? $tableName); |
191
|
|
|
|
192
|
28 |
|
continue; |
193
|
|
|
} |
194
|
|
|
|
195
|
12 |
|
if (! ($property instanceof ToOneAssociationMetadata)) { |
196
|
12 |
|
continue; |
197
|
|
|
} |
198
|
|
|
|
199
|
|
|
// Resolve association join column table names |
200
|
9 |
|
foreach ($property->getJoinColumns() as $joinColumn) { |
201
|
|
|
/** @var JoinColumnMetadata $joinColumn */ |
202
|
9 |
|
$joinColumn->setTableName($joinColumn->getTableName() ?? $tableName); |
203
|
|
|
} |
204
|
|
|
} |
205
|
28 |
|
} |
206
|
|
|
|
207
|
|
|
/** |
208
|
|
|
* Validate runtime metadata is correctly defined. |
209
|
|
|
* |
210
|
|
|
* @throws MappingException |
211
|
|
|
*/ |
212
|
359 |
|
protected function validateRuntimeMetadata(ClassMetadata $class, ?ClassMetadata $parent = null) : void |
213
|
|
|
{ |
214
|
359 |
|
if (! $class->getReflectionClass()) { |
215
|
|
|
// only validate if there is a reflection class instance |
216
|
|
|
return; |
217
|
|
|
} |
218
|
|
|
|
219
|
359 |
|
$class->validateIdentifier(); |
220
|
357 |
|
$class->validateAssociations(); |
221
|
357 |
|
$class->validateLifecycleCallbacks($this->getReflectionService()); |
222
|
|
|
|
223
|
|
|
// verify inheritance |
224
|
357 |
|
if (! $class->isMappedSuperclass && $class->inheritanceType !== InheritanceType::NONE) { |
225
|
74 |
|
if (! $parent) { |
226
|
72 |
|
if (! $class->discriminatorMap) { |
227
|
|
|
throw MappingException::missingDiscriminatorMap($class->getClassName()); |
228
|
|
|
} |
229
|
|
|
|
230
|
72 |
|
if (! $class->discriminatorColumn) { |
231
|
74 |
|
throw MappingException::missingDiscriminatorColumn($class->getClassName()); |
232
|
|
|
} |
233
|
|
|
} |
234
|
322 |
|
} elseif (($class->discriminatorMap || $class->discriminatorColumn) && $class->isMappedSuperclass && $class->isRootEntity()) { |
235
|
|
|
// second condition is necessary for mapped superclasses in the middle of an inheritance hierarchy |
236
|
|
|
throw MappingException::noInheritanceOnMappedSuperClass($class->getClassName()); |
237
|
|
|
} |
238
|
357 |
|
} |
239
|
|
|
|
240
|
|
|
/** |
241
|
|
|
* {@inheritdoc} |
242
|
|
|
*/ |
243
|
1951 |
|
protected function newClassMetadataBuildingContext() : ClassMetadataBuildingContext |
244
|
|
|
{ |
245
|
1951 |
|
return new ClassMetadataBuildingContext( |
246
|
1951 |
|
$this, |
247
|
1951 |
|
$this->getReflectionService(), |
248
|
1951 |
|
$this->em->getConfiguration()->getNamingStrategy() |
249
|
|
|
); |
250
|
|
|
} |
251
|
|
|
|
252
|
|
|
/** |
253
|
|
|
* Populates the discriminator value of the given metadata (if not set) by iterating over discriminator |
254
|
|
|
* map classes and looking for a fitting one. |
255
|
|
|
* |
256
|
|
|
* @throws \InvalidArgumentException |
257
|
|
|
* @throws \ReflectionException |
258
|
|
|
* @throws MappingException |
259
|
|
|
*/ |
260
|
355 |
|
private function resolveDiscriminatorValue(ClassMetadata $metadata) : void |
261
|
|
|
{ |
262
|
355 |
|
if ($metadata->discriminatorValue || ! $metadata->discriminatorMap || $metadata->isMappedSuperclass || |
263
|
355 |
|
! $metadata->getReflectionClass() || $metadata->getReflectionClass()->isAbstract()) { |
264
|
355 |
|
return; |
265
|
|
|
} |
266
|
|
|
|
267
|
|
|
// minor optimization: avoid loading related metadata when not needed |
268
|
4 |
|
foreach ($metadata->discriminatorMap as $discriminatorValue => $discriminatorClass) { |
269
|
4 |
|
if ($discriminatorClass === $metadata->getClassName()) { |
270
|
3 |
|
$metadata->discriminatorValue = $discriminatorValue; |
271
|
|
|
|
272
|
4 |
|
return; |
273
|
|
|
} |
274
|
|
|
} |
275
|
|
|
|
276
|
|
|
// iterate over discriminator mappings and resolve actual referenced classes according to existing metadata |
277
|
1 |
|
foreach ($metadata->discriminatorMap as $discriminatorValue => $discriminatorClass) { |
278
|
1 |
|
if ($metadata->getClassName() === $this->getMetadataFor($discriminatorClass)->getClassName()) { |
279
|
|
|
$metadata->discriminatorValue = $discriminatorValue; |
280
|
|
|
|
281
|
1 |
|
return; |
282
|
|
|
} |
283
|
|
|
} |
284
|
|
|
|
285
|
1 |
|
throw MappingException::mappedClassNotPartOfDiscriminatorMap($metadata->getClassName(), $metadata->getRootClassName()); |
286
|
|
|
} |
287
|
|
|
|
288
|
|
|
/** |
289
|
|
|
* Adds a default discriminator map if no one is given |
290
|
|
|
* |
291
|
|
|
* If an entity is of any inheritance type and does not contain a |
292
|
|
|
* discriminator map, then the map is generated automatically. This process |
293
|
|
|
* is expensive computation wise. |
294
|
|
|
* |
295
|
|
|
* The automatically generated discriminator map contains the lowercase short name of |
296
|
|
|
* each class as key. |
297
|
|
|
* |
298
|
|
|
* @throws MappingException |
299
|
|
|
*/ |
300
|
1 |
|
private function addDefaultDiscriminatorMap(ClassMetadata $class) : void |
301
|
|
|
{ |
302
|
1 |
|
$allClasses = $this->driver->getAllClassNames(); |
303
|
1 |
|
$fqcn = $class->getClassName(); |
304
|
1 |
|
$map = [$this->getShortName($fqcn) => $fqcn]; |
305
|
1 |
|
$duplicates = []; |
306
|
|
|
|
307
|
1 |
|
foreach ($allClasses as $subClassCandidate) { |
308
|
1 |
|
if (is_subclass_of($subClassCandidate, $fqcn)) { |
309
|
1 |
|
$shortName = $this->getShortName($subClassCandidate); |
310
|
|
|
|
311
|
1 |
|
if (isset($map[$shortName])) { |
312
|
|
|
$duplicates[] = $shortName; |
313
|
|
|
} |
314
|
|
|
|
315
|
1 |
|
$map[$shortName] = $subClassCandidate; |
316
|
|
|
} |
317
|
|
|
} |
318
|
|
|
|
319
|
1 |
|
if ($duplicates) { |
320
|
|
|
throw MappingException::duplicateDiscriminatorEntry($class->getClassName(), $duplicates, $map); |
321
|
|
|
} |
322
|
|
|
|
323
|
1 |
|
$class->setDiscriminatorMap($map); |
324
|
1 |
|
} |
325
|
|
|
|
326
|
|
|
/** |
327
|
|
|
* Gets the lower-case short name of a class. |
328
|
|
|
* |
329
|
|
|
* @param string $className |
330
|
|
|
*/ |
331
|
1 |
|
private function getShortName($className) : string |
332
|
|
|
{ |
333
|
1 |
|
if (strpos($className, '\\') === false) { |
334
|
|
|
return strtolower($className); |
335
|
|
|
} |
336
|
|
|
|
337
|
1 |
|
$parts = explode('\\', $className); |
338
|
|
|
|
339
|
1 |
|
return strtolower(end($parts)); |
340
|
|
|
} |
341
|
|
|
|
342
|
|
|
/** |
343
|
|
|
* Adds inherited fields to the subclass mapping. |
344
|
|
|
* |
345
|
|
|
* @throws MappingException |
346
|
|
|
*/ |
347
|
98 |
|
private function addInheritedProperties(ClassMetadata $subClass, ClassMetadata $parentClass) : void |
348
|
|
|
{ |
349
|
98 |
|
$isAbstract = $parentClass->isMappedSuperclass; |
350
|
|
|
|
351
|
98 |
|
foreach ($parentClass->getDeclaredPropertiesIterator() as $fieldName => $property) { |
352
|
97 |
|
if ($isAbstract && $property instanceof ToManyAssociationMetadata && ! $property->isOwningSide()) { |
353
|
1 |
|
throw MappingException::illegalToManyAssociationOnMappedSuperclass($parentClass->getClassName(), $fieldName); |
354
|
|
|
} |
355
|
|
|
|
356
|
96 |
|
$subClass->addInheritedProperty($property); |
357
|
|
|
} |
358
|
97 |
|
} |
359
|
|
|
|
360
|
|
|
/** |
361
|
|
|
* Adds inherited named native queries to the subclass mapping. |
362
|
|
|
* |
363
|
|
|
* @throws MappingException |
364
|
|
|
*/ |
365
|
7 |
|
private function addInheritedNamedNativeQueries(ClassMetadata $subClass, ClassMetadata $parentClass) : void |
366
|
|
|
{ |
367
|
7 |
|
foreach ($parentClass->namedNativeQueries as $name => $query) { |
368
|
7 |
|
if (isset($subClass->namedNativeQueries[$name])) { |
369
|
4 |
|
continue; |
370
|
|
|
} |
371
|
|
|
|
372
|
7 |
|
$subClass->addNamedNativeQuery( |
373
|
7 |
|
$name, |
374
|
7 |
|
$query['query'], |
375
|
|
|
[ |
376
|
7 |
|
'resultSetMapping' => $query['resultSetMapping'], |
377
|
7 |
|
'resultClass' => $query['resultClass'], |
378
|
|
|
] |
379
|
|
|
); |
380
|
|
|
} |
381
|
7 |
|
} |
382
|
|
|
|
383
|
|
|
/** |
384
|
|
|
* Adds inherited sql result set mappings to the subclass mapping. |
385
|
|
|
* |
386
|
|
|
* @throws MappingException |
387
|
|
|
*/ |
388
|
7 |
|
private function addInheritedSqlResultSetMappings(ClassMetadata $subClass, ClassMetadata $parentClass) : void |
389
|
|
|
{ |
390
|
7 |
|
foreach ($parentClass->sqlResultSetMappings as $name => $mapping) { |
391
|
7 |
|
if (isset($subClass->sqlResultSetMappings[$name])) { |
392
|
4 |
|
continue; |
393
|
|
|
} |
394
|
|
|
|
395
|
7 |
|
$entities = []; |
396
|
|
|
|
397
|
7 |
|
foreach ($mapping['entities'] as $entity) { |
398
|
7 |
|
$entities[] = [ |
399
|
7 |
|
'fields' => $entity['fields'], |
400
|
7 |
|
'discriminatorColumn' => $entity['discriminatorColumn'], |
401
|
7 |
|
'entityClass' => $entity['entityClass'], |
402
|
|
|
]; |
403
|
|
|
} |
404
|
|
|
|
405
|
7 |
|
$subClass->addSqlResultSetMapping( |
406
|
|
|
[ |
407
|
7 |
|
'name' => $mapping['name'], |
408
|
7 |
|
'columns' => $mapping['columns'], |
409
|
7 |
|
'entities' => $entities, |
410
|
|
|
] |
411
|
|
|
); |
412
|
|
|
} |
413
|
7 |
|
} |
414
|
|
|
|
415
|
|
|
/** |
416
|
|
|
* Completes the ID generator mapping. If "auto" is specified we choose the generator |
417
|
|
|
* most appropriate for the targeted database platform. |
418
|
|
|
* |
419
|
|
|
* @throws ORMException |
420
|
|
|
*/ |
421
|
360 |
|
private function completeIdentifierGeneratorMappings(ClassMetadata $class) : void |
422
|
|
|
{ |
423
|
360 |
|
foreach ($class->getDeclaredPropertiesIterator() as $property) { |
424
|
358 |
|
if (! $property instanceof FieldMetadata /*&& ! $property instanceof AssocationMetadata*/) { |
|
|
|
|
425
|
249 |
|
continue; |
426
|
|
|
} |
427
|
|
|
|
428
|
355 |
|
$this->completeFieldIdentifierGeneratorMapping($property); |
429
|
|
|
} |
430
|
360 |
|
} |
431
|
|
|
|
432
|
355 |
|
private function completeFieldIdentifierGeneratorMapping(FieldMetadata $field) |
433
|
|
|
{ |
434
|
355 |
|
if (! $field->hasValueGenerator()) { |
435
|
275 |
|
return; |
436
|
|
|
} |
437
|
|
|
|
438
|
285 |
|
$platform = $this->getTargetPlatform(); |
439
|
285 |
|
$class = $field->getDeclaringClass(); |
440
|
285 |
|
$generator = $field->getValueGenerator(); |
441
|
|
|
|
442
|
285 |
|
if ($generator->getType() === GeneratorType::AUTO) { |
443
|
274 |
|
$generator = new ValueGeneratorMetadata( |
444
|
274 |
|
$platform->prefersSequences() |
445
|
|
|
? GeneratorType::SEQUENCE |
446
|
274 |
|
: ($platform->prefersIdentityColumns() |
447
|
274 |
|
? GeneratorType::IDENTITY |
448
|
274 |
|
: GeneratorType::TABLE |
449
|
|
|
), |
450
|
274 |
|
$field->getValueGenerator()->getDefinition() |
451
|
|
|
); |
452
|
274 |
|
$field->setValueGenerator($generator); |
453
|
|
|
} |
454
|
|
|
|
455
|
|
|
// Validate generator definition and set defaults where needed |
456
|
285 |
|
switch ($generator->getType()) { |
457
|
285 |
|
case GeneratorType::SEQUENCE: |
458
|
|
|
// If there is no sequence definition yet, create a default definition |
459
|
6 |
|
if ($generator->getDefinition()) { |
460
|
6 |
|
break; |
461
|
|
|
} |
462
|
|
|
|
463
|
|
|
// @todo guilhermeblanco Move sequence generation to DBAL |
464
|
|
|
$sequencePrefix = $platform->getSequencePrefix($field->getTableName(), $field->getSchemaName()); |
465
|
|
|
$idSequenceName = sprintf('%s_%s_seq', $sequencePrefix, $field->getColumnName()); |
466
|
|
|
$sequenceName = $platform->fixSchemaElementName($idSequenceName); |
467
|
|
|
|
468
|
|
|
$field->setValueGenerator( |
469
|
|
|
new ValueGeneratorMetadata( |
470
|
|
|
$generator->getType(), |
471
|
|
|
[ |
472
|
|
|
'sequenceName' => $sequenceName, |
473
|
|
|
'allocationSize' => 1, |
474
|
|
|
] |
475
|
|
|
) |
476
|
|
|
); |
477
|
|
|
|
478
|
|
|
break; |
479
|
|
|
|
480
|
279 |
|
case GeneratorType::TABLE: |
481
|
|
|
throw new ORMException('TableGenerator not yet implemented.'); |
482
|
|
|
break; |
483
|
|
|
|
484
|
279 |
|
case GeneratorType::CUSTOM: |
485
|
1 |
|
$definition = $generator->getDefinition(); |
486
|
1 |
|
if (! isset($definition['class'])) { |
487
|
|
|
throw new ORMException(sprintf('Cannot instantiate custom generator, no class has been defined')); |
488
|
|
|
} |
489
|
1 |
|
if (! class_exists($definition['class'])) { |
490
|
|
|
throw new ORMException(sprintf('Cannot instantiate custom generator : %s', var_export($definition, true))); //$definition['class'])); |
|
|
|
|
491
|
|
|
} |
492
|
|
|
|
493
|
1 |
|
break; |
494
|
|
|
|
495
|
278 |
|
case GeneratorType::IDENTITY: |
496
|
|
|
case GeneratorType::NONE: |
497
|
|
|
case GeneratorType::UUID: |
498
|
278 |
|
break; |
499
|
|
|
|
500
|
|
|
default: |
501
|
|
|
throw new ORMException('Unknown generator type: ' . $generator->getType()); |
502
|
|
|
} |
503
|
285 |
|
} |
504
|
|
|
|
505
|
|
|
/** |
506
|
|
|
* {@inheritDoc} |
507
|
|
|
*/ |
508
|
197 |
|
protected function getDriver() : Driver\MappingDriver |
509
|
|
|
{ |
510
|
197 |
|
return $this->driver; |
511
|
|
|
} |
512
|
|
|
|
513
|
|
|
/** |
514
|
|
|
* {@inheritDoc} |
515
|
|
|
*/ |
516
|
|
|
protected function isEntity(ClassMetadata $class) : bool |
517
|
|
|
{ |
518
|
|
|
return isset($class->isMappedSuperclass) && $class->isMappedSuperclass === false; |
519
|
|
|
} |
520
|
|
|
|
521
|
285 |
|
private function getTargetPlatform() : Platforms\AbstractPlatform |
522
|
|
|
{ |
523
|
285 |
|
if (! $this->targetPlatform) { |
524
|
285 |
|
$this->targetPlatform = $this->em->getConnection()->getDatabasePlatform(); |
525
|
|
|
} |
526
|
|
|
|
527
|
285 |
|
return $this->targetPlatform; |
528
|
|
|
} |
529
|
|
|
|
530
|
359 |
|
private function buildValueGenerationPlan(ClassMetadata $class) : void |
531
|
|
|
{ |
532
|
|
|
/** @var LocalColumnMetadata[] $generatedProperties */ |
533
|
359 |
|
$generatedProperties = []; |
534
|
|
|
|
535
|
359 |
|
foreach ($class->getDeclaredPropertiesIterator() as $property) { |
536
|
357 |
|
if (! ($property instanceof LocalColumnMetadata && $property->hasValueGenerator())) { |
537
|
327 |
|
continue; |
538
|
|
|
} |
539
|
|
|
|
540
|
284 |
|
$generatedProperties[] = $property; |
541
|
|
|
} |
542
|
|
|
|
543
|
359 |
|
switch (count($generatedProperties)) { |
544
|
359 |
|
case 0: |
545
|
114 |
|
$class->setValueGenerationPlan(new NoopValueGenerationPlan()); |
546
|
114 |
|
break; |
547
|
|
|
|
548
|
284 |
|
case 1: |
549
|
284 |
|
$property = reset($generatedProperties); |
550
|
284 |
|
$executor = new ColumnValueGeneratorExecutor($property, $this->createPropertyValueGenerator($class, $property)); |
551
|
|
|
|
552
|
284 |
|
$class->setValueGenerationPlan(new SingleValueGenerationPlan($class, $executor)); |
553
|
284 |
|
break; |
554
|
|
|
|
555
|
|
|
default: |
556
|
|
|
$executors = []; |
557
|
|
|
|
558
|
|
|
foreach ($generatedProperties as $property) { |
559
|
|
|
$executors[] = new ColumnValueGeneratorExecutor($property, $this->createPropertyValueGenerator($class, $property)); |
560
|
|
|
} |
561
|
|
|
|
562
|
|
|
$class->setValueGenerationPlan(new CompositeValueGenerationPlan($class, $executors)); |
563
|
|
|
break; |
564
|
|
|
} |
565
|
359 |
|
} |
566
|
|
|
|
567
|
284 |
|
private function createPropertyValueGenerator( |
568
|
|
|
ClassMetadata $class, |
569
|
|
|
LocalColumnMetadata $property |
570
|
|
|
) : Sequencing\Generator { |
571
|
284 |
|
$platform = $this->getTargetPlatform(); |
572
|
|
|
|
573
|
284 |
|
switch ($property->getValueGenerator()->getType()) { |
574
|
284 |
|
case GeneratorType::IDENTITY: |
575
|
277 |
|
$sequenceName = null; |
576
|
|
|
|
577
|
|
|
// Platforms that do not have native IDENTITY support need a sequence to emulate this behaviour. |
578
|
277 |
|
if ($platform->usesSequenceEmulatedIdentityColumns()) { |
579
|
|
|
$sequencePrefix = $platform->getSequencePrefix($class->getTableName(), $class->getSchemaName()); |
580
|
|
|
$idSequenceName = $platform->getIdentitySequenceName($sequencePrefix, $property->getColumnName()); |
581
|
|
|
$sequenceName = $platform->quoteIdentifier($platform->fixSchemaElementName($idSequenceName)); |
582
|
|
|
} |
583
|
|
|
|
584
|
277 |
|
return $property->getTypeName() === 'bigint' |
585
|
1 |
|
? new Sequencing\BigIntegerIdentityGenerator($sequenceName) |
586
|
277 |
|
: new Sequencing\IdentityGenerator($sequenceName); |
587
|
|
|
|
588
|
7 |
|
case GeneratorType::SEQUENCE: |
589
|
6 |
|
$definition = $property->getValueGenerator()->getDefinition(); |
590
|
6 |
|
return new Sequencing\SequenceGenerator( |
591
|
6 |
|
$platform->quoteIdentifier($definition['sequenceName']), |
592
|
6 |
|
$definition['allocationSize'] |
593
|
|
|
); |
594
|
|
|
|
595
|
1 |
|
case GeneratorType::UUID: |
596
|
|
|
return new Sequencing\UuidGenerator(); |
597
|
|
|
|
598
|
1 |
|
case GeneratorType::CUSTOM: |
599
|
1 |
|
$class = $property->getValueGenerator()->getDefinition()['class']; |
600
|
1 |
|
return new $class(); |
601
|
|
|
} |
602
|
|
|
|
603
|
|
|
throw new \UnexpectedValueException('Platform ' . $property->getValueGenerator()->getType() . ' is not supported'); |
604
|
|
|
} |
605
|
|
|
} |
606
|
|
|
|
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.