1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace Doctrine\ORM\Mapping; |
6
|
|
|
|
7
|
|
|
use Doctrine\ORM\Cache\CacheException; |
8
|
|
|
use Doctrine\ORM\EntityManagerInterface; |
9
|
|
|
use Doctrine\ORM\Mapping\Factory\NamingStrategy; |
10
|
|
|
use Doctrine\ORM\Reflection\ReflectionService; |
11
|
|
|
use Doctrine\ORM\Sequencing\Planning\ValueGenerationPlan; |
12
|
|
|
use Doctrine\ORM\Utility\PersisterHelper; |
13
|
|
|
use function array_diff; |
14
|
|
|
use function array_filter; |
15
|
|
|
use function array_intersect; |
16
|
|
|
use function array_map; |
17
|
|
|
use function array_merge; |
18
|
|
|
use function class_exists; |
19
|
|
|
use function count; |
20
|
|
|
use function explode; |
21
|
|
|
use function get_class; |
22
|
|
|
use function in_array; |
23
|
|
|
use function interface_exists; |
24
|
|
|
use function is_subclass_of; |
25
|
|
|
use function method_exists; |
26
|
|
|
use function spl_object_id; |
27
|
|
|
use function sprintf; |
28
|
|
|
use function strpos; |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* A <tt>ClassMetadata</tt> instance holds all the object-relational mapping metadata |
32
|
|
|
* of an entity and its associations. |
33
|
|
|
* |
34
|
|
|
*/ |
35
|
|
|
class ClassMetadata extends ComponentMetadata implements TableOwner |
36
|
|
|
{ |
37
|
|
|
/** |
38
|
|
|
* The name of the custom repository class used for the entity class. |
39
|
|
|
* (Optional). |
40
|
|
|
* |
41
|
|
|
* @var string |
42
|
|
|
*/ |
43
|
|
|
protected $customRepositoryClassName; |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* READ-ONLY: Whether this class describes the mapping of a mapped superclass. |
47
|
|
|
* |
48
|
|
|
* @var bool |
49
|
|
|
*/ |
50
|
|
|
public $isMappedSuperclass = false; |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* READ-ONLY: Whether this class describes the mapping of an embeddable class. |
54
|
|
|
* |
55
|
|
|
* @var bool |
56
|
|
|
*/ |
57
|
|
|
public $isEmbeddedClass = false; |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* Whether this class describes the mapping of a read-only class. |
61
|
|
|
* That means it is never considered for change-tracking in the UnitOfWork. |
62
|
|
|
* It is a very helpful performance optimization for entities that are immutable, |
63
|
|
|
* either in your domain or through the relation database (coming from a view, |
64
|
|
|
* or a history table for example). |
65
|
|
|
* |
66
|
|
|
* @var bool |
67
|
|
|
*/ |
68
|
|
|
private $readOnly = false; |
69
|
|
|
|
70
|
|
|
/** |
71
|
|
|
* The names of all subclasses (descendants). |
72
|
|
|
* |
73
|
|
|
* @var string[] |
74
|
|
|
*/ |
75
|
|
|
protected $subClasses = []; |
76
|
|
|
|
77
|
|
|
/** |
78
|
|
|
* READ-ONLY: The names of all embedded classes based on properties. |
79
|
|
|
* |
80
|
|
|
* @var string[] |
81
|
|
|
*/ |
82
|
|
|
//public $embeddedClasses = []; |
|
|
|
|
83
|
|
|
|
84
|
|
|
/** |
85
|
|
|
* READ-ONLY: The registered lifecycle callbacks for entities of this class. |
86
|
|
|
* |
87
|
|
|
* @var string[][] |
88
|
|
|
*/ |
89
|
|
|
public $lifecycleCallbacks = []; |
90
|
|
|
|
91
|
|
|
/** |
92
|
|
|
* READ-ONLY: The registered entity listeners. |
93
|
|
|
* |
94
|
|
|
* @var mixed[][] |
95
|
|
|
*/ |
96
|
|
|
public $entityListeners = []; |
97
|
|
|
|
98
|
|
|
/** |
99
|
|
|
* READ-ONLY: The field names of all fields that are part of the identifier/primary key |
100
|
|
|
* of the mapped entity class. |
101
|
|
|
* |
102
|
|
|
* @var string[] |
103
|
|
|
*/ |
104
|
|
|
public $identifier = []; |
105
|
|
|
|
106
|
|
|
/** |
107
|
|
|
* READ-ONLY: The inheritance mapping type used by the class. |
108
|
|
|
* |
109
|
|
|
* @var string |
110
|
|
|
*/ |
111
|
|
|
public $inheritanceType = InheritanceType::NONE; |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* READ-ONLY: The policy used for change-tracking on entities of this class. |
115
|
|
|
* |
116
|
|
|
* @var string |
117
|
|
|
*/ |
118
|
|
|
public $changeTrackingPolicy = ChangeTrackingPolicy::DEFERRED_IMPLICIT; |
119
|
|
|
|
120
|
|
|
/** |
121
|
|
|
* READ-ONLY: The discriminator value of this class. |
122
|
|
|
* |
123
|
|
|
* <b>This does only apply to the JOINED and SINGLE_TABLE inheritance mapping strategies |
124
|
|
|
* where a discriminator column is used.</b> |
125
|
|
|
* |
126
|
|
|
* @var mixed |
127
|
|
|
* |
128
|
|
|
* @see discriminatorColumn |
129
|
|
|
*/ |
130
|
|
|
public $discriminatorValue; |
131
|
|
|
|
132
|
|
|
/** |
133
|
|
|
* READ-ONLY: The discriminator map of all mapped classes in the hierarchy. |
134
|
|
|
* |
135
|
|
|
* <b>This does only apply to the JOINED and SINGLE_TABLE inheritance mapping strategies |
136
|
|
|
* where a discriminator column is used.</b> |
137
|
|
|
* |
138
|
|
|
* @var string[] |
139
|
|
|
* |
140
|
|
|
* @see discriminatorColumn |
141
|
|
|
*/ |
142
|
|
|
public $discriminatorMap = []; |
143
|
|
|
|
144
|
|
|
/** |
145
|
|
|
* READ-ONLY: The definition of the discriminator column used in JOINED and SINGLE_TABLE |
146
|
|
|
* inheritance mappings. |
147
|
|
|
* |
148
|
|
|
* @var DiscriminatorColumnMetadata |
149
|
|
|
*/ |
150
|
|
|
public $discriminatorColumn; |
151
|
|
|
|
152
|
|
|
/** |
153
|
|
|
* READ-ONLY: The primary table metadata. |
154
|
|
|
* |
155
|
|
|
* @var TableMetadata |
156
|
|
|
*/ |
157
|
|
|
public $table; |
158
|
|
|
|
159
|
|
|
/** |
160
|
|
|
* READ-ONLY: An array of field names. Used to look up field names from column names. |
161
|
|
|
* Keys are column names and values are field names. |
162
|
|
|
* |
163
|
|
|
* @var string[] |
164
|
|
|
*/ |
165
|
|
|
public $fieldNames = []; |
166
|
|
|
|
167
|
|
|
/** |
168
|
|
|
* READ-ONLY: The field which is used for versioning in optimistic locking (if any). |
169
|
|
|
* |
170
|
|
|
* @var FieldMetadata|null |
171
|
|
|
*/ |
172
|
|
|
public $versionProperty; |
173
|
|
|
|
174
|
|
|
/** |
175
|
|
|
* NamingStrategy determining the default column and table names. |
176
|
|
|
* |
177
|
|
|
* @var NamingStrategy |
178
|
|
|
*/ |
179
|
|
|
protected $namingStrategy; |
180
|
|
|
|
181
|
|
|
/** |
182
|
|
|
* Value generation plan is responsible for generating values for auto-generated fields. |
183
|
|
|
* |
184
|
|
|
* @var ValueGenerationPlan |
185
|
|
|
*/ |
186
|
|
|
protected $valueGenerationPlan; |
187
|
|
|
|
188
|
|
|
/** |
189
|
|
|
* Initializes a new ClassMetadata instance that will hold the object-relational mapping |
190
|
|
|
* metadata of the class with the given name. |
191
|
|
|
* |
192
|
|
|
* @param string $entityName The name of the entity class. |
193
|
|
|
*/ |
194
|
456 |
|
public function __construct( |
195
|
|
|
string $entityName, |
196
|
|
|
ClassMetadataBuildingContext $metadataBuildingContext |
197
|
|
|
) { |
198
|
456 |
|
parent::__construct($entityName, $metadataBuildingContext); |
199
|
|
|
|
200
|
456 |
|
$this->namingStrategy = $metadataBuildingContext->getNamingStrategy(); |
201
|
456 |
|
} |
202
|
|
|
|
203
|
2 |
|
public function setClassName(string $className) |
204
|
|
|
{ |
205
|
2 |
|
$this->className = $className; |
206
|
2 |
|
} |
207
|
|
|
|
208
|
|
|
public function getColumnsIterator() : \ArrayIterator |
209
|
|
|
{ |
210
|
|
|
$iterator = parent::getColumnsIterator(); |
211
|
|
|
|
212
|
|
|
if ($this->discriminatorColumn) { |
213
|
|
|
$iterator->offsetSet($this->discriminatorColumn->getColumnName(), $this->discriminatorColumn); |
|
|
|
|
214
|
|
|
} |
215
|
|
|
|
216
|
|
|
return $iterator; |
217
|
|
|
} |
218
|
|
|
|
219
|
11 |
|
public function getAncestorsIterator() : \ArrayIterator |
220
|
|
|
{ |
221
|
11 |
|
$ancestors = new \ArrayIterator(); |
222
|
11 |
|
$parent = $this; |
223
|
|
|
|
224
|
11 |
|
while (($parent = $parent->parent) !== null) { |
225
|
8 |
|
if ($parent instanceof ClassMetadata && $parent->isMappedSuperclass) { |
226
|
1 |
|
continue; |
227
|
|
|
} |
228
|
|
|
|
229
|
7 |
|
$ancestors->append($parent); |
230
|
|
|
} |
231
|
|
|
|
232
|
11 |
|
return $ancestors; |
233
|
|
|
} |
234
|
|
|
|
235
|
1252 |
|
public function getRootClassName() : string |
236
|
|
|
{ |
237
|
1252 |
|
return ($this->parent instanceof ClassMetadata && ! $this->parent->isMappedSuperclass) |
238
|
403 |
|
? $this->parent->getRootClassName() |
239
|
1252 |
|
: $this->className |
240
|
|
|
; |
241
|
|
|
} |
242
|
|
|
|
243
|
|
|
/** |
244
|
|
|
* Handles metadata cloning nicely. |
245
|
|
|
*/ |
246
|
13 |
|
public function __clone() |
247
|
|
|
{ |
248
|
13 |
|
if ($this->cache) { |
249
|
12 |
|
$this->cache = clone $this->cache; |
250
|
|
|
} |
251
|
|
|
|
252
|
13 |
|
foreach ($this->declaredProperties as $name => $property) { |
253
|
13 |
|
$this->declaredProperties[$name] = clone $property; |
254
|
|
|
} |
255
|
13 |
|
} |
256
|
|
|
|
257
|
|
|
/** |
258
|
|
|
* Creates a string representation of this instance. |
259
|
|
|
* |
260
|
|
|
* @return string The string representation of this instance. |
261
|
|
|
* |
262
|
|
|
* @todo Construct meaningful string representation. |
263
|
|
|
*/ |
264
|
|
|
public function __toString() |
265
|
|
|
{ |
266
|
|
|
return __CLASS__ . '@' . spl_object_id($this); |
267
|
|
|
} |
268
|
|
|
|
269
|
|
|
/** |
270
|
|
|
* Determines which fields get serialized. |
271
|
|
|
* |
272
|
|
|
* It is only serialized what is necessary for best unserialization performance. |
273
|
|
|
* That means any metadata properties that are not set or empty or simply have |
274
|
|
|
* their default value are NOT serialized. |
275
|
|
|
* |
276
|
|
|
* Parts that are also NOT serialized because they can not be properly unserialized: |
277
|
|
|
* - reflectionClass |
278
|
|
|
* |
279
|
|
|
* @return string[] The names of all the fields that should be serialized. |
280
|
|
|
*/ |
281
|
5 |
|
public function __sleep() |
282
|
|
|
{ |
283
|
5 |
|
$serialized = []; |
284
|
|
|
|
285
|
|
|
// This metadata is always serialized/cached. |
286
|
5 |
|
$serialized = array_merge($serialized, [ |
287
|
5 |
|
'declaredProperties', |
288
|
|
|
'fieldNames', |
289
|
|
|
//'embeddedClasses', |
290
|
|
|
'identifier', |
291
|
|
|
'className', |
292
|
|
|
'parent', |
293
|
|
|
'table', |
294
|
|
|
'valueGenerationPlan', |
295
|
|
|
]); |
296
|
|
|
|
297
|
|
|
// The rest of the metadata is only serialized if necessary. |
298
|
5 |
|
if ($this->changeTrackingPolicy !== ChangeTrackingPolicy::DEFERRED_IMPLICIT) { |
299
|
|
|
$serialized[] = 'changeTrackingPolicy'; |
300
|
|
|
} |
301
|
|
|
|
302
|
5 |
|
if ($this->customRepositoryClassName) { |
303
|
1 |
|
$serialized[] = 'customRepositoryClassName'; |
304
|
|
|
} |
305
|
|
|
|
306
|
5 |
|
if ($this->inheritanceType !== InheritanceType::NONE) { |
307
|
1 |
|
$serialized[] = 'inheritanceType'; |
308
|
1 |
|
$serialized[] = 'discriminatorColumn'; |
309
|
1 |
|
$serialized[] = 'discriminatorValue'; |
310
|
1 |
|
$serialized[] = 'discriminatorMap'; |
311
|
1 |
|
$serialized[] = 'subClasses'; |
312
|
|
|
} |
313
|
|
|
|
314
|
5 |
|
if ($this->isMappedSuperclass) { |
315
|
|
|
$serialized[] = 'isMappedSuperclass'; |
316
|
|
|
} |
317
|
|
|
|
318
|
5 |
|
if ($this->isEmbeddedClass) { |
319
|
|
|
$serialized[] = 'isEmbeddedClass'; |
320
|
|
|
} |
321
|
|
|
|
322
|
5 |
|
if ($this->isVersioned()) { |
323
|
|
|
$serialized[] = 'versionProperty'; |
324
|
|
|
} |
325
|
|
|
|
326
|
5 |
|
if ($this->lifecycleCallbacks) { |
|
|
|
|
327
|
|
|
$serialized[] = 'lifecycleCallbacks'; |
328
|
|
|
} |
329
|
|
|
|
330
|
5 |
|
if ($this->entityListeners) { |
|
|
|
|
331
|
1 |
|
$serialized[] = 'entityListeners'; |
332
|
|
|
} |
333
|
|
|
|
334
|
5 |
|
if ($this->cache) { |
335
|
|
|
$serialized[] = 'cache'; |
336
|
|
|
} |
337
|
|
|
|
338
|
5 |
|
if ($this->readOnly) { |
339
|
1 |
|
$serialized[] = 'readOnly'; |
340
|
|
|
} |
341
|
|
|
|
342
|
5 |
|
return $serialized; |
343
|
|
|
} |
344
|
|
|
|
345
|
|
|
/** |
346
|
|
|
* Restores some state that can not be serialized/unserialized. |
347
|
|
|
*/ |
348
|
1622 |
|
public function wakeupReflection(ReflectionService $reflectionService) : void |
349
|
|
|
{ |
350
|
|
|
// Restore ReflectionClass and properties |
351
|
1622 |
|
$this->reflectionClass = $reflectionService->getClass($this->className); |
352
|
|
|
|
353
|
1622 |
|
if (! $this->reflectionClass) { |
354
|
|
|
return; |
355
|
|
|
} |
356
|
|
|
|
357
|
1622 |
|
$this->className = $this->reflectionClass->getName(); |
358
|
|
|
|
359
|
1622 |
|
foreach ($this->declaredProperties as $property) { |
360
|
|
|
/** @var Property $property */ |
361
|
1621 |
|
$property->wakeupReflection($reflectionService); |
362
|
|
|
} |
363
|
1622 |
|
} |
364
|
|
|
|
365
|
|
|
/** |
366
|
|
|
* Validates Identifier. |
367
|
|
|
* |
368
|
|
|
* @throws MappingException |
369
|
|
|
*/ |
370
|
360 |
|
public function validateIdentifier() : void |
371
|
|
|
{ |
372
|
360 |
|
if ($this->isMappedSuperclass || $this->isEmbeddedClass) { |
373
|
28 |
|
return; |
374
|
|
|
} |
375
|
|
|
|
376
|
|
|
// Verify & complete identifier mapping |
377
|
360 |
|
if (! $this->identifier) { |
|
|
|
|
378
|
4 |
|
throw MappingException::identifierRequired($this->className); |
379
|
|
|
} |
380
|
|
|
|
381
|
356 |
|
$explicitlyGeneratedProperties = array_filter($this->declaredProperties, function (Property $property) : bool { |
382
|
356 |
|
return $property instanceof FieldMetadata |
383
|
356 |
|
&& $property->isPrimaryKey() |
384
|
356 |
|
&& $property->hasValueGenerator(); |
385
|
356 |
|
}); |
386
|
|
|
|
387
|
356 |
|
if ($explicitlyGeneratedProperties && $this->isIdentifierComposite()) { |
|
|
|
|
388
|
|
|
throw MappingException::compositeKeyAssignedIdGeneratorRequired($this->className); |
389
|
|
|
} |
390
|
356 |
|
} |
391
|
|
|
|
392
|
|
|
/** |
393
|
|
|
* Validates association targets actually exist. |
394
|
|
|
* |
395
|
|
|
* @throws MappingException |
396
|
|
|
*/ |
397
|
359 |
|
public function validateAssociations() : void |
398
|
|
|
{ |
399
|
359 |
|
array_map( |
400
|
359 |
|
function (Property $property) { |
401
|
359 |
|
if (! ($property instanceof AssociationMetadata)) { |
402
|
356 |
|
return; |
403
|
|
|
} |
404
|
|
|
|
405
|
246 |
|
$targetEntity = $property->getTargetEntity(); |
406
|
|
|
|
407
|
246 |
|
if (! class_exists($targetEntity)) { |
408
|
1 |
|
throw MappingException::invalidTargetEntityClass($targetEntity, $this->className, $property->getName()); |
409
|
|
|
} |
410
|
359 |
|
}, |
411
|
359 |
|
$this->declaredProperties |
412
|
|
|
); |
413
|
358 |
|
} |
414
|
|
|
|
415
|
|
|
/** |
416
|
|
|
* Validates lifecycle callbacks. |
417
|
|
|
* |
418
|
|
|
* @throws MappingException |
419
|
|
|
*/ |
420
|
359 |
|
public function validateLifecycleCallbacks(ReflectionService $reflectionService) : void |
421
|
|
|
{ |
422
|
359 |
|
foreach ($this->lifecycleCallbacks as $callbacks) { |
423
|
|
|
/** @var array $callbacks */ |
424
|
11 |
|
foreach ($callbacks as $callbackFuncName) { |
425
|
11 |
|
if (! $reflectionService->hasPublicMethod($this->className, $callbackFuncName)) { |
426
|
11 |
|
throw MappingException::lifecycleCallbackMethodNotFound($this->className, $callbackFuncName); |
427
|
|
|
} |
428
|
|
|
} |
429
|
|
|
} |
430
|
358 |
|
} |
431
|
|
|
|
432
|
|
|
/** |
433
|
|
|
* Sets the change tracking policy used by this class. |
434
|
|
|
*/ |
435
|
103 |
|
public function setChangeTrackingPolicy(string $policy) : void |
436
|
|
|
{ |
437
|
103 |
|
$this->changeTrackingPolicy = $policy; |
438
|
103 |
|
} |
439
|
|
|
|
440
|
|
|
/** |
441
|
|
|
* Checks whether a field is part of the identifier/primary key field(s). |
442
|
|
|
* |
443
|
|
|
* @param string $fieldName The field name. |
444
|
|
|
* |
445
|
|
|
* @return bool TRUE if the field is part of the table identifier/primary key field(s), FALSE otherwise. |
446
|
|
|
*/ |
447
|
1015 |
|
public function isIdentifier(string $fieldName) : bool |
448
|
|
|
{ |
449
|
1015 |
|
if (! $this->identifier) { |
|
|
|
|
450
|
1 |
|
return false; |
451
|
|
|
} |
452
|
|
|
|
453
|
1014 |
|
if (! $this->isIdentifierComposite()) { |
454
|
1010 |
|
return $fieldName === $this->identifier[0]; |
455
|
|
|
} |
456
|
|
|
|
457
|
90 |
|
return in_array($fieldName, $this->identifier, true); |
458
|
|
|
} |
459
|
|
|
|
460
|
1199 |
|
public function isIdentifierComposite() : bool |
461
|
|
|
{ |
462
|
1199 |
|
return isset($this->identifier[1]); |
463
|
|
|
} |
464
|
|
|
|
465
|
|
|
/** |
466
|
|
|
* Gets the result set mapping. |
467
|
|
|
* |
468
|
|
|
* @see ClassMetadata::$sqlResultSetMappings |
469
|
|
|
* |
470
|
|
|
* @param string $name The result set mapping name. |
471
|
|
|
* |
472
|
|
|
* @return mixed[] |
473
|
|
|
* |
474
|
|
|
* @throws MappingException |
475
|
|
|
*/ |
476
|
|
|
public function getSqlResultSetMapping($name) |
477
|
|
|
{ |
478
|
|
|
if (! isset($this->sqlResultSetMappings[$name])) { |
479
|
|
|
throw MappingException::resultMappingNotFound($this->className, $name); |
480
|
|
|
} |
481
|
|
|
|
482
|
|
|
return $this->sqlResultSetMappings[$name]; |
483
|
|
|
} |
484
|
|
|
|
485
|
|
|
/** |
486
|
|
|
* Gets all sql result set mappings of the class. |
487
|
|
|
* |
488
|
|
|
* @return mixed[][] |
489
|
|
|
*/ |
490
|
|
|
public function getSqlResultSetMappings() |
491
|
|
|
{ |
492
|
|
|
return $this->sqlResultSetMappings; |
493
|
|
|
} |
494
|
|
|
|
495
|
|
|
/** |
496
|
|
|
* Validates & completes the basic mapping information for field mapping. |
497
|
|
|
* |
498
|
|
|
* @throws MappingException If something is wrong with the mapping. |
499
|
|
|
*/ |
500
|
404 |
|
protected function validateAndCompleteFieldMapping(FieldMetadata $property) |
501
|
|
|
{ |
502
|
404 |
|
$fieldName = $property->getName(); |
503
|
404 |
|
$columnName = $property->getColumnName(); |
504
|
|
|
|
505
|
404 |
|
if (empty($columnName)) { |
506
|
341 |
|
$columnName = $this->namingStrategy->propertyToColumnName($fieldName, $this->className); |
507
|
|
|
|
508
|
341 |
|
$property->setColumnName($columnName); |
509
|
|
|
} |
510
|
|
|
|
511
|
404 |
|
if (! $this->isMappedSuperclass) { |
512
|
397 |
|
$property->setTableName($this->getTableName()); |
513
|
|
|
} |
514
|
|
|
|
515
|
|
|
// Check for already declared column |
516
|
404 |
|
if (isset($this->fieldNames[$columnName]) || |
517
|
404 |
|
($this->discriminatorColumn !== null && $this->discriminatorColumn->getColumnName() === $columnName)) { |
518
|
2 |
|
throw MappingException::duplicateColumnName($this->className, $columnName); |
519
|
|
|
} |
520
|
|
|
|
521
|
|
|
// Complete id mapping |
522
|
403 |
|
if ($property->isPrimaryKey()) { |
523
|
389 |
|
if ($this->versionProperty !== null && $this->versionProperty->getName() === $fieldName) { |
524
|
|
|
throw MappingException::cannotVersionIdField($this->className, $fieldName); |
525
|
|
|
} |
526
|
|
|
|
527
|
389 |
|
if ($property->getType()->canRequireSQLConversion()) { |
528
|
|
|
throw MappingException::sqlConversionNotAllowedForPrimaryKeyProperties($this->className, $property); |
529
|
|
|
} |
530
|
|
|
|
531
|
389 |
|
if (! in_array($fieldName, $this->identifier, true)) { |
532
|
389 |
|
$this->identifier[] = $fieldName; |
533
|
|
|
} |
534
|
|
|
} |
535
|
|
|
|
536
|
403 |
|
$this->fieldNames[$columnName] = $fieldName; |
537
|
403 |
|
} |
538
|
|
|
|
539
|
|
|
/** |
540
|
|
|
* Validates & completes the basic mapping information for field mapping. |
541
|
|
|
* |
542
|
|
|
* @throws MappingException If something is wrong with the mapping. |
543
|
|
|
*/ |
544
|
20 |
|
protected function validateAndCompleteVersionFieldMapping(VersionFieldMetadata $property) |
545
|
|
|
{ |
546
|
20 |
|
$this->versionProperty = $property; |
547
|
|
|
|
548
|
20 |
|
$options = $property->getOptions(); |
549
|
|
|
|
550
|
20 |
|
if (isset($options['default'])) { |
551
|
|
|
return; |
552
|
|
|
} |
553
|
|
|
|
554
|
20 |
|
if (in_array($property->getTypeName(), ['integer', 'bigint', 'smallint'], true)) { |
555
|
18 |
|
$property->setOptions(array_merge($options, ['default' => 1])); |
556
|
|
|
|
557
|
18 |
|
return; |
558
|
|
|
} |
559
|
|
|
|
560
|
3 |
|
if ($property->getTypeName() === 'datetime') { |
561
|
2 |
|
$property->setOptions(array_merge($options, ['default' => 'CURRENT_TIMESTAMP'])); |
562
|
|
|
|
563
|
2 |
|
return; |
564
|
|
|
} |
565
|
|
|
|
566
|
1 |
|
throw MappingException::unsupportedOptimisticLockingType($property->getType()); |
567
|
|
|
} |
568
|
|
|
|
569
|
|
|
/** |
570
|
|
|
* Validates & completes the basic mapping information that is common to all |
571
|
|
|
* association mappings (one-to-one, many-ot-one, one-to-many, many-to-many). |
572
|
|
|
* |
573
|
|
|
* @throws MappingException If something is wrong with the mapping. |
574
|
|
|
* @throws CacheException If entity is not cacheable. |
575
|
|
|
*/ |
576
|
284 |
|
protected function validateAndCompleteAssociationMapping(AssociationMetadata $property) |
577
|
|
|
{ |
578
|
284 |
|
$fieldName = $property->getName(); |
579
|
284 |
|
$targetEntity = $property->getTargetEntity(); |
580
|
|
|
|
581
|
284 |
|
if (! $targetEntity) { |
582
|
|
|
throw MappingException::missingTargetEntity($fieldName); |
583
|
|
|
} |
584
|
|
|
|
585
|
284 |
|
$property->setSourceEntity($this->className); |
586
|
284 |
|
$property->setTargetEntity($targetEntity); |
587
|
|
|
|
588
|
|
|
// Complete id mapping |
589
|
284 |
|
if ($property->isPrimaryKey()) { |
590
|
44 |
|
if ($property->isOrphanRemoval()) { |
591
|
1 |
|
throw MappingException::illegalOrphanRemovalOnIdentifierAssociation($this->className, $fieldName); |
592
|
|
|
} |
593
|
|
|
|
594
|
43 |
|
if (! in_array($property->getName(), $this->identifier, true)) { |
595
|
43 |
|
if ($property instanceof ToOneAssociationMetadata && count($property->getJoinColumns()) >= 2) { |
596
|
|
|
throw MappingException::cannotMapCompositePrimaryKeyEntitiesAsForeignId( |
597
|
|
|
$property->getTargetEntity(), |
598
|
|
|
$this->className, |
599
|
|
|
$fieldName |
600
|
|
|
); |
601
|
|
|
} |
602
|
|
|
|
603
|
43 |
|
$this->identifier[] = $property->getName(); |
604
|
|
|
} |
605
|
|
|
|
606
|
43 |
|
if ($this->cache && ! $property->getCache()) { |
607
|
2 |
|
throw CacheException::nonCacheableEntityAssociation($this->className, $fieldName); |
608
|
|
|
} |
609
|
|
|
|
610
|
41 |
|
if ($property instanceof ToManyAssociationMetadata) { |
611
|
1 |
|
throw MappingException::illegalToManyIdentifierAssociation($this->className, $property->getName()); |
612
|
|
|
} |
613
|
|
|
} |
614
|
|
|
|
615
|
|
|
// Cascades |
616
|
280 |
|
$cascadeTypes = ['remove', 'persist', 'refresh']; |
617
|
280 |
|
$cascades = array_map('strtolower', $property->getCascade()); |
618
|
|
|
|
619
|
280 |
|
if (in_array('all', $cascades, true)) { |
620
|
6 |
|
$cascades = $cascadeTypes; |
621
|
|
|
} |
622
|
|
|
|
623
|
280 |
|
if (count($cascades) !== count(array_intersect($cascades, $cascadeTypes))) { |
624
|
1 |
|
$diffCascades = array_diff($cascades, array_intersect($cascades, $cascadeTypes)); |
625
|
|
|
|
626
|
1 |
|
throw MappingException::invalidCascadeOption($diffCascades, $this->className, $fieldName); |
627
|
|
|
} |
628
|
|
|
|
629
|
279 |
|
$property->setCascade($cascades); |
630
|
279 |
|
} |
631
|
|
|
|
632
|
|
|
/** |
633
|
|
|
* Validates & completes a to-one association mapping. |
634
|
|
|
* |
635
|
|
|
* @param ToOneAssociationMetadata $property The association mapping to validate & complete. |
636
|
|
|
* |
637
|
|
|
* @throws \RuntimeException |
638
|
|
|
* @throws MappingException |
639
|
|
|
*/ |
640
|
244 |
|
protected function validateAndCompleteToOneAssociationMetadata(ToOneAssociationMetadata $property) |
641
|
|
|
{ |
642
|
244 |
|
$fieldName = $property->getName(); |
643
|
|
|
|
644
|
244 |
|
if ($property->isOwningSide()) { |
645
|
240 |
|
if (empty($property->getJoinColumns())) { |
646
|
|
|
// Apply default join column |
647
|
85 |
|
$property->addJoinColumn(new JoinColumnMetadata()); |
648
|
|
|
} |
649
|
|
|
|
650
|
240 |
|
$uniqueConstraintColumns = []; |
651
|
|
|
|
652
|
240 |
|
foreach ($property->getJoinColumns() as $joinColumn) { |
653
|
|
|
/** @var JoinColumnMetadata $joinColumn */ |
654
|
240 |
|
if ($property instanceof OneToOneAssociationMetadata && $this->inheritanceType !== InheritanceType::SINGLE_TABLE) { |
655
|
109 |
|
if (count($property->getJoinColumns()) === 1) { |
656
|
107 |
|
if (! $property->isPrimaryKey()) { |
657
|
107 |
|
$joinColumn->setUnique(true); |
658
|
|
|
} |
659
|
|
|
} else { |
660
|
2 |
|
$uniqueConstraintColumns[] = $joinColumn->getColumnName(); |
661
|
|
|
} |
662
|
|
|
} |
663
|
|
|
|
664
|
240 |
|
$joinColumn->setTableName(! $this->isMappedSuperclass ? $this->getTableName() : null); |
665
|
|
|
|
666
|
240 |
|
if (! $joinColumn->getColumnName()) { |
667
|
98 |
|
$joinColumn->setColumnName($this->namingStrategy->joinColumnName($fieldName, $this->className)); |
668
|
|
|
} |
669
|
|
|
|
670
|
240 |
|
if (! $joinColumn->getReferencedColumnName()) { |
671
|
85 |
|
$joinColumn->setReferencedColumnName($this->namingStrategy->referenceColumnName()); |
672
|
|
|
} |
673
|
|
|
|
674
|
240 |
|
$this->fieldNames[$joinColumn->getColumnName()] = $fieldName; |
675
|
|
|
} |
676
|
|
|
|
677
|
240 |
|
if ($uniqueConstraintColumns) { |
678
|
2 |
|
if (! $this->table) { |
679
|
|
|
throw new \RuntimeException( |
680
|
|
|
'ClassMetadata::setTable() has to be called before defining a one to one relationship.' |
681
|
|
|
); |
682
|
|
|
} |
683
|
|
|
|
684
|
2 |
|
$this->table->addUniqueConstraint( |
685
|
|
|
[ |
686
|
2 |
|
'name' => sprintf('%s_uniq', $fieldName), |
687
|
2 |
|
'columns' => $uniqueConstraintColumns, |
688
|
|
|
'options' => [], |
689
|
|
|
'flags' => [], |
690
|
|
|
] |
691
|
|
|
); |
692
|
|
|
} |
693
|
|
|
} |
694
|
|
|
|
695
|
244 |
|
if ($property->isOrphanRemoval()) { |
696
|
7 |
|
$cascades = $property->getCascade(); |
697
|
|
|
|
698
|
7 |
|
if (! in_array('remove', $cascades, true)) { |
699
|
6 |
|
$cascades[] = 'remove'; |
700
|
|
|
|
701
|
6 |
|
$property->setCascade($cascades); |
702
|
|
|
} |
703
|
|
|
|
704
|
|
|
// @todo guilhermeblanco where is this used? |
705
|
|
|
// @todo guilhermeblanco Shouldn't we iterate through JoinColumns to set non-uniqueness? |
706
|
|
|
//$property->setUnique(false); |
|
|
|
|
707
|
|
|
} |
708
|
|
|
|
709
|
244 |
|
if ($property->isPrimaryKey() && ! $property->isOwningSide()) { |
710
|
1 |
|
throw MappingException::illegalInverseIdentifierAssociation($this->className, $fieldName); |
711
|
|
|
} |
712
|
243 |
|
} |
713
|
|
|
|
714
|
|
|
/** |
715
|
|
|
* Validates & completes a to-many association mapping. |
716
|
|
|
* |
717
|
|
|
* @param ToManyAssociationMetadata $property The association mapping to validate & complete. |
718
|
|
|
* |
719
|
|
|
* @throws MappingException |
720
|
|
|
*/ |
721
|
170 |
|
protected function validateAndCompleteToManyAssociationMetadata(ToManyAssociationMetadata $property) |
|
|
|
|
722
|
|
|
{ |
723
|
|
|
// Do nothing |
724
|
170 |
|
} |
725
|
|
|
|
726
|
|
|
/** |
727
|
|
|
* Validates & completes a one-to-one association mapping. |
728
|
|
|
* |
729
|
|
|
* @param OneToOneAssociationMetadata $property The association mapping to validate & complete. |
730
|
|
|
*/ |
731
|
126 |
|
protected function validateAndCompleteOneToOneMapping(OneToOneAssociationMetadata $property) |
|
|
|
|
732
|
|
|
{ |
733
|
|
|
// Do nothing |
734
|
126 |
|
} |
735
|
|
|
|
736
|
|
|
/** |
737
|
|
|
* Validates & completes a many-to-one association mapping. |
738
|
|
|
* |
739
|
|
|
* @param ManyToOneAssociationMetadata $property The association mapping to validate & complete. |
740
|
|
|
* |
741
|
|
|
* @throws MappingException |
742
|
|
|
*/ |
743
|
145 |
|
protected function validateAndCompleteManyToOneMapping(ManyToOneAssociationMetadata $property) |
744
|
|
|
{ |
745
|
|
|
// A many-to-one mapping is essentially a one-one backreference |
746
|
145 |
|
if ($property->isOrphanRemoval()) { |
747
|
|
|
throw MappingException::illegalOrphanRemoval($this->className, $property->getName()); |
748
|
|
|
} |
749
|
145 |
|
} |
750
|
|
|
|
751
|
|
|
/** |
752
|
|
|
* Validates & completes a one-to-many association mapping. |
753
|
|
|
* |
754
|
|
|
* @param OneToManyAssociationMetadata $property The association mapping to validate & complete. |
755
|
|
|
* |
756
|
|
|
* @throws MappingException |
757
|
|
|
*/ |
758
|
114 |
|
protected function validateAndCompleteOneToManyMapping(OneToManyAssociationMetadata $property) |
759
|
|
|
{ |
760
|
|
|
// OneToMany MUST have mappedBy |
761
|
114 |
|
if (! $property->getMappedBy()) { |
762
|
|
|
throw MappingException::oneToManyRequiresMappedBy($property->getName()); |
763
|
|
|
} |
764
|
|
|
|
765
|
114 |
|
if ($property->isOrphanRemoval()) { |
766
|
19 |
|
$cascades = $property->getCascade(); |
767
|
|
|
|
768
|
19 |
|
if (! in_array('remove', $cascades, true)) { |
769
|
16 |
|
$cascades[] = 'remove'; |
770
|
|
|
|
771
|
16 |
|
$property->setCascade($cascades); |
772
|
|
|
} |
773
|
|
|
} |
774
|
114 |
|
} |
775
|
|
|
|
776
|
|
|
/** |
777
|
|
|
* Validates & completes a many-to-many association mapping. |
778
|
|
|
* |
779
|
|
|
* @param ManyToManyAssociationMetadata $property The association mapping to validate & complete. |
780
|
|
|
* |
781
|
|
|
* @throws MappingException |
782
|
|
|
*/ |
783
|
109 |
|
protected function validateAndCompleteManyToManyMapping(ManyToManyAssociationMetadata $property) |
784
|
|
|
{ |
785
|
109 |
|
if ($property->isOwningSide()) { |
786
|
|
|
// owning side MUST have a join table |
787
|
97 |
|
$joinTable = $property->getJoinTable() ?: new JoinTableMetadata(); |
788
|
|
|
|
789
|
97 |
|
$property->setJoinTable($joinTable); |
790
|
|
|
|
791
|
97 |
|
if (! $joinTable->getName()) { |
792
|
18 |
|
$joinTableName = $this->namingStrategy->joinTableName( |
793
|
18 |
|
$property->getSourceEntity(), |
794
|
18 |
|
$property->getTargetEntity(), |
795
|
18 |
|
$property->getName() |
796
|
|
|
); |
797
|
|
|
|
798
|
18 |
|
$joinTable->setName($joinTableName); |
799
|
|
|
} |
800
|
|
|
|
801
|
97 |
|
$selfReferencingEntityWithoutJoinColumns = $property->getSourceEntity() === $property->getTargetEntity() && ! $joinTable->hasColumns(); |
802
|
|
|
|
803
|
97 |
|
if (! $joinTable->getJoinColumns()) { |
804
|
16 |
|
$referencedColumnName = $this->namingStrategy->referenceColumnName(); |
805
|
16 |
|
$sourceReferenceName = $selfReferencingEntityWithoutJoinColumns ? 'source' : $referencedColumnName; |
806
|
16 |
|
$columnName = $this->namingStrategy->joinKeyColumnName($property->getSourceEntity(), $sourceReferenceName); |
807
|
16 |
|
$joinColumn = new JoinColumnMetadata(); |
808
|
|
|
|
809
|
16 |
|
$joinColumn->setColumnName($columnName); |
810
|
16 |
|
$joinColumn->setReferencedColumnName($referencedColumnName); |
811
|
16 |
|
$joinColumn->setOnDelete('CASCADE'); |
812
|
|
|
|
813
|
16 |
|
$joinTable->addJoinColumn($joinColumn); |
814
|
|
|
} |
815
|
|
|
|
816
|
97 |
|
if (! $joinTable->getInverseJoinColumns()) { |
817
|
16 |
|
$referencedColumnName = $this->namingStrategy->referenceColumnName(); |
818
|
16 |
|
$targetReferenceName = $selfReferencingEntityWithoutJoinColumns ? 'target' : $referencedColumnName; |
819
|
16 |
|
$columnName = $this->namingStrategy->joinKeyColumnName($property->getTargetEntity(), $targetReferenceName); |
820
|
16 |
|
$joinColumn = new JoinColumnMetadata(); |
821
|
|
|
|
822
|
16 |
|
$joinColumn->setColumnName($columnName); |
823
|
16 |
|
$joinColumn->setReferencedColumnName($referencedColumnName); |
824
|
16 |
|
$joinColumn->setOnDelete('CASCADE'); |
825
|
|
|
|
826
|
16 |
|
$joinTable->addInverseJoinColumn($joinColumn); |
827
|
|
|
} |
828
|
|
|
|
829
|
97 |
|
foreach ($joinTable->getJoinColumns() as $joinColumn) { |
830
|
|
|
/** @var JoinColumnMetadata $joinColumn */ |
831
|
97 |
|
if (! $joinColumn->getReferencedColumnName()) { |
832
|
2 |
|
$joinColumn->setReferencedColumnName($this->namingStrategy->referenceColumnName()); |
833
|
|
|
} |
834
|
|
|
|
835
|
97 |
|
$referencedColumnName = $joinColumn->getReferencedColumnName(); |
836
|
|
|
|
837
|
97 |
|
if (! $joinColumn->getColumnName()) { |
838
|
2 |
|
$columnName = $this->namingStrategy->joinKeyColumnName( |
839
|
2 |
|
$property->getSourceEntity(), |
840
|
2 |
|
$referencedColumnName |
841
|
|
|
); |
842
|
|
|
|
843
|
97 |
|
$joinColumn->setColumnName($columnName); |
844
|
|
|
} |
845
|
|
|
} |
846
|
|
|
|
847
|
97 |
|
foreach ($joinTable->getInverseJoinColumns() as $inverseJoinColumn) { |
848
|
|
|
/** @var JoinColumnMetadata $inverseJoinColumn */ |
849
|
97 |
|
if (! $inverseJoinColumn->getReferencedColumnName()) { |
850
|
2 |
|
$inverseJoinColumn->setReferencedColumnName($this->namingStrategy->referenceColumnName()); |
851
|
|
|
} |
852
|
|
|
|
853
|
97 |
|
$referencedColumnName = $inverseJoinColumn->getReferencedColumnName(); |
854
|
|
|
|
855
|
97 |
|
if (! $inverseJoinColumn->getColumnName()) { |
856
|
2 |
|
$columnName = $this->namingStrategy->joinKeyColumnName( |
857
|
2 |
|
$property->getTargetEntity(), |
858
|
2 |
|
$referencedColumnName |
859
|
|
|
); |
860
|
|
|
|
861
|
97 |
|
$inverseJoinColumn->setColumnName($columnName); |
862
|
|
|
} |
863
|
|
|
} |
864
|
|
|
} |
865
|
109 |
|
} |
866
|
|
|
|
867
|
|
|
/** |
868
|
|
|
* {@inheritDoc} |
869
|
|
|
*/ |
870
|
394 |
|
public function getIdentifierFieldNames() |
871
|
|
|
{ |
872
|
394 |
|
return $this->identifier; |
873
|
|
|
} |
874
|
|
|
|
875
|
|
|
/** |
876
|
|
|
* Gets the name of the single id field. Note that this only works on |
877
|
|
|
* entity classes that have a single-field pk. |
878
|
|
|
* |
879
|
|
|
* @return string |
880
|
|
|
* |
881
|
|
|
* @throws MappingException If the class has a composite primary key. |
882
|
|
|
*/ |
883
|
149 |
|
public function getSingleIdentifierFieldName() |
884
|
|
|
{ |
885
|
149 |
|
if ($this->isIdentifierComposite()) { |
886
|
1 |
|
throw MappingException::singleIdNotAllowedOnCompositePrimaryKey($this->className); |
887
|
|
|
} |
888
|
|
|
|
889
|
148 |
|
if (! isset($this->identifier[0])) { |
890
|
1 |
|
throw MappingException::noIdDefined($this->className); |
891
|
|
|
} |
892
|
|
|
|
893
|
147 |
|
return $this->identifier[0]; |
894
|
|
|
} |
895
|
|
|
|
896
|
|
|
/** |
897
|
|
|
* INTERNAL: |
898
|
|
|
* Sets the mapped identifier/primary key fields of this class. |
899
|
|
|
* Mainly used by the ClassMetadataFactory to assign inherited identifiers. |
900
|
|
|
* |
901
|
|
|
* @param mixed[] $identifier |
902
|
|
|
*/ |
903
|
100 |
|
public function setIdentifier(array $identifier) |
904
|
|
|
{ |
905
|
100 |
|
$this->identifier = $identifier; |
906
|
100 |
|
} |
907
|
|
|
|
908
|
|
|
/** |
909
|
|
|
* {@inheritDoc} |
910
|
|
|
*/ |
911
|
1040 |
|
public function getIdentifier() |
912
|
|
|
{ |
913
|
1040 |
|
return $this->identifier; |
914
|
|
|
} |
915
|
|
|
|
916
|
|
|
/** |
917
|
|
|
* {@inheritDoc} |
918
|
|
|
*/ |
919
|
184 |
|
public function hasField($fieldName) |
920
|
|
|
{ |
921
|
184 |
|
return isset($this->declaredProperties[$fieldName]) |
922
|
184 |
|
&& $this->declaredProperties[$fieldName] instanceof FieldMetadata; |
923
|
|
|
} |
924
|
|
|
|
925
|
|
|
/** |
926
|
|
|
* Returns an array with identifier column names and their corresponding ColumnMetadata. |
927
|
|
|
* |
928
|
|
|
* @return ColumnMetadata[] |
929
|
|
|
*/ |
930
|
439 |
|
public function getIdentifierColumns(EntityManagerInterface $em) : array |
931
|
|
|
{ |
932
|
439 |
|
$columns = []; |
933
|
|
|
|
934
|
439 |
|
foreach ($this->identifier as $idProperty) { |
935
|
439 |
|
$property = $this->getProperty($idProperty); |
936
|
|
|
|
937
|
439 |
|
if ($property instanceof FieldMetadata) { |
938
|
434 |
|
$columns[$property->getColumnName()] = $property; |
939
|
|
|
|
940
|
434 |
|
continue; |
941
|
|
|
} |
942
|
|
|
|
943
|
|
|
/** @var AssociationMetadata $property */ |
944
|
|
|
|
945
|
|
|
// Association defined as Id field |
946
|
24 |
|
$targetClass = $em->getClassMetadata($property->getTargetEntity()); |
947
|
|
|
|
948
|
24 |
|
if (! $property->isOwningSide()) { |
949
|
|
|
$property = $targetClass->getProperty($property->getMappedBy()); |
|
|
|
|
950
|
|
|
$targetClass = $em->getClassMetadata($property->getTargetEntity()); |
951
|
|
|
} |
952
|
|
|
|
953
|
24 |
|
$joinColumns = $property instanceof ManyToManyAssociationMetadata |
954
|
|
|
? $property->getJoinTable()->getInverseJoinColumns() |
955
|
24 |
|
: $property->getJoinColumns() |
956
|
|
|
; |
957
|
|
|
|
958
|
24 |
|
foreach ($joinColumns as $joinColumn) { |
959
|
|
|
/** @var JoinColumnMetadata $joinColumn */ |
960
|
24 |
|
$columnName = $joinColumn->getColumnName(); |
961
|
24 |
|
$referencedColumnName = $joinColumn->getReferencedColumnName(); |
962
|
|
|
|
963
|
24 |
|
if (! $joinColumn->getType()) { |
964
|
12 |
|
$joinColumn->setType(PersisterHelper::getTypeOfColumn($referencedColumnName, $targetClass, $em)); |
|
|
|
|
965
|
|
|
} |
966
|
|
|
|
967
|
24 |
|
$columns[$columnName] = $joinColumn; |
968
|
|
|
} |
969
|
|
|
} |
970
|
|
|
|
971
|
439 |
|
return $columns; |
972
|
|
|
} |
973
|
|
|
|
974
|
|
|
/** |
975
|
|
|
* Gets the name of the primary table. |
976
|
|
|
*/ |
977
|
1578 |
|
public function getTableName() : ?string |
978
|
|
|
{ |
979
|
1578 |
|
return $this->table->getName(); |
980
|
|
|
} |
981
|
|
|
|
982
|
|
|
/** |
983
|
|
|
* Gets primary table's schema name. |
984
|
|
|
*/ |
985
|
14 |
|
public function getSchemaName() : ?string |
986
|
|
|
{ |
987
|
14 |
|
return $this->table->getSchema(); |
988
|
|
|
} |
989
|
|
|
|
990
|
|
|
/** |
991
|
|
|
* Gets the table name to use for temporary identifier tables of this class. |
992
|
|
|
*/ |
993
|
7 |
|
public function getTemporaryIdTableName() : string |
994
|
|
|
{ |
995
|
7 |
|
$schema = $this->getSchemaName() === null |
996
|
6 |
|
? '' |
997
|
7 |
|
: $this->getSchemaName() . '_' |
998
|
|
|
; |
999
|
|
|
|
1000
|
|
|
// replace dots with underscores because PostgreSQL creates temporary tables in a special schema |
1001
|
7 |
|
return $schema . $this->getTableName() . '_id_tmp'; |
1002
|
|
|
} |
1003
|
|
|
|
1004
|
|
|
/** |
1005
|
|
|
* Sets the mapped subclasses of this class. |
1006
|
|
|
* |
1007
|
|
|
* @todo guilhermeblanco Only used for ClassMetadataTest. Remove if possible! |
1008
|
|
|
* |
1009
|
|
|
* @param string[] $subclasses The names of all mapped subclasses. |
1010
|
|
|
*/ |
1011
|
4 |
|
public function setSubclasses(array $subclasses) : void |
1012
|
|
|
{ |
1013
|
4 |
|
foreach ($subclasses as $subclass) { |
1014
|
3 |
|
$this->subClasses[] = $subclass; |
1015
|
|
|
} |
1016
|
4 |
|
} |
1017
|
|
|
|
1018
|
|
|
/** |
1019
|
|
|
* @return string[] |
1020
|
|
|
*/ |
1021
|
1071 |
|
public function getSubClasses() : array |
1022
|
|
|
{ |
1023
|
1071 |
|
return $this->subClasses; |
1024
|
|
|
} |
1025
|
|
|
|
1026
|
|
|
/** |
1027
|
|
|
* Sets the inheritance type used by the class and its subclasses. |
1028
|
|
|
* |
1029
|
|
|
* @param int $type |
1030
|
|
|
* |
1031
|
|
|
* @throws MappingException |
1032
|
|
|
*/ |
1033
|
118 |
|
public function setInheritanceType($type) : void |
1034
|
|
|
{ |
1035
|
118 |
|
if (! $this->isInheritanceType($type)) { |
1036
|
|
|
throw MappingException::invalidInheritanceType($this->className, $type); |
1037
|
|
|
} |
1038
|
|
|
|
1039
|
118 |
|
$this->inheritanceType = $type; |
1040
|
118 |
|
} |
1041
|
|
|
|
1042
|
|
|
/** |
1043
|
|
|
* Sets the override property mapping for an entity relationship. |
1044
|
|
|
* |
1045
|
|
|
* @throws \RuntimeException |
1046
|
|
|
* @throws MappingException |
1047
|
|
|
* @throws CacheException |
1048
|
|
|
*/ |
1049
|
12 |
|
public function setPropertyOverride(Property $property) : void |
1050
|
|
|
{ |
1051
|
12 |
|
$fieldName = $property->getName(); |
1052
|
|
|
|
1053
|
12 |
|
if (! isset($this->declaredProperties[$fieldName])) { |
1054
|
2 |
|
throw MappingException::invalidOverrideFieldName($this->className, $fieldName); |
1055
|
|
|
} |
1056
|
|
|
|
1057
|
10 |
|
$originalProperty = $this->getProperty($fieldName); |
1058
|
10 |
|
$originalPropertyClassName = get_class($originalProperty); |
1059
|
|
|
|
1060
|
|
|
// If moving from transient to persistent, assume it's a new property |
1061
|
10 |
|
if ($originalPropertyClassName === TransientMetadata::class) { |
1062
|
1 |
|
unset($this->declaredProperties[$fieldName]); |
1063
|
|
|
|
1064
|
1 |
|
$this->addProperty($property); |
1065
|
|
|
|
1066
|
1 |
|
return; |
1067
|
|
|
} |
1068
|
|
|
|
1069
|
|
|
// Do not allow to change property type |
1070
|
9 |
|
if ($originalPropertyClassName !== get_class($property)) { |
1071
|
|
|
throw MappingException::invalidOverridePropertyType($this->className, $fieldName); |
1072
|
|
|
} |
1073
|
|
|
|
1074
|
|
|
// Do not allow to change version property |
1075
|
9 |
|
if ($originalProperty instanceof VersionFieldMetadata) { |
1076
|
|
|
throw MappingException::invalidOverrideVersionField($this->className, $fieldName); |
1077
|
|
|
} |
1078
|
|
|
|
1079
|
9 |
|
unset($this->declaredProperties[$fieldName]); |
1080
|
|
|
|
1081
|
9 |
|
if ($property instanceof FieldMetadata) { |
1082
|
|
|
// Unset defined fieldName prior to override |
1083
|
5 |
|
unset($this->fieldNames[$originalProperty->getColumnName()]); |
|
|
|
|
1084
|
|
|
|
1085
|
|
|
// Revert what should not be allowed to change |
1086
|
5 |
|
$property->setDeclaringClass($originalProperty->getDeclaringClass()); |
1087
|
5 |
|
$property->setPrimaryKey($originalProperty->isPrimaryKey()); |
1088
|
9 |
|
} elseif ($property instanceof AssociationMetadata) { |
1089
|
|
|
// Unset all defined fieldNames prior to override |
1090
|
9 |
|
if ($originalProperty instanceof ToOneAssociationMetadata && $originalProperty->isOwningSide()) { |
1091
|
5 |
|
foreach ($originalProperty->getJoinColumns() as $joinColumn) { |
1092
|
5 |
|
unset($this->fieldNames[$joinColumn->getColumnName()]); |
1093
|
|
|
} |
1094
|
|
|
} |
1095
|
|
|
|
1096
|
|
|
// Override what it should be allowed to change |
1097
|
9 |
|
if ($property->getInversedBy()) { |
1098
|
2 |
|
$originalProperty->setInversedBy($property->getInversedBy()); |
|
|
|
|
1099
|
|
|
} |
1100
|
|
|
|
1101
|
9 |
|
if ($property->getFetchMode() !== $originalProperty->getFetchMode()) { |
|
|
|
|
1102
|
2 |
|
$originalProperty->setFetchMode($property->getFetchMode()); |
|
|
|
|
1103
|
|
|
} |
1104
|
|
|
|
1105
|
9 |
|
if ($originalProperty instanceof ToOneAssociationMetadata && $property->getJoinColumns()) { |
|
|
|
|
1106
|
5 |
|
$originalProperty->setJoinColumns($property->getJoinColumns()); |
1107
|
8 |
|
} elseif ($originalProperty instanceof ManyToManyAssociationMetadata && $property->getJoinTable()) { |
|
|
|
|
1108
|
4 |
|
$originalProperty->setJoinTable($property->getJoinTable()); |
1109
|
|
|
} |
1110
|
|
|
|
1111
|
9 |
|
$property = $originalProperty; |
1112
|
|
|
} |
1113
|
|
|
|
1114
|
9 |
|
$this->addProperty($property); |
|
|
|
|
1115
|
9 |
|
} |
1116
|
|
|
|
1117
|
|
|
/** |
1118
|
|
|
* Checks if this entity is the root in any entity-inheritance-hierarchy. |
1119
|
|
|
* |
1120
|
|
|
* @return bool |
1121
|
|
|
*/ |
1122
|
337 |
|
public function isRootEntity() |
1123
|
|
|
{ |
1124
|
337 |
|
return $this->className === $this->getRootClassName(); |
1125
|
|
|
} |
1126
|
|
|
|
1127
|
|
|
/** |
1128
|
|
|
* Checks whether a mapped field is inherited from a superclass. |
1129
|
|
|
* |
1130
|
|
|
* @param string $fieldName |
1131
|
|
|
* |
1132
|
|
|
* @return bool TRUE if the field is inherited, FALSE otherwise. |
1133
|
|
|
*/ |
1134
|
618 |
|
public function isInheritedProperty($fieldName) |
1135
|
|
|
{ |
1136
|
618 |
|
$declaringClass = $this->declaredProperties[$fieldName]->getDeclaringClass(); |
1137
|
|
|
|
1138
|
618 |
|
return ! ($declaringClass->className === $this->className); |
1139
|
|
|
} |
1140
|
|
|
|
1141
|
|
|
/** |
1142
|
|
|
* {@inheritdoc} |
1143
|
|
|
*/ |
1144
|
438 |
|
public function setTable(TableMetadata $table) : void |
1145
|
|
|
{ |
1146
|
438 |
|
$this->table = $table; |
1147
|
|
|
|
1148
|
438 |
|
if (empty($table->getName())) { |
1149
|
|
|
$table->setName($this->namingStrategy->classToTableName($this->className)); |
1150
|
|
|
} |
1151
|
438 |
|
} |
1152
|
|
|
|
1153
|
|
|
/** |
1154
|
|
|
* Checks whether the given type identifies an inheritance type. |
1155
|
|
|
* |
1156
|
|
|
* @param int $type |
1157
|
|
|
* |
1158
|
|
|
* @return bool TRUE if the given type identifies an inheritance type, FALSe otherwise. |
1159
|
|
|
*/ |
1160
|
118 |
|
private function isInheritanceType($type) |
1161
|
|
|
{ |
1162
|
118 |
|
return $type === InheritanceType::NONE |
1163
|
92 |
|
|| $type === InheritanceType::SINGLE_TABLE |
1164
|
50 |
|
|| $type === InheritanceType::JOINED |
1165
|
118 |
|
|| $type === InheritanceType::TABLE_PER_CLASS; |
1166
|
|
|
} |
1167
|
|
|
|
1168
|
905 |
|
public function getColumn(string $columnName) : ?LocalColumnMetadata |
1169
|
|
|
{ |
1170
|
905 |
|
foreach ($this->declaredProperties as $property) { |
1171
|
905 |
|
if ($property instanceof LocalColumnMetadata && $property->getColumnName() === $columnName) { |
1172
|
905 |
|
return $property; |
1173
|
|
|
} |
1174
|
|
|
} |
1175
|
|
|
|
1176
|
|
|
return null; |
1177
|
|
|
} |
1178
|
|
|
|
1179
|
|
|
/** |
1180
|
|
|
* Add a property mapping. |
1181
|
|
|
* |
1182
|
|
|
* @throws \RuntimeException |
1183
|
|
|
* @throws MappingException |
1184
|
|
|
* @throws CacheException |
1185
|
|
|
*/ |
1186
|
428 |
|
public function addProperty(Property $property) |
1187
|
|
|
{ |
1188
|
428 |
|
$fieldName = $property->getName(); |
1189
|
|
|
|
1190
|
|
|
// Check for empty field name |
1191
|
428 |
|
if (empty($fieldName)) { |
1192
|
1 |
|
throw MappingException::missingFieldName($this->className); |
1193
|
|
|
} |
1194
|
|
|
|
1195
|
427 |
|
$property->setDeclaringClass($this); |
1196
|
|
|
|
1197
|
|
|
switch (true) { |
1198
|
427 |
|
case ($property instanceof VersionFieldMetadata): |
|
|
|
|
1199
|
20 |
|
$this->validateAndCompleteFieldMapping($property); |
1200
|
20 |
|
$this->validateAndCompleteVersionFieldMapping($property); |
1201
|
19 |
|
break; |
1202
|
|
|
|
1203
|
426 |
|
case ($property instanceof FieldMetadata): |
1204
|
403 |
|
$this->validateAndCompleteFieldMapping($property); |
1205
|
402 |
|
break; |
1206
|
|
|
|
1207
|
287 |
|
case ($property instanceof OneToOneAssociationMetadata): |
1208
|
128 |
|
$this->validateAndCompleteAssociationMapping($property); |
1209
|
127 |
|
$this->validateAndCompleteToOneAssociationMetadata($property); |
1210
|
126 |
|
$this->validateAndCompleteOneToOneMapping($property); |
1211
|
126 |
|
break; |
1212
|
|
|
|
1213
|
224 |
|
case ($property instanceof OneToManyAssociationMetadata): |
1214
|
114 |
|
$this->validateAndCompleteAssociationMapping($property); |
1215
|
114 |
|
$this->validateAndCompleteToManyAssociationMetadata($property); |
1216
|
114 |
|
$this->validateAndCompleteOneToManyMapping($property); |
1217
|
114 |
|
break; |
1218
|
|
|
|
1219
|
220 |
|
case ($property instanceof ManyToOneAssociationMetadata): |
1220
|
148 |
|
$this->validateAndCompleteAssociationMapping($property); |
1221
|
145 |
|
$this->validateAndCompleteToOneAssociationMetadata($property); |
1222
|
145 |
|
$this->validateAndCompleteManyToOneMapping($property); |
1223
|
145 |
|
break; |
1224
|
|
|
|
1225
|
126 |
|
case ($property instanceof ManyToManyAssociationMetadata): |
1226
|
110 |
|
$this->validateAndCompleteAssociationMapping($property); |
1227
|
109 |
|
$this->validateAndCompleteToManyAssociationMetadata($property); |
1228
|
109 |
|
$this->validateAndCompleteManyToManyMapping($property); |
1229
|
109 |
|
break; |
1230
|
|
|
|
1231
|
|
|
default: |
1232
|
|
|
// Transient properties are ignored on purpose here! =) |
1233
|
30 |
|
break; |
1234
|
|
|
} |
1235
|
|
|
|
1236
|
419 |
|
$this->addDeclaredProperty($property); |
1237
|
419 |
|
} |
1238
|
|
|
|
1239
|
|
|
/** |
1240
|
|
|
* INTERNAL: |
1241
|
|
|
* Adds a property mapping without completing/validating it. |
1242
|
|
|
* This is mainly used to add inherited property mappings to derived classes. |
1243
|
|
|
*/ |
1244
|
98 |
|
public function addInheritedProperty(Property $property) |
1245
|
|
|
{ |
1246
|
98 |
|
$inheritedProperty = clone $property; |
1247
|
98 |
|
$declaringClass = $property->getDeclaringClass(); |
1248
|
|
|
|
1249
|
98 |
|
if ($inheritedProperty instanceof FieldMetadata) { |
1250
|
97 |
|
if (! $declaringClass->isMappedSuperclass) { |
1251
|
75 |
|
$inheritedProperty->setTableName($property->getTableName()); |
|
|
|
|
1252
|
|
|
} |
1253
|
|
|
|
1254
|
97 |
|
$this->fieldNames[$property->getColumnName()] = $property->getName(); |
1255
|
43 |
|
} elseif ($inheritedProperty instanceof AssociationMetadata) { |
1256
|
42 |
|
if ($declaringClass->isMappedSuperclass) { |
1257
|
10 |
|
$inheritedProperty->setSourceEntity($this->className); |
1258
|
|
|
} |
1259
|
|
|
|
1260
|
|
|
// Need to add inherited fieldNames |
1261
|
42 |
|
if ($inheritedProperty instanceof ToOneAssociationMetadata && $inheritedProperty->isOwningSide()) { |
1262
|
35 |
|
foreach ($inheritedProperty->getJoinColumns() as $joinColumn) { |
1263
|
|
|
/** @var JoinColumnMetadata $joinColumn */ |
1264
|
34 |
|
$this->fieldNames[$joinColumn->getColumnName()] = $property->getName(); |
1265
|
|
|
} |
1266
|
|
|
} |
1267
|
|
|
} |
1268
|
|
|
|
1269
|
98 |
|
if (isset($this->declaredProperties[$property->getName()])) { |
1270
|
1 |
|
throw MappingException::duplicateProperty($this->className, $this->getProperty($property->getName())); |
1271
|
|
|
} |
1272
|
|
|
|
1273
|
98 |
|
$this->declaredProperties[$property->getName()] = $inheritedProperty; |
1274
|
|
|
|
1275
|
98 |
|
if ($inheritedProperty instanceof VersionFieldMetadata) { |
1276
|
4 |
|
$this->versionProperty = $inheritedProperty; |
1277
|
|
|
} |
1278
|
98 |
|
} |
1279
|
|
|
|
1280
|
|
|
/** |
1281
|
|
|
* INTERNAL: |
1282
|
|
|
* Adds a sql result set mapping to this class. |
1283
|
|
|
* |
1284
|
|
|
* @param mixed[] $resultMapping |
1285
|
|
|
* |
1286
|
|
|
* @throws MappingException |
1287
|
|
|
*/ |
1288
|
|
|
public function addSqlResultSetMapping(array $resultMapping) |
1289
|
|
|
{ |
1290
|
|
|
if (! isset($resultMapping['name'])) { |
1291
|
|
|
throw MappingException::nameIsMandatoryForSqlResultSetMapping($this->className); |
1292
|
|
|
} |
1293
|
|
|
|
1294
|
|
|
if (isset($this->sqlResultSetMappings[$resultMapping['name']])) { |
1295
|
|
|
throw MappingException::duplicateResultSetMapping($this->className, $resultMapping['name']); |
1296
|
|
|
} |
1297
|
|
|
|
1298
|
|
|
if (isset($resultMapping['entities'])) { |
1299
|
|
|
foreach ($resultMapping['entities'] as $key => $entityResult) { |
1300
|
|
|
if (! isset($entityResult['entityClass'])) { |
1301
|
|
|
throw MappingException::missingResultSetMappingEntity($this->className, $resultMapping['name']); |
1302
|
|
|
} |
1303
|
|
|
|
1304
|
|
|
$entityClassName = $entityResult['entityClass']; |
1305
|
|
|
$resultMapping['entities'][$key]['entityClass'] = $entityClassName; |
1306
|
|
|
|
1307
|
|
|
if (isset($entityResult['fields'])) { |
1308
|
|
|
foreach ($entityResult['fields'] as $k => $field) { |
1309
|
|
|
if (! isset($field['name'])) { |
1310
|
|
|
throw MappingException::missingResultSetMappingFieldName($this->className, $resultMapping['name']); |
1311
|
|
|
} |
1312
|
|
|
|
1313
|
|
|
if (! isset($field['column'])) { |
1314
|
|
|
$fieldName = $field['name']; |
1315
|
|
|
|
1316
|
|
|
if (strpos($fieldName, '.')) { |
1317
|
|
|
list(, $fieldName) = explode('.', $fieldName); |
1318
|
|
|
} |
1319
|
|
|
|
1320
|
|
|
$resultMapping['entities'][$key]['fields'][$k]['column'] = $fieldName; |
1321
|
|
|
} |
1322
|
|
|
} |
1323
|
|
|
} |
1324
|
|
|
} |
1325
|
|
|
} |
1326
|
|
|
|
1327
|
|
|
$this->sqlResultSetMappings[$resultMapping['name']] = $resultMapping; |
|
|
|
|
1328
|
|
|
} |
1329
|
|
|
|
1330
|
|
|
/** |
1331
|
|
|
* Registers a custom repository class for the entity class. |
1332
|
|
|
* |
1333
|
|
|
* @param string|null $repositoryClassName The class name of the custom mapper. |
1334
|
|
|
*/ |
1335
|
31 |
|
public function setCustomRepositoryClassName(?string $repositoryClassName) |
1336
|
|
|
{ |
1337
|
31 |
|
$this->customRepositoryClassName = $repositoryClassName; |
1338
|
31 |
|
} |
1339
|
|
|
|
1340
|
164 |
|
public function getCustomRepositoryClassName() : ?string |
1341
|
|
|
{ |
1342
|
164 |
|
return $this->customRepositoryClassName; |
1343
|
|
|
} |
1344
|
|
|
|
1345
|
|
|
/** |
1346
|
|
|
* Whether the class has any attached lifecycle listeners or callbacks for a lifecycle event. |
1347
|
|
|
* |
1348
|
|
|
* @param string $lifecycleEvent |
1349
|
|
|
* |
1350
|
|
|
* @return bool |
1351
|
|
|
*/ |
1352
|
|
|
public function hasLifecycleCallbacks($lifecycleEvent) |
1353
|
|
|
{ |
1354
|
|
|
return isset($this->lifecycleCallbacks[$lifecycleEvent]); |
1355
|
|
|
} |
1356
|
|
|
|
1357
|
|
|
/** |
1358
|
|
|
* Gets the registered lifecycle callbacks for an event. |
1359
|
|
|
* |
1360
|
|
|
* @param string $event |
1361
|
|
|
* |
1362
|
|
|
* @return string[] |
1363
|
|
|
*/ |
1364
|
|
|
public function getLifecycleCallbacks($event) |
1365
|
|
|
{ |
1366
|
|
|
return $this->lifecycleCallbacks[$event] ?? []; |
1367
|
|
|
} |
1368
|
|
|
|
1369
|
|
|
/** |
1370
|
|
|
* Adds a lifecycle callback for entities of this class. |
1371
|
|
|
* |
1372
|
|
|
* @param string $callback |
1373
|
|
|
* @param string $event |
1374
|
|
|
*/ |
1375
|
17 |
|
public function addLifecycleCallback($callback, $event) |
1376
|
|
|
{ |
1377
|
17 |
|
if (isset($this->lifecycleCallbacks[$event]) && in_array($callback, $this->lifecycleCallbacks[$event], true)) { |
1378
|
3 |
|
return; |
1379
|
|
|
} |
1380
|
|
|
|
1381
|
17 |
|
$this->lifecycleCallbacks[$event][] = $callback; |
1382
|
17 |
|
} |
1383
|
|
|
|
1384
|
|
|
/** |
1385
|
|
|
* Sets the lifecycle callbacks for entities of this class. |
1386
|
|
|
* Any previously registered callbacks are overwritten. |
1387
|
|
|
* |
1388
|
|
|
* @param string[][] $callbacks |
1389
|
|
|
*/ |
1390
|
98 |
|
public function setLifecycleCallbacks(array $callbacks) : void |
1391
|
|
|
{ |
1392
|
98 |
|
$this->lifecycleCallbacks = $callbacks; |
1393
|
98 |
|
} |
1394
|
|
|
|
1395
|
|
|
/** |
1396
|
|
|
* Adds a entity listener for entities of this class. |
1397
|
|
|
* |
1398
|
|
|
* @param string $eventName The entity lifecycle event. |
1399
|
|
|
* @param string $class The listener class. |
1400
|
|
|
* @param string $method The listener callback method. |
1401
|
|
|
* |
1402
|
|
|
* @throws MappingException |
1403
|
|
|
*/ |
1404
|
17 |
|
public function addEntityListener(string $eventName, string $class, string $method) : void |
1405
|
|
|
{ |
1406
|
|
|
$listener = [ |
1407
|
17 |
|
'class' => $class, |
1408
|
17 |
|
'method' => $method, |
1409
|
|
|
]; |
1410
|
|
|
|
1411
|
17 |
|
if (! class_exists($class)) { |
1412
|
1 |
|
throw MappingException::entityListenerClassNotFound($class, $this->className); |
1413
|
|
|
} |
1414
|
|
|
|
1415
|
16 |
|
if (! method_exists($class, $method)) { |
1416
|
1 |
|
throw MappingException::entityListenerMethodNotFound($class, $method, $this->className); |
1417
|
|
|
} |
1418
|
|
|
|
1419
|
15 |
|
if (isset($this->entityListeners[$eventName]) && in_array($listener, $this->entityListeners[$eventName], true)) { |
1420
|
1 |
|
throw MappingException::duplicateEntityListener($class, $method, $this->className); |
1421
|
|
|
} |
1422
|
|
|
|
1423
|
15 |
|
$this->entityListeners[$eventName][] = $listener; |
1424
|
15 |
|
} |
1425
|
|
|
|
1426
|
|
|
/** |
1427
|
|
|
* Sets the discriminator column definition. |
1428
|
|
|
* |
1429
|
|
|
* @throws MappingException |
1430
|
|
|
* |
1431
|
|
|
* @see getDiscriminatorColumn() |
1432
|
|
|
*/ |
1433
|
94 |
|
public function setDiscriminatorColumn(DiscriminatorColumnMetadata $discriminatorColumn) : void |
1434
|
|
|
{ |
1435
|
94 |
|
if (isset($this->fieldNames[$discriminatorColumn->getColumnName()])) { |
1436
|
1 |
|
throw MappingException::duplicateColumnName($this->className, $discriminatorColumn->getColumnName()); |
1437
|
|
|
} |
1438
|
|
|
|
1439
|
93 |
|
$discriminatorColumn->setTableName($discriminatorColumn->getTableName() ?? $this->getTableName()); |
1440
|
|
|
|
1441
|
93 |
|
$allowedTypeList = ['boolean', 'array', 'object', 'datetime', 'time', 'date']; |
1442
|
|
|
|
1443
|
93 |
|
if (in_array($discriminatorColumn->getTypeName(), $allowedTypeList, true)) { |
1444
|
|
|
throw MappingException::invalidDiscriminatorColumnType($discriminatorColumn->getTypeName()); |
1445
|
|
|
} |
1446
|
|
|
|
1447
|
93 |
|
$this->discriminatorColumn = $discriminatorColumn; |
1448
|
93 |
|
} |
1449
|
|
|
|
1450
|
|
|
/** |
1451
|
|
|
* Sets the discriminator values used by this class. |
1452
|
|
|
* Used for JOINED and SINGLE_TABLE inheritance mapping strategies. |
1453
|
|
|
* |
1454
|
|
|
* @param string[] $map |
1455
|
|
|
* |
1456
|
|
|
* @throws MappingException |
1457
|
|
|
*/ |
1458
|
91 |
|
public function setDiscriminatorMap(array $map) : void |
1459
|
|
|
{ |
1460
|
91 |
|
foreach ($map as $value => $className) { |
1461
|
91 |
|
$this->addDiscriminatorMapClass($value, $className); |
1462
|
|
|
} |
1463
|
91 |
|
} |
1464
|
|
|
|
1465
|
|
|
/** |
1466
|
|
|
* Adds one entry of the discriminator map with a new class and corresponding name. |
1467
|
|
|
* |
1468
|
|
|
* @param string|int $name |
1469
|
|
|
* |
1470
|
|
|
* @throws MappingException |
1471
|
|
|
*/ |
1472
|
91 |
|
public function addDiscriminatorMapClass($name, string $className) : void |
1473
|
|
|
{ |
1474
|
91 |
|
$this->discriminatorMap[$name] = $className; |
1475
|
|
|
|
1476
|
91 |
|
if ($this->className === $className) { |
1477
|
77 |
|
$this->discriminatorValue = $name; |
1478
|
|
|
|
1479
|
77 |
|
return; |
1480
|
|
|
} |
1481
|
|
|
|
1482
|
90 |
|
if (! (class_exists($className) || interface_exists($className))) { |
1483
|
|
|
throw MappingException::invalidClassInDiscriminatorMap($className, $this->className); |
1484
|
|
|
} |
1485
|
|
|
|
1486
|
90 |
|
if (is_subclass_of($className, $this->className) && ! in_array($className, $this->subClasses, true)) { |
1487
|
85 |
|
$this->subClasses[] = $className; |
1488
|
|
|
} |
1489
|
90 |
|
} |
1490
|
|
|
|
1491
|
1019 |
|
public function getValueGenerationPlan() : ValueGenerationPlan |
1492
|
|
|
{ |
1493
|
1019 |
|
return $this->valueGenerationPlan; |
1494
|
|
|
} |
1495
|
|
|
|
1496
|
360 |
|
public function setValueGenerationPlan(ValueGenerationPlan $valueGenerationPlan) : void |
1497
|
|
|
{ |
1498
|
360 |
|
$this->valueGenerationPlan = $valueGenerationPlan; |
1499
|
360 |
|
} |
1500
|
|
|
|
1501
|
|
|
/** |
1502
|
|
|
* Checks whether the class has a named native query with the given query name. |
1503
|
|
|
* |
1504
|
|
|
* @param string $name |
1505
|
|
|
*/ |
1506
|
|
|
public function hasSqlResultSetMapping($name) : bool |
1507
|
|
|
{ |
1508
|
|
|
return isset($this->sqlResultSetMappings[$name]); |
1509
|
|
|
} |
1510
|
|
|
|
1511
|
|
|
/** |
1512
|
|
|
* Marks this class as read only, no change tracking is applied to it. |
1513
|
|
|
*/ |
1514
|
2 |
|
public function asReadOnly() : void |
1515
|
|
|
{ |
1516
|
2 |
|
$this->readOnly = true; |
1517
|
2 |
|
} |
1518
|
|
|
|
1519
|
|
|
/** |
1520
|
|
|
* Whether this class is read only or not. |
1521
|
|
|
*/ |
1522
|
441 |
|
public function isReadOnly() : bool |
1523
|
|
|
{ |
1524
|
441 |
|
return $this->readOnly; |
1525
|
|
|
} |
1526
|
|
|
|
1527
|
1079 |
|
public function isVersioned() : bool |
1528
|
|
|
{ |
1529
|
1079 |
|
return $this->versionProperty !== null; |
1530
|
|
|
} |
1531
|
|
|
|
1532
|
|
|
/** |
1533
|
|
|
* Map Embedded Class |
1534
|
|
|
* |
1535
|
|
|
* @param mixed[] $mapping |
1536
|
|
|
* |
1537
|
|
|
* @throws MappingException |
1538
|
|
|
*/ |
1539
|
|
|
public function mapEmbedded(array $mapping) : void |
|
|
|
|
1540
|
|
|
{ |
1541
|
|
|
/*if (isset($this->declaredProperties[$mapping['fieldName']])) { |
|
|
|
|
1542
|
|
|
throw MappingException::duplicateProperty($this->className, $this->getProperty($mapping['fieldName'])); |
1543
|
|
|
} |
1544
|
|
|
|
1545
|
|
|
$this->embeddedClasses[$mapping['fieldName']] = [ |
1546
|
|
|
'class' => $this->fullyQualifiedClassName($mapping['class']), |
1547
|
|
|
'columnPrefix' => $mapping['columnPrefix'], |
1548
|
|
|
'declaredField' => $mapping['declaredField'] ?? null, |
1549
|
|
|
'originalField' => $mapping['originalField'] ?? null, |
1550
|
|
|
'declaringClass' => $this, |
1551
|
|
|
];*/ |
1552
|
|
|
} |
1553
|
|
|
|
1554
|
|
|
/** |
1555
|
|
|
* Inline the embeddable class |
1556
|
|
|
* |
1557
|
|
|
* @param string $property |
1558
|
|
|
*/ |
1559
|
|
|
public function inlineEmbeddable($property, ClassMetadata $embeddable) : void |
|
|
|
|
1560
|
|
|
{ |
1561
|
|
|
/*foreach ($embeddable->fieldMappings as $fieldName => $fieldMapping) { |
|
|
|
|
1562
|
|
|
$fieldMapping['fieldName'] = $property . "." . $fieldName; |
1563
|
|
|
$fieldMapping['originalClass'] = $fieldMapping['originalClass'] ?? $embeddable->getClassName(); |
1564
|
|
|
$fieldMapping['originalField'] = $fieldMapping['originalField'] ?? $fieldName; |
1565
|
|
|
$fieldMapping['declaredField'] = isset($fieldMapping['declaredField']) |
1566
|
|
|
? $property . '.' . $fieldMapping['declaredField'] |
1567
|
|
|
: $property; |
1568
|
|
|
|
1569
|
|
|
if (! empty($this->embeddedClasses[$property]['columnPrefix'])) { |
1570
|
|
|
$fieldMapping['columnName'] = $this->embeddedClasses[$property]['columnPrefix'] . $fieldMapping['columnName']; |
1571
|
|
|
} elseif ($this->embeddedClasses[$property]['columnPrefix'] !== false) { |
1572
|
|
|
$fieldMapping['columnName'] = $this->namingStrategy->embeddedFieldToColumnName( |
1573
|
|
|
$property, |
1574
|
|
|
$fieldMapping['columnName'], |
1575
|
|
|
$this->reflectionClass->getName(), |
1576
|
|
|
$embeddable->reflectionClass->getName() |
1577
|
|
|
); |
1578
|
|
|
} |
1579
|
|
|
|
1580
|
|
|
$this->mapField($fieldMapping); |
1581
|
|
|
}*/ |
1582
|
|
|
} |
1583
|
|
|
} |
1584
|
|
|
|
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.