1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace Doctrine\ORM\Mapping\Driver; |
6
|
|
|
|
7
|
|
|
use Doctrine\Common\Annotations\AnnotationReader; |
8
|
|
|
use Doctrine\DBAL\Types\Type; |
9
|
|
|
use Doctrine\ORM\Annotation; |
10
|
|
|
use Doctrine\ORM\Events; |
11
|
|
|
use Doctrine\ORM\Mapping; |
12
|
|
|
|
13
|
|
|
/** |
14
|
|
|
* The AnnotationDriver reads the mapping metadata from docblock annotations. |
15
|
|
|
* |
16
|
|
|
* @since 2.0 |
17
|
|
|
* @author Benjamin Eberlei <[email protected]> |
18
|
|
|
* @author Guilherme Blanco <[email protected]> |
19
|
|
|
* @author Jonathan H. Wage <[email protected]> |
20
|
|
|
* @author Roman Borschel <[email protected]> |
21
|
|
|
*/ |
22
|
|
|
class AnnotationDriver implements MappingDriver |
23
|
|
|
{ |
24
|
|
|
/** |
25
|
|
|
* {@inheritDoc} |
26
|
|
|
*/ |
27
|
|
|
protected $entityAnnotationClasses = [ |
28
|
|
|
Annotation\Entity::class => 1, |
29
|
|
|
Annotation\MappedSuperclass::class => 2, |
30
|
|
|
]; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* The AnnotationReader. |
34
|
|
|
* |
35
|
|
|
* @var AnnotationReader |
36
|
|
|
*/ |
37
|
|
|
protected $reader; |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* The paths where to look for mapping files. |
41
|
|
|
* |
42
|
|
|
* @var array |
43
|
|
|
*/ |
44
|
|
|
protected $paths = []; |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* The paths excluded from path where to look for mapping files. |
48
|
|
|
* |
49
|
|
|
* @var array |
50
|
|
|
*/ |
51
|
|
|
protected $excludePaths = []; |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* The file extension of mapping documents. |
55
|
|
|
* |
56
|
362 |
|
* @var string |
57
|
|
|
*/ |
58
|
|
|
protected $fileExtension = '.php'; |
59
|
362 |
|
|
60
|
362 |
|
/** |
61
|
|
|
* Cache for AnnotationDriver#getAllClassNames(). |
62
|
362 |
|
* |
63
|
|
|
* @var array|null |
64
|
|
|
*/ |
65
|
1 |
|
protected $classNames; |
66
|
|
|
|
67
|
|
|
/** |
68
|
362 |
|
* Initializes a new AnnotationDriver that uses the given AnnotationReader for reading |
69
|
|
|
* docblock annotations. |
70
|
362 |
|
* |
71
|
359 |
|
* @param AnnotationReader $reader The AnnotationReader to use, duck-typed. |
72
|
359 |
|
* @param string|array|null $paths One or multiple paths where mapping classes can be found. |
73
|
|
|
*/ |
74
|
|
|
public function __construct($reader, $paths = null) |
75
|
|
|
{ |
76
|
359 |
|
$this->reader = $reader; |
77
|
|
|
if ($paths) { |
78
|
|
|
$this->addPaths((array) $paths); |
79
|
|
|
} |
80
|
|
|
} |
81
|
|
|
|
82
|
362 |
|
/** |
83
|
354 |
|
* Appends lookup paths to metadata driver. |
84
|
|
|
* |
85
|
354 |
|
* @param array $paths |
86
|
8 |
|
* |
87
|
|
|
* @return void |
88
|
|
|
*/ |
89
|
354 |
|
public function addPaths(array $paths) |
90
|
1 |
|
{ |
91
|
|
|
$this->paths = array_unique(array_merge($this->paths, $paths)); |
92
|
|
|
} |
93
|
354 |
|
|
94
|
|
|
/** |
95
|
38 |
|
* Retrieves the defined metadata lookup paths. |
96
|
32 |
|
* |
97
|
|
|
* @return array |
98
|
32 |
|
*/ |
99
|
32 |
|
public function getPaths() |
100
|
32 |
|
{ |
101
|
|
|
return $this->paths; |
102
|
7 |
|
} |
103
|
4 |
|
|
104
|
4 |
|
/** |
105
|
|
|
* Append exclude lookup paths to metadata driver. |
106
|
|
|
* |
107
|
3 |
|
* @param array $paths |
108
|
|
|
*/ |
109
|
|
|
public function addExcludePaths(array $paths) |
110
|
|
|
{ |
111
|
359 |
|
$this->excludePaths = array_unique(array_merge($this->excludePaths, $paths)); |
112
|
187 |
|
} |
113
|
|
|
|
114
|
187 |
|
/** |
115
|
|
|
* Retrieve the defined metadata lookup exclude paths. |
116
|
187 |
|
* |
117
|
13 |
|
* @return array |
118
|
13 |
|
*/ |
119
|
13 |
|
public function getExcludePaths() |
120
|
13 |
|
{ |
121
|
13 |
|
return $this->excludePaths; |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
/** |
125
|
187 |
|
* Retrieve the current annotation reader |
126
|
8 |
|
* |
127
|
8 |
|
* @return AnnotationReader |
128
|
8 |
|
*/ |
129
|
8 |
|
public function getReader() |
130
|
|
|
{ |
131
|
|
|
return $this->reader; |
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
/** |
135
|
359 |
|
* Gets the file extension used to look for mapping files under. |
136
|
14 |
|
* |
137
|
|
|
* @return string |
138
|
14 |
|
*/ |
139
|
14 |
|
public function getFileExtension() |
140
|
14 |
|
{ |
141
|
|
|
return $this->fileExtension; |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
/** |
145
|
359 |
|
* Sets the file extension used to look for mapping files under. |
146
|
14 |
|
* |
147
|
|
|
* @param string $fileExtension The file extension to set. |
148
|
14 |
|
* |
149
|
14 |
|
* @return void |
150
|
14 |
|
*/ |
151
|
14 |
|
public function setFileExtension($fileExtension) |
152
|
14 |
|
{ |
153
|
14 |
|
$this->fileExtension = $fileExtension; |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
/** |
157
|
|
|
* Returns whether the class with the specified name is transient. Only non-transient |
158
|
|
|
* classes, that is entities and mapped superclasses, should have their metadata loaded. |
159
|
359 |
|
* |
160
|
14 |
|
* A class is non-transient if it is annotated with an annotation |
161
|
|
|
* from the {@see AnnotationDriver::entityAnnotationClasses}. |
162
|
14 |
|
* |
163
|
14 |
|
* @param string $className |
164
|
14 |
|
* |
165
|
|
|
* @return boolean |
166
|
14 |
|
*/ |
167
|
|
|
public function isTransient($className) |
168
|
14 |
|
{ |
169
|
14 |
|
$classAnnotations = $this->reader->getClassAnnotations(new \ReflectionClass($className)); |
170
|
14 |
|
|
171
|
|
|
foreach ($classAnnotations as $annot) { |
172
|
|
|
if (isset($this->entityAnnotationClasses[get_class($annot)])) { |
173
|
14 |
|
return false; |
174
|
14 |
|
} |
175
|
14 |
|
} |
176
|
14 |
|
return true; |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
/** |
180
|
14 |
|
* {@inheritDoc} |
181
|
|
|
*/ |
182
|
|
|
public function getAllClassNames() |
183
|
14 |
|
{ |
184
|
9 |
|
if ($this->classNames !== null) { |
185
|
9 |
|
return $this->classNames; |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
if (!$this->paths) { |
|
|
|
|
189
|
14 |
|
throw Mapping\MappingException::pathRequired(); |
190
|
14 |
|
} |
191
|
14 |
|
|
192
|
14 |
|
$classes = []; |
193
|
|
|
$includedFiles = []; |
194
|
|
|
|
195
|
|
|
foreach ($this->paths as $path) { |
196
|
|
|
if ( ! is_dir($path)) { |
197
|
|
|
throw Mapping\MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path); |
198
|
359 |
|
} |
199
|
9 |
|
|
200
|
|
|
$iterator = new \RegexIterator( |
201
|
9 |
|
new \RecursiveIteratorIterator( |
202
|
|
|
new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS), |
203
|
|
|
\RecursiveIteratorIterator::LEAVES_ONLY |
204
|
|
|
), |
205
|
9 |
|
'/^.+' . preg_quote($this->fileExtension) . '$/i', |
206
|
9 |
|
\RecursiveRegexIterator::GET_MATCH |
207
|
|
|
); |
208
|
|
|
|
209
|
|
|
foreach ($iterator as $file) { |
210
|
9 |
|
$sourceFile = $file[0]; |
211
|
|
|
|
212
|
|
|
if ( ! preg_match('(^phar:)i', $sourceFile)) { |
213
|
|
|
$sourceFile = realpath($sourceFile); |
214
|
|
|
} |
215
|
359 |
|
|
216
|
65 |
|
foreach ($this->excludePaths as $excludePath) { |
217
|
|
|
$exclude = str_replace('\\', '/', realpath($excludePath)); |
218
|
65 |
|
$current = str_replace('\\', '/', $sourceFile); |
219
|
65 |
|
|
220
|
|
|
if (strpos($current, $exclude) !== false) { |
221
|
|
|
continue 2; |
222
|
65 |
|
} |
223
|
65 |
|
} |
224
|
|
|
|
225
|
65 |
|
require_once $sourceFile; |
226
|
|
|
|
227
|
|
|
$includedFiles[] = $sourceFile; |
228
|
65 |
|
} |
229
|
48 |
|
} |
230
|
|
|
|
231
|
48 |
|
$declared = get_declared_classes(); |
232
|
48 |
|
|
233
|
48 |
|
foreach ($declared as $className) { |
234
|
48 |
|
$rc = new \ReflectionClass($className); |
235
|
|
|
$sourceFile = $rc->getFileName(); |
236
|
20 |
|
if (in_array($sourceFile, $includedFiles) && ! $this->isTransient($className)) { |
237
|
20 |
|
$classes[] = $className; |
238
|
20 |
|
} |
239
|
|
|
} |
240
|
|
|
|
241
|
65 |
|
$this->classNames = $classes; |
242
|
|
|
|
243
|
|
|
return $classes; |
244
|
65 |
|
} |
245
|
64 |
|
|
246
|
|
|
/** |
247
|
64 |
|
* {@inheritDoc} |
248
|
|
|
*/ |
249
|
|
|
public function loadMetadataForClass( |
250
|
|
|
string $className, |
251
|
|
|
Mapping\ClassMetadata $metadata, |
252
|
|
|
Mapping\ClassMetadataBuildingContext $metadataBuildingContext |
253
|
|
|
) : Mapping\ClassMetadata |
254
|
359 |
|
{ |
255
|
5 |
|
$reflectionClass = $metadata->getReflectionClass(); |
256
|
|
|
|
257
|
5 |
|
if (! $reflectionClass) { |
258
|
5 |
|
// this happens when running annotation driver in combination with |
259
|
|
|
// static reflection services. This is not the nicest fix |
260
|
|
|
$reflectionClass = new \ReflectionClass($metadata->getClassName()); |
261
|
|
|
} |
262
|
|
|
|
263
|
|
|
$classAnnotations = $this->getClassAnnotations($reflectionClass); |
264
|
359 |
|
$classMetadata = $this->convertClassAnnotationsToClassMetadata( |
265
|
|
|
$classAnnotations, |
266
|
|
|
$reflectionClass, |
267
|
|
|
$metadata, |
268
|
|
|
$metadataBuildingContext |
269
|
357 |
|
); |
270
|
357 |
|
|
271
|
357 |
|
// Evaluate @Cache annotation |
272
|
357 |
|
if (isset($classAnnotations[Annotation\Cache::class])) { |
273
|
69 |
|
$cacheAnnot = $classAnnotations[Annotation\Cache::class]; |
274
|
|
|
$cache = $this->convertCacheAnnotationToCacheMetadata($cacheAnnot, $metadata); |
275
|
|
|
|
276
|
357 |
|
$classMetadata->setCache($cache); |
277
|
357 |
|
} |
278
|
|
|
|
279
|
|
|
// Evaluate annotations on properties/fields |
280
|
357 |
|
/* @var $reflProperty \ReflectionProperty */ |
281
|
12 |
|
foreach ($reflectionClass->getProperties() as $reflectionProperty) { |
282
|
12 |
|
if ($reflectionProperty->getDeclaringClass()->getName() !== $reflectionClass->getName()) { |
|
|
|
|
283
|
12 |
|
continue; |
284
|
|
|
} |
285
|
|
|
|
286
|
|
|
$propertyAnnotations = $this->getPropertyAnnotations($reflectionProperty); |
287
|
356 |
|
$property = $this->convertPropertyAnnotationsToProperty( |
|
|
|
|
288
|
|
|
$propertyAnnotations, |
289
|
356 |
|
$reflectionProperty, |
290
|
136 |
|
$classMetadata |
291
|
355 |
|
); |
292
|
21 |
|
|
293
|
21 |
|
if (! $property) { |
294
|
|
|
continue; |
295
|
|
|
} |
296
|
|
|
|
297
|
|
|
$metadata->addProperty($property); |
298
|
|
|
} |
299
|
356 |
|
|
300
|
347 |
|
$this->attachPropertyOverrides($classAnnotations, $reflectionClass, $metadata); |
301
|
|
|
|
302
|
|
|
return $classMetadata; |
303
|
|
|
} |
304
|
347 |
|
|
305
|
|
|
/** |
306
|
347 |
|
* @param array $classAnnotations |
307
|
340 |
|
* @param \ReflectionClass $reflectionClass |
308
|
|
|
* @param Mapping\ClassMetadata $metadata |
309
|
|
|
* @param Mapping\ClassMetadataBuildingContext $metadataBuildingContext |
310
|
347 |
|
* |
311
|
291 |
|
* @return Mapping\ClassMetadata |
312
|
291 |
|
* |
313
|
|
|
* @throws Mapping\MappingException |
314
|
|
|
*/ |
315
|
|
|
private function convertClassAnnotationsToClassMetadata( |
316
|
347 |
|
array $classAnnotations, |
317
|
|
|
\ReflectionClass $reflectionClass, |
318
|
347 |
|
Mapping\ClassMetadata $metadata, |
319
|
13 |
|
Mapping\ClassMetadataBuildingContext $metadataBuildingContext |
320
|
|
|
) : Mapping\ClassMetadata |
321
|
|
|
{ |
322
|
|
|
switch (true) { |
323
|
347 |
|
case isset($classAnnotations[Annotation\Entity::class]): |
324
|
8 |
|
return $this->convertClassAnnotationsToEntityClassMetadata( |
325
|
8 |
|
$classAnnotations, |
326
|
8 |
|
$reflectionClass, |
327
|
8 |
|
$metadata, |
328
|
|
|
$metadataBuildingContext |
329
|
344 |
|
); |
330
|
|
|
|
331
|
344 |
|
break; |
|
|
|
|
332
|
2 |
|
|
333
|
347 |
|
case isset($classAnnotations[Annotation\MappedSuperclass::class]): |
334
|
|
|
return $this->convertClassAnnotationsToMappedSuperClassMetadata( |
335
|
|
|
$classAnnotations, |
336
|
255 |
|
$reflectionClass, |
337
|
106 |
|
$metadata |
338
|
9 |
|
); |
339
|
|
|
|
340
|
|
|
case isset($classAnnotations[Annotation\Embeddable::class]): |
341
|
106 |
|
return $this->convertClassAnnotationsToEmbeddableClassMetadata( |
342
|
106 |
|
$classAnnotations, |
343
|
106 |
|
$reflectionClass, |
344
|
106 |
|
$metadata |
345
|
106 |
|
); |
346
|
106 |
|
|
347
|
106 |
|
default: |
348
|
106 |
|
throw Mapping\MappingException::classIsNotAValidEntityOrMappedSuperClass($reflectionClass->getName()); |
|
|
|
|
349
|
206 |
|
} |
350
|
102 |
|
} |
351
|
102 |
|
|
352
|
102 |
|
/** |
353
|
102 |
|
* @param array $classAnnotations |
354
|
102 |
|
* @param \ReflectionClass $reflectionClass |
355
|
102 |
|
* @param Mapping\ClassMetadata $metadata |
356
|
|
|
* @param Mapping\ClassMetadataBuildingContext $metadataBuildingContext |
357
|
102 |
|
* |
358
|
15 |
|
* @return Mapping\ClassMetadata |
359
|
|
|
* |
360
|
|
|
* @throws Mapping\MappingException |
361
|
102 |
|
* @throws \UnexpectedValueException |
362
|
203 |
|
*/ |
363
|
133 |
|
private function convertClassAnnotationsToEntityClassMetadata( |
364
|
30 |
|
array $classAnnotations, |
365
|
|
|
\ReflectionClass $reflectionClass, |
366
|
|
|
Mapping\ClassMetadata $metadata, |
367
|
133 |
|
Mapping\ClassMetadataBuildingContext $metadataBuildingContext |
368
|
133 |
|
) |
369
|
133 |
|
{ |
370
|
133 |
|
/** @var Annotation\Entity $entityAnnot */ |
371
|
133 |
|
$entityAnnot = $classAnnotations[Annotation\Entity::class]; |
372
|
133 |
|
|
373
|
113 |
|
if ($entityAnnot->repositoryClass !== null) { |
374
|
84 |
|
$metadata->setCustomRepositoryClassName( |
375
|
|
|
$metadata->fullyQualifiedClassName($entityAnnot->repositoryClass) |
376
|
84 |
|
); |
377
|
|
|
} |
378
|
65 |
|
|
379
|
65 |
|
if ($entityAnnot->readOnly) { |
380
|
|
|
$metadata->asReadOnly(); |
381
|
|
|
} |
382
|
65 |
|
|
383
|
64 |
|
$metadata->isMappedSuperclass = false; |
384
|
|
|
$metadata->isEmbeddedClass = false; |
385
|
|
|
|
386
|
65 |
|
$this->attachTable($classAnnotations, $reflectionClass, $metadata, $metadataBuildingContext); |
387
|
64 |
|
|
388
|
|
|
// Evaluate @ChangeTrackingPolicy annotation |
389
|
|
|
if (isset($classAnnotations[Annotation\ChangeTrackingPolicy::class])) { |
390
|
|
|
$changeTrackingAnnot = $classAnnotations[Annotation\ChangeTrackingPolicy::class]; |
391
|
84 |
|
|
392
|
84 |
|
$metadata->setChangeTrackingPolicy( |
393
|
84 |
|
constant(sprintf('%s::%s', Mapping\ChangeTrackingPolicy::class, $changeTrackingAnnot->value)) |
394
|
84 |
|
); |
395
|
84 |
|
} |
396
|
84 |
|
|
397
|
84 |
|
// Evaluate @InheritanceType annotation |
398
|
84 |
|
if (isset($classAnnotations[Annotation\InheritanceType::class])) { |
399
|
|
|
$inheritanceTypeAnnot = $classAnnotations[Annotation\InheritanceType::class]; |
400
|
84 |
|
|
401
|
3 |
|
$metadata->setInheritanceType( |
402
|
|
|
constant(sprintf('%s::%s', Mapping\InheritanceType::class, $inheritanceTypeAnnot->value)) |
403
|
|
|
); |
404
|
84 |
|
|
405
|
44 |
|
if ($metadata->inheritanceType !== Mapping\InheritanceType::NONE) { |
406
|
10 |
|
$this->attachDiscriminatorColumn($classAnnotations, $reflectionClass, $metadata); |
407
|
10 |
|
} |
408
|
|
|
} |
409
|
355 |
|
|
410
|
|
|
$this->attachNamedQueries($classAnnotations, $reflectionClass, $metadata); |
411
|
|
|
$this->attachNamedNativeQueries($classAnnotations, $reflectionClass, $metadata); |
412
|
|
|
$this->attachLifecycleCallbacks($classAnnotations, $reflectionClass, $metadata); |
413
|
|
|
$this->attachEntityListeners($classAnnotations, $reflectionClass, $metadata); |
414
|
357 |
|
|
415
|
4 |
|
return $metadata; |
416
|
|
|
} |
417
|
4 |
|
|
418
|
4 |
|
/** |
419
|
4 |
|
* @param array $classAnnotations |
420
|
|
|
* @param \ReflectionClass $reflectionClass |
421
|
|
|
* @param Mapping\ClassMetadata $metadata |
422
|
4 |
|
* |
423
|
3 |
|
* @return Mapping\ClassMetadata |
424
|
|
|
*/ |
425
|
3 |
|
private function convertClassAnnotationsToMappedSuperClassMetadata( |
426
|
3 |
|
array $classAnnotations, |
427
|
|
|
\ReflectionClass $reflectionClass, |
428
|
|
|
Mapping\ClassMetadata $metadata |
429
|
3 |
|
) : Mapping\ClassMetadata |
430
|
|
|
{ |
431
|
|
|
/** @var Annotation\MappedSuperclass $mappedSuperclassAnnot */ |
432
|
|
|
$mappedSuperclassAnnot = $classAnnotations[Annotation\MappedSuperclass::class]; |
433
|
4 |
|
|
434
|
2 |
|
if ($mappedSuperclassAnnot->repositoryClass !== null) { |
435
|
|
|
$metadata->setCustomRepositoryClassName( |
436
|
2 |
|
$metadata->fullyQualifiedClassName($mappedSuperclassAnnot->repositoryClass) |
437
|
2 |
|
); |
438
|
|
|
} |
439
|
|
|
|
440
|
2 |
|
$metadata->isMappedSuperclass = true; |
441
|
2 |
|
$metadata->isEmbeddedClass = false; |
442
|
|
|
|
443
|
|
|
$this->attachNamedQueries($classAnnotations, $reflectionClass, $metadata); |
444
|
2 |
|
$this->attachNamedNativeQueries($classAnnotations, $reflectionClass, $metadata); |
445
|
2 |
|
$this->attachLifecycleCallbacks($classAnnotations, $reflectionClass, $metadata); |
446
|
|
|
$this->attachEntityListeners($classAnnotations, $reflectionClass, $metadata); |
447
|
|
|
|
448
|
2 |
|
return $metadata; |
449
|
|
|
} |
450
|
|
|
|
451
|
|
|
/** |
452
|
4 |
|
* @param array $classAnnotations |
453
|
1 |
|
* @param \ReflectionClass $reflectionClass |
454
|
|
|
* @param Mapping\ClassMetadata $metadata |
455
|
|
|
* |
456
|
4 |
|
* @return Mapping\ClassMetadata |
457
|
|
|
*/ |
458
|
|
|
private function convertClassAnnotationsToEmbeddableClassMetadata( |
459
|
|
|
array $classAnnotations, |
|
|
|
|
460
|
|
|
\ReflectionClass $reflectionClass, |
|
|
|
|
461
|
357 |
|
Mapping\ClassMetadata $metadata |
462
|
3 |
|
) : Mapping\ClassMetadata |
463
|
|
|
{ |
464
|
3 |
|
$metadata->isMappedSuperclass = false; |
465
|
3 |
|
$metadata->isEmbeddedClass = true; |
466
|
|
|
|
467
|
3 |
|
return $metadata; |
468
|
|
|
} |
469
|
|
|
|
470
|
|
|
/** |
471
|
|
|
* @param array $propertyAnnotations |
472
|
357 |
|
* @param \ReflectionProperty $reflectionProperty |
473
|
10 |
|
* @param Mapping\ClassMetadata $metadata |
474
|
|
|
* |
475
|
10 |
|
* @todo guilhermeblanco Remove nullable typehint once embeddables are back |
476
|
10 |
|
* |
477
|
|
|
* @return Mapping\Property|null |
478
|
10 |
|
*/ |
479
|
|
|
private function convertPropertyAnnotationsToProperty( |
480
|
|
|
array $propertyAnnotations, |
481
|
|
|
\ReflectionProperty $reflectionProperty, |
482
|
10 |
|
Mapping\ClassMetadata $metadata |
483
|
10 |
|
) : ?Mapping\Property |
484
|
|
|
{ |
485
|
|
|
switch (true) { |
486
|
10 |
|
case isset($propertyAnnotations[Annotation\Column::class]): |
|
|
|
|
487
|
|
|
return $this->convertReflectionPropertyToFieldMetadata( |
488
|
10 |
|
$reflectionProperty, |
489
|
10 |
|
$propertyAnnotations, |
490
|
|
|
$metadata |
491
|
10 |
|
); |
492
|
10 |
|
|
493
|
|
|
case isset($propertyAnnotations[Annotation\OneToOne::class]): |
494
|
|
|
return $this->convertReflectionPropertyToOneToOneAssociationMetadata( |
495
|
|
|
$reflectionProperty, |
496
|
|
|
$propertyAnnotations, |
497
|
10 |
|
$metadata |
498
|
10 |
|
); |
499
|
|
|
|
500
|
|
|
case isset($propertyAnnotations[Annotation\ManyToOne::class]): |
501
|
|
|
return $this->convertReflectionPropertyToManyToOneAssociationMetadata( |
502
|
|
|
$reflectionProperty, |
503
|
|
|
$propertyAnnotations, |
504
|
357 |
|
$metadata |
505
|
|
|
); |
506
|
18 |
|
|
507
|
17 |
|
case isset($propertyAnnotations[Annotation\OneToMany::class]): |
508
|
17 |
|
return $this->convertReflectionPropertyToOneToManyAssociationMetadata( |
509
|
|
|
$reflectionProperty, |
510
|
|
|
$propertyAnnotations, |
511
|
|
|
$metadata |
512
|
357 |
|
); |
513
|
|
|
|
514
|
|
|
case isset($propertyAnnotations[Annotation\ManyToMany::class]): |
515
|
|
|
return $this->convertReflectionPropertyToManyToManyAssociationMetadata( |
516
|
|
|
$reflectionProperty, |
517
|
|
|
$propertyAnnotations, |
518
|
|
|
$metadata |
519
|
|
|
); |
520
|
|
|
|
521
|
|
|
case isset($propertyAnnotations[Annotation\Embedded::class]): |
522
|
|
|
return null; |
523
|
|
|
|
524
|
241 |
|
default: |
525
|
|
|
return new Mapping\TransientMetadata($reflectionProperty->getName()); |
526
|
241 |
|
} |
527
|
|
|
} |
528
|
241 |
|
|
529
|
|
|
/** |
530
|
|
|
* @param \ReflectionProperty $reflProperty |
531
|
|
|
* @param array $propertyAnnotations |
532
|
241 |
|
* @param Mapping\ClassMetadata $metadata |
533
|
|
|
* |
534
|
|
|
* @return Mapping\FieldMetadata |
535
|
|
|
* |
536
|
|
|
* @throws Mapping\MappingException |
537
|
|
|
*/ |
538
|
|
|
private function convertReflectionPropertyToFieldMetadata( |
539
|
|
|
\ReflectionProperty $reflProperty, |
540
|
|
|
array $propertyAnnotations, |
541
|
|
|
Mapping\ClassMetadata $metadata |
542
|
27 |
|
) : Mapping\FieldMetadata |
543
|
|
|
{ |
544
|
27 |
|
$className = $metadata->getClassName(); |
545
|
27 |
|
$fieldName = $reflProperty->getName(); |
546
|
|
|
$isVersioned = isset($propertyAnnotations[Annotation\Version::class]); |
547
|
27 |
|
$columnAnnot = $propertyAnnotations[Annotation\Column::class]; |
548
|
21 |
|
|
549
|
14 |
|
if ($columnAnnot->type == null) { |
550
|
|
|
throw Mapping\MappingException::propertyTypeIsRequired($className, $fieldName); |
551
|
|
|
} |
552
|
21 |
|
|
553
|
10 |
|
$fieldMetadata = $this->convertColumnAnnotationToFieldMetadata($columnAnnot, $fieldName, $isVersioned); |
554
|
|
|
|
555
|
|
|
// Check for Id |
556
|
21 |
|
if (isset($propertyAnnotations[Annotation\Id::class])) { |
557
|
11 |
|
$fieldMetadata->setPrimaryKey(true); |
558
|
|
|
} |
559
|
|
|
|
560
|
21 |
|
// Check for GeneratedValue strategy |
561
|
7 |
|
if (isset($propertyAnnotations[Annotation\GeneratedValue::class])) { |
562
|
|
|
$generatedValueAnnot = $propertyAnnotations[Annotation\GeneratedValue::class]; |
563
|
|
|
$strategy = strtoupper($generatedValueAnnot->strategy); |
564
|
21 |
|
$idGeneratorType = constant(sprintf('%s::%s', Mapping\GeneratorType::class, $strategy)); |
565
|
8 |
|
|
566
|
|
|
if ($idGeneratorType !== Mapping\GeneratorType::NONE) { |
567
|
|
|
$idGeneratorDefinition = []; |
568
|
21 |
|
|
569
|
6 |
|
// Check for CustomGenerator/SequenceGenerator/TableGenerator definition |
570
|
|
|
switch (true) { |
571
|
|
|
case isset($propertyAnnotations[Annotation\SequenceGenerator::class]): |
572
|
21 |
|
$seqGeneratorAnnot = $propertyAnnotations[Annotation\SequenceGenerator::class]; |
573
|
11 |
|
|
574
|
|
|
$idGeneratorDefinition = [ |
575
|
|
|
'sequenceName' => $seqGeneratorAnnot->sequenceName, |
576
|
21 |
|
'allocationSize' => $seqGeneratorAnnot->allocationSize, |
577
|
21 |
|
]; |
578
|
|
|
|
579
|
|
|
break; |
580
|
|
|
|
581
|
27 |
|
case isset($propertyAnnotations[Annotation\CustomIdGenerator::class]): |
582
|
|
|
$customGeneratorAnnot = $propertyAnnotations[Annotation\CustomIdGenerator::class]; |
583
|
|
|
|
584
|
|
|
$idGeneratorDefinition = [ |
585
|
|
|
'class' => $customGeneratorAnnot->class, |
586
|
|
|
'arguments' => $customGeneratorAnnot->arguments, |
587
|
|
|
]; |
588
|
|
|
|
589
|
|
|
break; |
590
|
|
|
|
591
|
173 |
|
/* @todo If it is not supported, why does this exist? */ |
592
|
|
|
case isset($propertyAnnotations['Doctrine\ORM\Mapping\TableGenerator']): |
593
|
|
|
throw Mapping\MappingException::tableIdGeneratorNotImplemented($className); |
594
|
173 |
|
} |
595
|
173 |
|
|
596
|
173 |
|
$fieldMetadata->setValueGenerator(new Mapping\ValueGeneratorMetadata($idGeneratorType, $idGeneratorDefinition)); |
597
|
173 |
|
} |
598
|
173 |
|
} |
599
|
173 |
|
|
600
|
|
|
return $fieldMetadata; |
601
|
|
|
} |
602
|
|
|
|
603
|
|
|
/** |
604
|
|
|
* @param \ReflectionProperty $reflectionProperty |
605
|
|
|
* @param array $propertyAnnotations |
606
|
|
|
* @param Mapping\ClassMetadata $metadata |
607
|
|
|
* |
608
|
|
|
* @return Mapping\OneToOneAssociationMetadata |
609
|
|
|
*/ |
610
|
|
|
private function convertReflectionPropertyToOneToOneAssociationMetadata( |
611
|
347 |
|
\ReflectionProperty $reflectionProperty, |
612
|
|
|
array $propertyAnnotations, |
613
|
|
|
Mapping\ClassMetadata $metadata |
614
|
347 |
|
) |
615
|
347 |
|
{ |
616
|
347 |
|
$className = $metadata->getClassName(); |
617
|
347 |
|
$fieldName = $reflectionProperty->getName(); |
618
|
347 |
|
$oneToOneAnnot = $propertyAnnotations[Annotation\OneToOne::class]; |
619
|
|
|
$assocMetadata = new Mapping\OneToOneAssociationMetadata($fieldName); |
620
|
|
|
$targetEntity = $metadata->fullyQualifiedClassName($oneToOneAnnot->targetEntity); |
621
|
347 |
|
|
622
|
9 |
|
$assocMetadata->setTargetEntity($targetEntity); |
623
|
|
|
$assocMetadata->setCascade($this->getCascade($className, $fieldName, $oneToOneAnnot->cascade)); |
624
|
|
|
$assocMetadata->setOrphanRemoval($oneToOneAnnot->orphanRemoval); |
625
|
347 |
|
$assocMetadata->setFetchMode($this->getFetchMode($className, $oneToOneAnnot->fetch)); |
626
|
80 |
|
|
627
|
|
|
if (! empty($oneToOneAnnot->mappedBy)) { |
628
|
|
|
$assocMetadata->setMappedBy($oneToOneAnnot->mappedBy); |
629
|
347 |
|
} |
630
|
5 |
|
|
631
|
|
|
if (! empty($oneToOneAnnot->inversedBy)) { |
632
|
|
|
$assocMetadata->setInversedBy($oneToOneAnnot->inversedBy); |
633
|
347 |
|
} |
634
|
|
|
|
635
|
|
|
// Check for Id |
636
|
|
|
if (isset($propertyAnnotations[Annotation\Id::class])) { |
637
|
|
|
$assocMetadata->setPrimaryKey(true); |
638
|
|
|
} |
639
|
|
|
|
640
|
|
|
$this->attachAssociationPropertyCache($propertyAnnotations, $reflectionProperty, $assocMetadata, $metadata); |
641
|
|
|
|
642
|
|
|
// Check for JoinColumn/JoinColumns annotations |
643
|
|
|
switch (true) { |
644
|
|
|
case isset($propertyAnnotations[Annotation\JoinColumn::class]): |
645
|
|
|
$joinColumnAnnot = $propertyAnnotations[Annotation\JoinColumn::class]; |
646
|
|
|
|
647
|
|
|
$assocMetadata->addJoinColumn( |
648
|
|
|
$this->convertJoinColumnAnnotationToJoinColumnMetadata($joinColumnAnnot) |
649
|
|
|
); |
650
|
|
|
|
651
|
|
|
break; |
652
|
|
|
|
653
|
|
|
case isset($propertyAnnotations[Annotation\JoinColumns::class]): |
654
|
|
|
$joinColumnsAnnot = $propertyAnnotations[Annotation\JoinColumns::class]; |
655
|
|
|
|
656
|
|
|
foreach ($joinColumnsAnnot->value as $joinColumnAnnot) { |
657
|
|
|
$assocMetadata->addJoinColumn( |
658
|
|
|
$this->convertJoinColumnAnnotationToJoinColumnMetadata($joinColumnAnnot) |
659
|
|
|
); |
660
|
|
|
} |
661
|
|
|
|
662
|
|
|
break; |
663
|
|
|
} |
664
|
|
|
|
665
|
|
|
return $assocMetadata; |
666
|
|
|
} |
667
|
|
|
|
668
|
|
|
/** |
669
|
|
|
* @param \ReflectionProperty $reflectionProperty |
670
|
|
|
* @param array $propertyAnnotations |
671
|
|
|
* @param Mapping\ClassMetadata $metadata |
672
|
|
|
* |
673
|
|
|
* @return Mapping\ManyToOneAssociationMetadata |
674
|
|
|
*/ |
675
|
|
|
private function convertReflectionPropertyToManyToOneAssociationMetadata( |
676
|
|
|
\ReflectionProperty $reflectionProperty, |
677
|
|
|
array $propertyAnnotations, |
678
|
|
|
Mapping\ClassMetadata $metadata |
679
|
|
|
) |
680
|
|
|
{ |
681
|
|
|
$className = $metadata->getClassName(); |
682
|
|
|
$fieldName = $reflectionProperty->getName(); |
683
|
|
|
$manyToOneAnnot = $propertyAnnotations[Annotation\ManyToOne::class]; |
684
|
|
|
$assocMetadata = new Mapping\ManyToOneAssociationMetadata($fieldName); |
685
|
|
|
$targetEntity = $metadata->fullyQualifiedClassName($manyToOneAnnot->targetEntity); |
686
|
|
|
|
687
|
|
|
$assocMetadata->setTargetEntity($targetEntity); |
688
|
|
|
$assocMetadata->setCascade($this->getCascade($className, $fieldName, $manyToOneAnnot->cascade)); |
689
|
|
|
$assocMetadata->setFetchMode($this->getFetchMode($className, $manyToOneAnnot->fetch)); |
690
|
|
|
|
691
|
|
|
if (! empty($manyToOneAnnot->inversedBy)) { |
692
|
|
|
$assocMetadata->setInversedBy($manyToOneAnnot->inversedBy); |
693
|
|
|
} |
694
|
|
|
|
695
|
|
|
// Check for Id |
696
|
|
|
if (isset($propertyAnnotations[Annotation\Id::class])) { |
697
|
|
|
$assocMetadata->setPrimaryKey(true); |
698
|
|
|
} |
699
|
|
|
|
700
|
|
|
$this->attachAssociationPropertyCache($propertyAnnotations, $reflectionProperty, $assocMetadata, $metadata); |
701
|
|
|
|
702
|
|
|
// Check for JoinColumn/JoinColumns annotations |
703
|
|
|
switch (true) { |
704
|
|
|
case isset($propertyAnnotations[Annotation\JoinColumn::class]): |
705
|
|
|
$joinColumnAnnot = $propertyAnnotations[Annotation\JoinColumn::class]; |
706
|
|
|
|
707
|
|
|
$assocMetadata->addJoinColumn( |
708
|
|
|
$this->convertJoinColumnAnnotationToJoinColumnMetadata($joinColumnAnnot) |
709
|
|
|
); |
710
|
|
|
|
711
|
|
|
break; |
712
|
|
|
|
713
|
|
|
case isset($propertyAnnotations[Annotation\JoinColumns::class]): |
714
|
|
|
$joinColumnsAnnot = $propertyAnnotations[Annotation\JoinColumns::class]; |
715
|
|
|
|
716
|
|
|
foreach ($joinColumnsAnnot->value as $joinColumnAnnot) { |
717
|
|
|
$assocMetadata->addJoinColumn( |
718
|
|
|
$this->convertJoinColumnAnnotationToJoinColumnMetadata($joinColumnAnnot) |
719
|
|
|
); |
720
|
|
|
} |
721
|
|
|
|
722
|
|
|
break; |
723
|
|
|
} |
724
|
|
|
|
725
|
|
|
return $assocMetadata; |
726
|
|
|
} |
727
|
|
|
|
728
|
|
|
/** |
729
|
|
|
* @param \ReflectionProperty $reflectionProperty |
730
|
|
|
* @param array $propertyAnnotations |
731
|
|
|
* @param Mapping\ClassMetadata $metadata |
732
|
|
|
* |
733
|
|
|
* @return Mapping\OneToManyAssociationMetadata |
734
|
|
|
* |
735
|
|
|
* @throws Mapping\MappingException |
736
|
|
|
*/ |
737
|
|
|
private function convertReflectionPropertyToOneToManyAssociationMetadata( |
738
|
|
|
\ReflectionProperty $reflectionProperty, |
739
|
|
|
array $propertyAnnotations, |
740
|
|
|
Mapping\ClassMetadata $metadata |
741
|
|
|
) : Mapping\OneToManyAssociationMetadata |
742
|
|
|
{ |
743
|
|
|
$className = $metadata->getClassName(); |
744
|
|
|
$fieldName = $reflectionProperty->getName(); |
745
|
|
|
$oneToManyAnnot = $propertyAnnotations[Annotation\OneToMany::class]; |
746
|
|
|
$assocMetadata = new Mapping\OneToManyAssociationMetadata($fieldName); |
747
|
|
|
$targetEntity = $metadata->fullyQualifiedClassName($oneToManyAnnot->targetEntity); |
748
|
|
|
|
749
|
|
|
$assocMetadata->setTargetEntity($targetEntity); |
750
|
|
|
$assocMetadata->setCascade($this->getCascade($className, $fieldName, $oneToManyAnnot->cascade)); |
751
|
|
|
$assocMetadata->setOrphanRemoval($oneToManyAnnot->orphanRemoval); |
752
|
|
|
$assocMetadata->setFetchMode($this->getFetchMode($className, $oneToManyAnnot->fetch)); |
753
|
|
|
|
754
|
|
|
if (! empty($oneToManyAnnot->mappedBy)) { |
755
|
|
|
$assocMetadata->setMappedBy($oneToManyAnnot->mappedBy); |
756
|
|
|
} |
757
|
|
|
|
758
|
|
|
if (! empty($oneToManyAnnot->indexBy)) { |
759
|
|
|
$assocMetadata->setIndexedBy($oneToManyAnnot->indexBy); |
760
|
|
|
} |
761
|
|
|
|
762
|
|
|
// Check for OrderBy |
763
|
|
|
if (isset($propertyAnnotations[Annotation\OrderBy::class])) { |
764
|
|
|
$orderByAnnot = $propertyAnnotations[Annotation\OrderBy::class]; |
765
|
|
|
|
766
|
|
|
$assocMetadata->setOrderBy($orderByAnnot->value); |
767
|
|
|
} |
768
|
|
|
|
769
|
|
|
// Check for Id |
770
|
|
|
if (isset($propertyAnnotations[Annotation\Id::class])) { |
771
|
|
|
throw Mapping\MappingException::illegalToManyIdentifierAssociation($className, $fieldName); |
772
|
|
|
} |
773
|
|
|
|
774
|
|
|
$this->attachAssociationPropertyCache($propertyAnnotations, $reflectionProperty, $assocMetadata, $metadata); |
775
|
|
|
|
776
|
|
|
return $assocMetadata; |
777
|
|
|
} |
778
|
|
|
|
779
|
|
|
/** |
780
|
|
|
* @param \ReflectionProperty $reflectionProperty |
781
|
|
|
* @param array $propertyAnnotations |
782
|
|
|
* @param Mapping\ClassMetadata $metadata |
783
|
|
|
* |
784
|
|
|
* @return Mapping\ManyToManyAssociationMetadata |
785
|
|
|
* |
786
|
|
|
* @throws Mapping\MappingException |
787
|
|
|
*/ |
788
|
|
|
private function convertReflectionPropertyToManyToManyAssociationMetadata( |
789
|
|
|
\ReflectionProperty $reflectionProperty, |
790
|
|
|
array $propertyAnnotations, |
791
|
|
|
Mapping\ClassMetadata $metadata |
792
|
|
|
) : Mapping\ManyToManyAssociationMetadata |
793
|
|
|
{ |
794
|
|
|
$className = $metadata->getClassName(); |
795
|
|
|
$fieldName = $reflectionProperty->getName(); |
796
|
|
|
$manyToManyAnnot = $propertyAnnotations[Annotation\ManyToMany::class]; |
797
|
|
|
$assocMetadata = new Mapping\ManyToManyAssociationMetadata($fieldName); |
798
|
|
|
$targetEntity = $metadata->fullyQualifiedClassName($manyToManyAnnot->targetEntity); |
799
|
|
|
|
800
|
|
|
$assocMetadata->setTargetEntity($targetEntity); |
801
|
|
|
$assocMetadata->setCascade($this->getCascade($className, $fieldName, $manyToManyAnnot->cascade)); |
802
|
|
|
$assocMetadata->setOrphanRemoval($manyToManyAnnot->orphanRemoval); |
803
|
|
|
$assocMetadata->setFetchMode($this->getFetchMode($className, $manyToManyAnnot->fetch)); |
804
|
|
|
|
805
|
|
|
if (! empty($manyToManyAnnot->mappedBy)) { |
806
|
|
|
$assocMetadata->setMappedBy($manyToManyAnnot->mappedBy); |
807
|
|
|
} |
808
|
|
|
|
809
|
|
|
if (! empty($manyToManyAnnot->inversedBy)) { |
810
|
|
|
$assocMetadata->setInversedBy($manyToManyAnnot->inversedBy); |
811
|
|
|
} |
812
|
|
|
|
813
|
|
|
if (! empty($manyToManyAnnot->indexBy)) { |
814
|
|
|
$assocMetadata->setIndexedBy($manyToManyAnnot->indexBy); |
815
|
|
|
} |
816
|
|
|
|
817
|
|
|
// Check for JoinTable |
818
|
|
|
if (isset($propertyAnnotations[Annotation\JoinTable::class])) { |
819
|
|
|
$joinTableAnnot = $propertyAnnotations[Annotation\JoinTable::class]; |
820
|
|
|
$joinTableMetadata = $this->convertJoinTableAnnotationToJoinTableMetadata($joinTableAnnot); |
821
|
|
|
|
822
|
|
|
$assocMetadata->setJoinTable($joinTableMetadata); |
823
|
|
|
} |
824
|
|
|
|
825
|
|
|
// Check for OrderBy |
826
|
|
|
if (isset($propertyAnnotations[Annotation\OrderBy::class])) { |
827
|
|
|
$orderByAnnot = $propertyAnnotations[Annotation\OrderBy::class]; |
828
|
|
|
|
829
|
|
|
$assocMetadata->setOrderBy($orderByAnnot->value); |
830
|
|
|
} |
831
|
|
|
|
832
|
|
|
// Check for Id |
833
|
|
|
if (isset($propertyAnnotations[Annotation\Id::class])) { |
834
|
|
|
throw Mapping\MappingException::illegalToManyIdentifierAssociation($className, $fieldName); |
835
|
|
|
} |
836
|
|
|
|
837
|
|
|
$this->attachAssociationPropertyCache($propertyAnnotations, $reflectionProperty, $assocMetadata, $metadata); |
838
|
|
|
|
839
|
|
|
return $assocMetadata; |
840
|
|
|
} |
841
|
|
|
|
842
|
|
|
/** |
843
|
|
|
* Parse the given Column as FieldMetadata |
844
|
|
|
* |
845
|
|
|
* @param Annotation\Column $columnAnnot |
846
|
|
|
* @param string $fieldName |
847
|
|
|
* @param bool $isVersioned |
848
|
|
|
* |
849
|
|
|
* @return Mapping\FieldMetadata |
850
|
|
|
*/ |
851
|
|
|
private function convertColumnAnnotationToFieldMetadata( |
852
|
|
|
Annotation\Column $columnAnnot, |
853
|
|
|
string $fieldName, |
854
|
|
|
bool $isVersioned |
855
|
|
|
) : Mapping\FieldMetadata |
856
|
|
|
{ |
857
|
|
|
$fieldMetadata = $isVersioned |
858
|
|
|
? new Mapping\VersionFieldMetadata($fieldName) |
859
|
|
|
: new Mapping\FieldMetadata($fieldName) |
860
|
|
|
; |
861
|
|
|
|
862
|
|
|
$fieldMetadata->setType(Type::getType($columnAnnot->type)); |
863
|
|
|
|
864
|
|
|
if (! empty($columnAnnot->name)) { |
865
|
|
|
$fieldMetadata->setColumnName($columnAnnot->name); |
866
|
|
|
} |
867
|
|
|
|
868
|
|
|
if (! empty($columnAnnot->columnDefinition)) { |
869
|
|
|
$fieldMetadata->setColumnDefinition($columnAnnot->columnDefinition); |
870
|
|
|
} |
871
|
|
|
|
872
|
|
|
if (! empty($columnAnnot->length)) { |
873
|
|
|
$fieldMetadata->setLength($columnAnnot->length); |
874
|
|
|
} |
875
|
|
|
|
876
|
|
|
if ($columnAnnot->options) { |
|
|
|
|
877
|
|
|
$fieldMetadata->setOptions($columnAnnot->options); |
878
|
|
|
} |
879
|
|
|
|
880
|
|
|
$fieldMetadata->setScale($columnAnnot->scale); |
881
|
|
|
$fieldMetadata->setPrecision($columnAnnot->precision); |
882
|
|
|
$fieldMetadata->setNullable($columnAnnot->nullable); |
883
|
|
|
$fieldMetadata->setUnique($columnAnnot->unique); |
884
|
|
|
|
885
|
|
|
return $fieldMetadata; |
886
|
|
|
} |
887
|
|
|
|
888
|
|
|
/** |
889
|
|
|
* Parse the given Table as TableMetadata |
890
|
|
|
* |
891
|
|
|
* @param Annotation\Table $tableAnnot |
892
|
|
|
* @param Mapping\TableMetadata $tableMetadata |
893
|
|
|
* |
894
|
|
|
* @return void |
895
|
|
|
*/ |
896
|
|
|
private function convertTableAnnotationToTableMetadata( |
897
|
|
|
Annotation\Table $tableAnnot, |
898
|
|
|
Mapping\TableMetadata $tableMetadata |
899
|
|
|
) : void |
900
|
|
|
{ |
901
|
|
|
if (! empty($tableAnnot->name)) { |
902
|
|
|
$tableMetadata->setName($tableAnnot->name); |
903
|
|
|
} |
904
|
|
|
|
905
|
|
|
if (! empty($tableAnnot->schema)) { |
906
|
|
|
$tableMetadata->setSchema($tableAnnot->schema); |
907
|
|
|
} |
908
|
|
|
|
909
|
|
|
foreach ($tableAnnot->options as $optionName => $optionValue) { |
910
|
|
|
$tableMetadata->addOption($optionName, $optionValue); |
911
|
|
|
} |
912
|
|
|
|
913
|
|
|
foreach ($tableAnnot->indexes as $indexAnnot) { |
914
|
|
|
$tableMetadata->addIndex([ |
915
|
|
|
'name' => $indexAnnot->name, |
916
|
|
|
'columns' => $indexAnnot->columns, |
917
|
|
|
'unique' => $indexAnnot->unique, |
918
|
|
|
'options' => $indexAnnot->options, |
919
|
|
|
'flags' => $indexAnnot->flags, |
920
|
|
|
]); |
921
|
|
|
} |
922
|
|
|
|
923
|
|
|
foreach ($tableAnnot->uniqueConstraints as $uniqueConstraintAnnot) { |
924
|
|
|
$tableMetadata->addUniqueConstraint([ |
925
|
|
|
'name' => $uniqueConstraintAnnot->name, |
926
|
|
|
'columns' => $uniqueConstraintAnnot->columns, |
927
|
|
|
'options' => $uniqueConstraintAnnot->options, |
928
|
|
|
'flags' => $uniqueConstraintAnnot->flags, |
929
|
|
|
]); |
930
|
|
|
} |
931
|
|
|
} |
932
|
|
|
|
933
|
|
|
/** |
934
|
|
|
* Parse the given JoinTable as JoinTableMetadata |
935
|
|
|
* |
936
|
|
|
* @param Annotation\JoinTable $joinTableAnnot |
937
|
|
|
* |
938
|
|
|
* @return Mapping\JoinTableMetadata |
939
|
|
|
*/ |
940
|
|
|
private function convertJoinTableAnnotationToJoinTableMetadata( |
941
|
|
|
Annotation\JoinTable $joinTableAnnot |
942
|
|
|
) : Mapping\JoinTableMetadata |
943
|
|
|
{ |
944
|
|
|
$joinTable = new Mapping\JoinTableMetadata(); |
945
|
|
|
|
946
|
|
|
if (! empty($joinTableAnnot->name)) { |
947
|
|
|
$joinTable->setName($joinTableAnnot->name); |
948
|
|
|
} |
949
|
|
|
|
950
|
|
|
if (! empty($joinTableAnnot->schema)) { |
951
|
|
|
$joinTable->setSchema($joinTableAnnot->schema); |
952
|
|
|
} |
953
|
|
|
|
954
|
|
|
foreach ($joinTableAnnot->joinColumns as $joinColumnAnnot) { |
955
|
|
|
$joinColumn = $this->convertJoinColumnAnnotationToJoinColumnMetadata($joinColumnAnnot); |
956
|
|
|
|
957
|
|
|
$joinTable->addJoinColumn($joinColumn); |
958
|
|
|
} |
959
|
|
|
|
960
|
|
|
foreach ($joinTableAnnot->inverseJoinColumns as $joinColumnAnnot) { |
961
|
|
|
$joinColumn = $this->convertJoinColumnAnnotationToJoinColumnMetadata($joinColumnAnnot); |
962
|
|
|
|
963
|
|
|
$joinTable->addInverseJoinColumn($joinColumn); |
964
|
|
|
} |
965
|
|
|
|
966
|
|
|
return $joinTable; |
967
|
|
|
} |
968
|
|
|
|
969
|
|
|
/** |
970
|
|
|
* Parse the given JoinColumn as JoinColumnMetadata |
971
|
|
|
* |
972
|
|
|
* @param Annotation\JoinColumn $joinColumnAnnot |
973
|
|
|
* |
974
|
|
|
* @return Mapping\JoinColumnMetadata |
975
|
|
|
*/ |
976
|
|
|
private function convertJoinColumnAnnotationToJoinColumnMetadata( |
977
|
|
|
Annotation\JoinColumn $joinColumnAnnot |
978
|
|
|
) : Mapping\JoinColumnMetadata |
979
|
|
|
{ |
980
|
|
|
$joinColumn = new Mapping\JoinColumnMetadata(); |
981
|
|
|
|
982
|
|
|
// @todo Remove conditionals for name and referencedColumnName once naming strategy is brought into drivers |
983
|
|
|
if (! empty($joinColumnAnnot->name)) { |
984
|
|
|
$joinColumn->setColumnName($joinColumnAnnot->name); |
985
|
|
|
} |
986
|
|
|
|
987
|
|
|
if (! empty($joinColumnAnnot->referencedColumnName)) { |
988
|
|
|
$joinColumn->setReferencedColumnName($joinColumnAnnot->referencedColumnName); |
989
|
|
|
} |
990
|
|
|
|
991
|
|
|
$joinColumn->setNullable($joinColumnAnnot->nullable); |
992
|
|
|
$joinColumn->setUnique($joinColumnAnnot->unique); |
993
|
|
|
|
994
|
|
|
if (! empty($joinColumnAnnot->fieldName)) { |
995
|
|
|
$joinColumn->setAliasedName($joinColumnAnnot->fieldName); |
996
|
|
|
} |
997
|
|
|
|
998
|
|
|
if (! empty($joinColumnAnnot->columnDefinition)) { |
999
|
|
|
$joinColumn->setColumnDefinition($joinColumnAnnot->columnDefinition); |
1000
|
|
|
} |
1001
|
|
|
|
1002
|
|
|
if ($joinColumnAnnot->onDelete) { |
1003
|
|
|
$joinColumn->setOnDelete(strtoupper($joinColumnAnnot->onDelete)); |
1004
|
|
|
} |
1005
|
|
|
|
1006
|
|
|
return $joinColumn; |
1007
|
|
|
} |
1008
|
|
|
|
1009
|
|
|
/** |
1010
|
|
|
* Parse the given Cache as CacheMetadata |
1011
|
|
|
* |
1012
|
|
|
* @param Annotation\Cache $cacheAnnot |
1013
|
|
|
* @param Mapping\ClassMetadata $metadata |
1014
|
|
|
* @param null|string $fieldName |
1015
|
|
|
* |
1016
|
|
|
* @return Mapping\CacheMetadata |
1017
|
|
|
*/ |
1018
|
|
|
private function convertCacheAnnotationToCacheMetadata( |
1019
|
|
|
Annotation\Cache $cacheAnnot, |
1020
|
|
|
Mapping\ClassMetadata $metadata, |
1021
|
|
|
$fieldName = null |
1022
|
|
|
) : Mapping\CacheMetadata |
1023
|
|
|
{ |
1024
|
|
|
$baseRegion = strtolower(str_replace('\\', '_', $metadata->getRootClassName())); |
1025
|
|
|
$defaultRegion = $baseRegion . ($fieldName ? '__' . $fieldName : ''); |
1026
|
|
|
|
1027
|
|
|
$usage = constant(sprintf('%s::%s', Mapping\CacheUsage::class, $cacheAnnot->usage)); |
1028
|
|
|
$region = $cacheAnnot->region ?: $defaultRegion; |
1029
|
|
|
|
1030
|
|
|
return new Mapping\CacheMetadata($usage, $region); |
1031
|
|
|
} |
1032
|
|
|
|
1033
|
|
|
/** |
1034
|
|
|
* @param Annotation\SqlResultSetMapping $resultSetMapping |
1035
|
|
|
* |
1036
|
|
|
* @return array |
1037
|
|
|
*/ |
1038
|
|
|
private function convertSqlResultSetMapping(Annotation\SqlResultSetMapping $resultSetMapping) |
1039
|
|
|
{ |
1040
|
|
|
$entities = []; |
1041
|
|
|
|
1042
|
|
|
foreach ($resultSetMapping->entities as $entityResultAnnot) { |
1043
|
|
|
$entityResult = [ |
1044
|
|
|
'fields' => [], |
1045
|
|
|
'entityClass' => $entityResultAnnot->entityClass, |
1046
|
|
|
'discriminatorColumn' => $entityResultAnnot->discriminatorColumn, |
1047
|
|
|
]; |
1048
|
|
|
|
1049
|
|
|
foreach ($entityResultAnnot->fields as $fieldResultAnnot) { |
1050
|
|
|
$entityResult['fields'][] = [ |
1051
|
|
|
'name' => $fieldResultAnnot->name, |
1052
|
|
|
'column' => $fieldResultAnnot->column |
1053
|
|
|
]; |
1054
|
|
|
} |
1055
|
|
|
|
1056
|
|
|
$entities[] = $entityResult; |
1057
|
|
|
} |
1058
|
|
|
|
1059
|
|
|
$columns = []; |
1060
|
|
|
|
1061
|
|
|
foreach ($resultSetMapping->columns as $columnResultAnnot) { |
1062
|
|
|
$columns[] = [ |
1063
|
|
|
'name' => $columnResultAnnot->name, |
1064
|
|
|
]; |
1065
|
|
|
} |
1066
|
|
|
|
1067
|
|
|
return [ |
1068
|
|
|
'name' => $resultSetMapping->name, |
1069
|
|
|
'entities' => $entities, |
1070
|
|
|
'columns' => $columns |
1071
|
|
|
]; |
1072
|
|
|
} |
1073
|
|
|
|
1074
|
|
|
private function attachTable( |
1075
|
|
|
array $classAnnotations, |
1076
|
|
|
\ReflectionClass $reflectionClass, |
|
|
|
|
1077
|
|
|
Mapping\ClassMetadata $metadata, |
1078
|
|
|
Mapping\ClassMetadataBuildingContext $metadataBuildingContext |
1079
|
|
|
) : void |
1080
|
|
|
{ |
1081
|
|
|
$parent = $metadata->getParent(); |
1082
|
|
|
|
1083
|
|
|
if ($parent && $parent->inheritanceType === Mapping\InheritanceType::SINGLE_TABLE) { |
|
|
|
|
1084
|
|
|
// Handle the case where a middle mapped super class inherits from a single table inheritance tree. |
1085
|
|
|
do { |
1086
|
|
|
if (! $parent->isMappedSuperclass) { |
|
|
|
|
1087
|
|
|
$metadata->setTable($parent->table); |
|
|
|
|
1088
|
|
|
|
1089
|
|
|
break; |
1090
|
|
|
} |
1091
|
|
|
} while (($parent = $parent->getParent()) !== null); |
1092
|
|
|
|
1093
|
|
|
return; |
1094
|
|
|
} |
1095
|
|
|
|
1096
|
|
|
$namingStrategy = $metadataBuildingContext->getNamingStrategy(); |
1097
|
|
|
$tableMetadata = new Mapping\TableMetadata(); |
1098
|
|
|
|
1099
|
|
|
$tableMetadata->setName($namingStrategy->classToTableName($metadata->getClassName())); |
1100
|
|
|
|
1101
|
|
|
// Evaluate @Table annotation |
1102
|
|
|
if (isset($classAnnotations[Annotation\Table::class])) { |
1103
|
|
|
$tableAnnot = $classAnnotations[Annotation\Table::class]; |
1104
|
|
|
|
1105
|
|
|
$this->convertTableAnnotationToTableMetadata($tableAnnot, $tableMetadata); |
1106
|
|
|
} |
1107
|
|
|
|
1108
|
|
|
$metadata->setTable($tableMetadata); |
1109
|
|
|
} |
1110
|
|
|
|
1111
|
|
|
/** |
1112
|
|
|
* @param array $classAnnotations |
1113
|
|
|
* @param \ReflectionClass $reflectionClass |
1114
|
|
|
* @param Mapping\ClassMetadata $metadata |
1115
|
|
|
* |
1116
|
|
|
* @return void |
1117
|
|
|
* |
1118
|
|
|
* @throws Mapping\MappingException |
1119
|
|
|
*/ |
1120
|
|
|
private function attachDiscriminatorColumn( |
1121
|
|
|
array $classAnnotations, |
1122
|
|
|
\ReflectionClass $reflectionClass, |
|
|
|
|
1123
|
|
|
Mapping\ClassMetadata $metadata |
1124
|
|
|
) : void |
1125
|
|
|
{ |
1126
|
|
|
$discriminatorColumn = new Mapping\DiscriminatorColumnMetadata(); |
1127
|
|
|
|
1128
|
|
|
$discriminatorColumn->setTableName($metadata->getTableName()); |
1129
|
|
|
$discriminatorColumn->setColumnName('dtype'); |
1130
|
|
|
$discriminatorColumn->setType(Type::getType('string')); |
1131
|
|
|
$discriminatorColumn->setLength(255); |
1132
|
|
|
|
1133
|
|
|
// Evaluate DiscriminatorColumn annotation |
1134
|
|
|
if (isset($classAnnotations[Annotation\DiscriminatorColumn::class])) { |
1135
|
|
|
/** @var Annotation\DiscriminatorColumn $discriminatorColumnAnnotation */ |
1136
|
|
|
$discriminatorColumnAnnotation = $classAnnotations[Annotation\DiscriminatorColumn::class]; |
1137
|
|
|
$typeName = ! empty($discriminatorColumnAnnotation->type) |
1138
|
|
|
? $discriminatorColumnAnnotation->type |
1139
|
|
|
: 'string'; |
1140
|
|
|
|
1141
|
|
|
$discriminatorColumn->setType(Type::getType($typeName)); |
1142
|
|
|
$discriminatorColumn->setColumnName($discriminatorColumnAnnotation->name); |
1143
|
|
|
|
1144
|
|
|
if (! empty($discriminatorColumnAnnotation->columnDefinition)) { |
1145
|
|
|
$discriminatorColumn->setColumnDefinition($discriminatorColumnAnnotation->columnDefinition); |
1146
|
|
|
} |
1147
|
|
|
|
1148
|
|
|
if (! empty($discriminatorColumnAnnotation->length)) { |
1149
|
|
|
$discriminatorColumn->setLength($discriminatorColumnAnnotation->length); |
1150
|
|
|
} |
1151
|
|
|
} |
1152
|
|
|
|
1153
|
|
|
$metadata->setDiscriminatorColumn($discriminatorColumn); |
1154
|
|
|
|
1155
|
|
|
// Evaluate DiscriminatorMap annotation |
1156
|
|
|
if (isset($classAnnotations[Annotation\DiscriminatorMap::class])) { |
1157
|
|
|
$discriminatorMapAnnotation = $classAnnotations[Annotation\DiscriminatorMap::class]; |
1158
|
|
|
$discriminatorMap = array_map( |
1159
|
|
|
function ($className) use ($metadata) { |
1160
|
|
|
return $metadata->fullyQualifiedClassName($className); |
1161
|
|
|
}, |
1162
|
|
|
$discriminatorMapAnnotation->value |
1163
|
|
|
); |
1164
|
|
|
|
1165
|
|
|
$metadata->setDiscriminatorMap($discriminatorMap); |
1166
|
|
|
} |
1167
|
|
|
} |
1168
|
|
|
|
1169
|
|
|
/** |
1170
|
|
|
* @param array $classAnnotations |
1171
|
|
|
* @param \ReflectionClass $reflectionClass |
1172
|
|
|
* @param Mapping\ClassMetadata $metadata |
1173
|
|
|
* |
1174
|
|
|
* @return void |
1175
|
|
|
* |
1176
|
|
|
* @throws \UnexpectedValueException |
1177
|
|
|
* @throws Mapping\MappingException |
1178
|
|
|
*/ |
1179
|
|
|
private function attachNamedQueries( |
1180
|
|
|
array $classAnnotations, |
1181
|
|
|
\ReflectionClass $reflectionClass, |
|
|
|
|
1182
|
|
|
Mapping\ClassMetadata $metadata |
1183
|
|
|
) : void |
1184
|
|
|
{ |
1185
|
|
|
// Evaluate @NamedQueries annotation |
1186
|
|
|
if (isset($classAnnotations[Annotation\NamedQueries::class])) { |
1187
|
|
|
$namedQueriesAnnot = $classAnnotations[Annotation\NamedQueries::class]; |
1188
|
|
|
|
1189
|
|
|
if (! is_array($namedQueriesAnnot->value)) { |
1190
|
|
|
throw new \UnexpectedValueException("@NamedQueries should contain an array of @NamedQuery annotations."); |
1191
|
|
|
} |
1192
|
|
|
|
1193
|
|
|
foreach ($namedQueriesAnnot->value as $namedQuery) { |
1194
|
|
|
if (! ($namedQuery instanceof Annotation\NamedQuery)) { |
1195
|
|
|
throw new \UnexpectedValueException("@NamedQueries should contain an array of @NamedQuery annotations."); |
1196
|
|
|
} |
1197
|
|
|
|
1198
|
|
|
$metadata->addNamedQuery($namedQuery->name, $namedQuery->query); |
1199
|
|
|
} |
1200
|
|
|
} |
1201
|
|
|
} |
1202
|
|
|
|
1203
|
|
|
/** |
1204
|
|
|
* @param array $classAnnotations |
1205
|
|
|
* @param \ReflectionClass $reflectionClass |
1206
|
|
|
* @param Mapping\ClassMetadata $metadata |
1207
|
|
|
* |
1208
|
|
|
* @return void |
1209
|
|
|
*/ |
1210
|
|
|
private function attachNamedNativeQueries( |
1211
|
|
|
array $classAnnotations, |
1212
|
|
|
\ReflectionClass $reflectionClass, |
|
|
|
|
1213
|
|
|
Mapping\ClassMetadata $metadata |
1214
|
|
|
) : void |
1215
|
|
|
{ |
1216
|
|
|
// Evaluate @NamedNativeQueries annotation |
1217
|
|
|
if (isset($classAnnotations[Annotation\NamedNativeQueries::class])) { |
1218
|
|
|
$namedNativeQueriesAnnot = $classAnnotations[Annotation\NamedNativeQueries::class]; |
1219
|
|
|
|
1220
|
|
|
foreach ($namedNativeQueriesAnnot->value as $namedNativeQuery) { |
1221
|
|
|
$metadata->addNamedNativeQuery( |
1222
|
|
|
$namedNativeQuery->name, |
1223
|
|
|
$namedNativeQuery->query, |
1224
|
|
|
[ |
1225
|
|
|
'resultClass' => $namedNativeQuery->resultClass, |
1226
|
|
|
'resultSetMapping' => $namedNativeQuery->resultSetMapping, |
1227
|
|
|
] |
1228
|
|
|
); |
1229
|
|
|
} |
1230
|
|
|
} |
1231
|
|
|
|
1232
|
|
|
// Evaluate @SqlResultSetMappings annotation |
1233
|
|
|
if (isset($classAnnotations[Annotation\SqlResultSetMappings::class])) { |
1234
|
|
|
$sqlResultSetMappingsAnnot = $classAnnotations[Annotation\SqlResultSetMappings::class]; |
1235
|
|
|
|
1236
|
|
|
foreach ($sqlResultSetMappingsAnnot->value as $resultSetMapping) { |
1237
|
|
|
$sqlResultSetMapping = $this->convertSqlResultSetMapping($resultSetMapping); |
1238
|
|
|
|
1239
|
|
|
$metadata->addSqlResultSetMapping($sqlResultSetMapping); |
1240
|
|
|
} |
1241
|
|
|
} |
1242
|
|
|
} |
1243
|
|
|
|
1244
|
|
|
/** |
1245
|
|
|
* @param array $classAnnotations |
1246
|
|
|
* @param \ReflectionClass $reflectionClass |
1247
|
|
|
* @param Mapping\ClassMetadata $metadata |
1248
|
|
|
* |
1249
|
|
|
* @return void |
1250
|
|
|
*/ |
1251
|
|
|
private function attachLifecycleCallbacks( |
1252
|
|
|
array $classAnnotations, |
1253
|
|
|
\ReflectionClass $reflectionClass, |
1254
|
|
|
Mapping\ClassMetadata $metadata |
1255
|
|
|
) : void |
1256
|
|
|
{ |
1257
|
|
|
// Evaluate @HasLifecycleCallbacks annotation |
1258
|
|
|
if (isset($classAnnotations[Annotation\HasLifecycleCallbacks::class])) { |
1259
|
|
|
/* @var $method \ReflectionMethod */ |
1260
|
|
|
foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) { |
1261
|
|
|
foreach ($this->getMethodCallbacks($method) as $callback) { |
1262
|
|
|
$metadata->addLifecycleCallback($method->getName(), $callback); |
|
|
|
|
1263
|
|
|
} |
1264
|
|
|
} |
1265
|
|
|
} |
1266
|
|
|
} |
1267
|
|
|
|
1268
|
|
|
/** |
1269
|
|
|
* @param array $classAnnotations |
1270
|
|
|
* @param \ReflectionClass $reflectionClass |
1271
|
|
|
* @param Mapping\ClassMetadata $metadata |
1272
|
|
|
* |
1273
|
|
|
* @return void |
1274
|
|
|
* |
1275
|
|
|
* @throws \ReflectionException |
1276
|
|
|
* @throws Mapping\MappingException |
1277
|
|
|
*/ |
1278
|
|
|
private function attachEntityListeners( |
1279
|
|
|
array $classAnnotations, |
1280
|
|
|
\ReflectionClass $reflectionClass, |
|
|
|
|
1281
|
|
|
Mapping\ClassMetadata $metadata |
1282
|
|
|
) : void |
1283
|
|
|
{ |
1284
|
|
|
// Evaluate @EntityListeners annotation |
1285
|
|
|
if (isset($classAnnotations[Annotation\EntityListeners::class])) { |
1286
|
|
|
/** @var Annotation\EntityListeners $entityListenersAnnot */ |
1287
|
|
|
$entityListenersAnnot = $classAnnotations[Annotation\EntityListeners::class]; |
1288
|
|
|
|
1289
|
|
|
foreach ($entityListenersAnnot->value as $item) { |
1290
|
|
|
$listenerClassName = $metadata->fullyQualifiedClassName($item); |
1291
|
|
|
|
1292
|
|
|
if (! class_exists($listenerClassName)) { |
1293
|
|
|
throw Mapping\MappingException::entityListenerClassNotFound( |
1294
|
|
|
$listenerClassName, |
1295
|
|
|
$metadata->getClassName() |
1296
|
|
|
); |
1297
|
|
|
} |
1298
|
|
|
|
1299
|
|
|
$listenerClass = new \ReflectionClass($listenerClassName); |
1300
|
|
|
|
1301
|
|
|
/* @var $method \ReflectionMethod */ |
1302
|
|
|
foreach ($listenerClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) { |
1303
|
|
|
foreach ($this->getMethodCallbacks($method) as $callback) { |
1304
|
|
|
$metadata->addEntityListener($callback, $listenerClassName, $method->getName()); |
|
|
|
|
1305
|
|
|
} |
1306
|
|
|
} |
1307
|
|
|
} |
1308
|
|
|
} |
1309
|
|
|
} |
1310
|
|
|
|
1311
|
|
|
/** |
1312
|
|
|
* @param array $classAnnotations |
1313
|
|
|
* @param \ReflectionClass $reflectionClass |
1314
|
|
|
* @param Mapping\ClassMetadata $metadata |
1315
|
|
|
* |
1316
|
|
|
* @return void |
1317
|
|
|
* |
1318
|
|
|
* @throws Mapping\MappingException |
1319
|
|
|
*/ |
1320
|
|
|
private function attachPropertyOverrides( |
1321
|
|
|
array $classAnnotations, |
1322
|
|
|
\ReflectionClass $reflectionClass, |
|
|
|
|
1323
|
|
|
Mapping\ClassMetadata $metadata |
1324
|
|
|
) : void |
1325
|
|
|
{ |
1326
|
|
|
// Evaluate AssociationOverrides annotation |
1327
|
|
|
if (isset($classAnnotations[Annotation\AssociationOverrides::class])) { |
1328
|
|
|
$associationOverridesAnnot = $classAnnotations[Annotation\AssociationOverrides::class]; |
1329
|
|
|
|
1330
|
|
|
foreach ($associationOverridesAnnot->value as $associationOverride) { |
1331
|
|
|
$fieldName = $associationOverride->name; |
1332
|
|
|
$property = $metadata->getProperty($fieldName); |
1333
|
|
|
|
1334
|
|
|
if (! $property) { |
1335
|
|
|
throw Mapping\MappingException::invalidOverrideFieldName($metadata->getClassName(), $fieldName); |
1336
|
|
|
} |
1337
|
|
|
|
1338
|
|
|
$existingClass = get_class($property); |
1339
|
|
|
$override = new $existingClass($fieldName); |
1340
|
|
|
|
1341
|
|
|
// Check for JoinColumn/JoinColumns annotations |
1342
|
|
|
if ($associationOverride->joinColumns) { |
1343
|
|
|
$joinColumns = []; |
1344
|
|
|
|
1345
|
|
|
foreach ($associationOverride->joinColumns as $joinColumnAnnot) { |
1346
|
|
|
$joinColumns[] = $this->convertJoinColumnAnnotationToJoinColumnMetadata($joinColumnAnnot); |
1347
|
|
|
} |
1348
|
|
|
|
1349
|
|
|
$override->setJoinColumns($joinColumns); |
1350
|
|
|
} |
1351
|
|
|
|
1352
|
|
|
// Check for JoinTable annotations |
1353
|
|
|
if ($associationOverride->joinTable) { |
1354
|
|
|
$joinTableAnnot = $associationOverride->joinTable; |
1355
|
|
|
$joinTableMetadata = $this->convertJoinTableAnnotationToJoinTableMetadata($joinTableAnnot); |
1356
|
|
|
|
1357
|
|
|
$override->setJoinTable($joinTableMetadata); |
1358
|
|
|
} |
1359
|
|
|
|
1360
|
|
|
// Check for inversedBy |
1361
|
|
|
if ($associationOverride->inversedBy) { |
1362
|
|
|
$override->setInversedBy($associationOverride->inversedBy); |
1363
|
|
|
} |
1364
|
|
|
|
1365
|
|
|
// Check for fetch |
1366
|
|
|
if ($associationOverride->fetch) { |
1367
|
|
|
$override->setFetchMode( |
1368
|
|
|
constant(Mapping\FetchMode::class . '::' . $associationOverride->fetch) |
1369
|
|
|
); |
1370
|
|
|
} |
1371
|
|
|
|
1372
|
|
|
$metadata->setPropertyOverride($override); |
1373
|
|
|
} |
1374
|
|
|
} |
1375
|
|
|
|
1376
|
|
|
// Evaluate AttributeOverrides annotation |
1377
|
|
|
if (isset($classAnnotations[Annotation\AttributeOverrides::class])) { |
1378
|
|
|
$attributeOverridesAnnot = $classAnnotations[Annotation\AttributeOverrides::class]; |
1379
|
|
|
|
1380
|
|
|
foreach ($attributeOverridesAnnot->value as $attributeOverrideAnnot) { |
1381
|
|
|
$fieldMetadata = $this->convertColumnAnnotationToFieldMetadata( |
1382
|
|
|
$attributeOverrideAnnot->column, |
1383
|
|
|
$attributeOverrideAnnot->name, |
1384
|
|
|
false |
1385
|
|
|
); |
1386
|
|
|
|
1387
|
|
|
$metadata->setPropertyOverride($fieldMetadata); |
1388
|
|
|
} |
1389
|
|
|
} |
1390
|
|
|
} |
1391
|
|
|
|
1392
|
|
|
private function attachAssociationPropertyCache( |
1393
|
|
|
array $propertyAnnotations, |
1394
|
|
|
\ReflectionProperty $reflectionProperty, |
1395
|
|
|
Mapping\AssociationMetadata $assocMetadata, |
1396
|
|
|
Mapping\ClassMetadata $metadata |
1397
|
|
|
) : void |
1398
|
|
|
{ |
1399
|
|
|
// Check for Cache |
1400
|
|
|
if (isset($propertyAnnotations[Annotation\Cache::class])) { |
1401
|
|
|
$cacheAnnot = $propertyAnnotations[Annotation\Cache::class]; |
1402
|
|
|
$cacheMetadata = $this->convertCacheAnnotationToCacheMetadata( |
1403
|
|
|
$cacheAnnot, |
1404
|
|
|
$metadata, |
1405
|
|
|
$reflectionProperty->getName() |
1406
|
|
|
); |
1407
|
|
|
|
1408
|
|
|
$assocMetadata->setCache($cacheMetadata); |
1409
|
|
|
} |
1410
|
|
|
} |
1411
|
|
|
|
1412
|
|
|
/** |
1413
|
|
|
* Attempts to resolve the cascade modes. |
1414
|
|
|
* |
1415
|
|
|
* @param string $className The class name. |
1416
|
|
|
* @param string $fieldName The field name. |
1417
|
|
|
* @param array $originalCascades The original unprocessed field cascades. |
1418
|
|
|
* |
1419
|
|
|
* @return array The processed field cascades. |
1420
|
|
|
* |
1421
|
|
|
* @throws Mapping\MappingException If a cascade option is not valid. |
1422
|
|
|
*/ |
1423
|
|
|
private function getCascade(string $className, string $fieldName, array $originalCascades) |
1424
|
|
|
{ |
1425
|
|
|
$cascadeTypes = ['remove', 'persist', 'refresh', 'merge', 'detach']; |
1426
|
|
|
$cascades = array_map('strtolower', $originalCascades); |
1427
|
|
|
|
1428
|
|
|
if (in_array('all', $cascades)) { |
1429
|
|
|
$cascades = $cascadeTypes; |
1430
|
|
|
} |
1431
|
|
|
|
1432
|
|
|
if (count($cascades) !== count(array_intersect($cascades, $cascadeTypes))) { |
1433
|
|
|
$diffCascades = array_diff($cascades, array_intersect($cascades, $cascadeTypes)); |
1434
|
|
|
|
1435
|
|
|
throw Mapping\MappingException::invalidCascadeOption($diffCascades, $className, $fieldName); |
1436
|
|
|
} |
1437
|
|
|
|
1438
|
|
|
return $cascades; |
1439
|
|
|
} |
1440
|
|
|
|
1441
|
|
|
/** |
1442
|
|
|
* Attempts to resolve the fetch mode. |
1443
|
|
|
* |
1444
|
|
|
* @param string $className The class name. |
1445
|
|
|
* @param string $fetchMode The fetch mode. |
1446
|
|
|
* |
1447
|
|
|
* @return string The fetch mode as defined in ClassMetadata. |
1448
|
|
|
* |
1449
|
|
|
* @throws Mapping\MappingException If the fetch mode is not valid. |
1450
|
|
|
*/ |
1451
|
|
|
private function getFetchMode($className, $fetchMode) : string |
1452
|
|
|
{ |
1453
|
|
|
$fetchModeConstant = sprintf('%s::%s', Mapping\FetchMode::class, $fetchMode); |
1454
|
|
|
|
1455
|
|
|
if ( ! defined($fetchModeConstant)) { |
1456
|
|
|
throw Mapping\MappingException::invalidFetchMode($className, $fetchMode); |
1457
|
|
|
} |
1458
|
|
|
|
1459
|
|
|
return constant($fetchModeConstant); |
1460
|
|
|
} |
1461
|
|
|
|
1462
|
|
|
/** |
1463
|
|
|
* Parses the given method. |
1464
|
|
|
* |
1465
|
|
|
* @param \ReflectionMethod $method |
1466
|
|
|
* |
1467
|
|
|
* @return array |
1468
|
|
|
*/ |
1469
|
|
|
private function getMethodCallbacks(\ReflectionMethod $method) : array |
1470
|
|
|
{ |
1471
|
|
|
$annotations = $this->getMethodAnnotations($method); |
1472
|
|
|
$events = [ |
1473
|
|
|
Events::prePersist => Annotation\PrePersist::class, |
1474
|
|
|
Events::postPersist => Annotation\PostPersist::class, |
1475
|
|
|
Events::preUpdate => Annotation\PreUpdate::class, |
1476
|
|
|
Events::postUpdate => Annotation\PostUpdate::class, |
1477
|
|
|
Events::preRemove => Annotation\PreRemove::class, |
1478
|
|
|
Events::postRemove => Annotation\PostRemove::class, |
1479
|
|
|
Events::postLoad => Annotation\PostLoad::class, |
1480
|
|
|
Events::preFlush => Annotation\PreFlush::class, |
1481
|
|
|
]; |
1482
|
|
|
|
1483
|
|
|
// Check for callbacks |
1484
|
|
|
$callbacks = []; |
1485
|
|
|
|
1486
|
|
|
foreach ($events as $eventName => $annotationClassName) { |
1487
|
|
|
if (isset($annotations[$annotationClassName]) || $method->getName() === $eventName) { |
|
|
|
|
1488
|
|
|
$callbacks[] = $eventName; |
1489
|
|
|
} |
1490
|
|
|
} |
1491
|
|
|
|
1492
|
|
|
return $callbacks; |
1493
|
|
|
} |
1494
|
|
|
|
1495
|
|
|
/** |
1496
|
|
|
* @param \ReflectionClass $reflectionClass |
1497
|
|
|
* |
1498
|
|
|
* @return array |
1499
|
|
|
*/ |
1500
|
|
|
private function getClassAnnotations(\ReflectionClass $reflectionClass) : array |
1501
|
|
|
{ |
1502
|
|
|
$classAnnotations = $this->reader->getClassAnnotations($reflectionClass); |
1503
|
|
|
|
1504
|
|
|
foreach ($classAnnotations as $key => $annot) { |
1505
|
|
|
if (! is_numeric($key)) { |
1506
|
|
|
continue; |
1507
|
|
|
} |
1508
|
|
|
|
1509
|
|
|
$classAnnotations[get_class($annot)] = $annot; |
1510
|
|
|
} |
1511
|
|
|
|
1512
|
|
|
return $classAnnotations; |
1513
|
|
|
} |
1514
|
|
|
|
1515
|
|
|
/** |
1516
|
|
|
* @param \ReflectionProperty $reflectionProperty |
1517
|
|
|
* |
1518
|
|
|
* @return array |
1519
|
|
|
*/ |
1520
|
|
|
private function getPropertyAnnotations(\ReflectionProperty $reflectionProperty) : array |
1521
|
|
|
{ |
1522
|
|
|
$propertyAnnotations = $this->reader->getPropertyAnnotations($reflectionProperty); |
1523
|
|
|
|
1524
|
|
|
foreach ($propertyAnnotations as $key => $annot) { |
1525
|
|
|
if (! is_numeric($key)) { |
1526
|
|
|
continue; |
1527
|
|
|
} |
1528
|
|
|
|
1529
|
|
|
$propertyAnnotations[get_class($annot)] = $annot; |
1530
|
|
|
} |
1531
|
|
|
|
1532
|
|
|
return $propertyAnnotations; |
1533
|
|
|
} |
1534
|
|
|
|
1535
|
|
|
/** |
1536
|
|
|
* @param \ReflectionMethod $reflectionMethod |
1537
|
|
|
* |
1538
|
|
|
* @return array |
1539
|
|
|
*/ |
1540
|
|
|
private function getMethodAnnotations(\ReflectionMethod $reflectionMethod) : array |
1541
|
|
|
{ |
1542
|
|
|
$methodAnnotations = $this->reader->getMethodAnnotations($reflectionMethod); |
1543
|
|
|
|
1544
|
|
|
foreach ($methodAnnotations as $key => $annot) { |
1545
|
|
|
if (! is_numeric($key)) { |
1546
|
|
|
continue; |
1547
|
|
|
} |
1548
|
|
|
|
1549
|
|
|
$methodAnnotations[get_class($annot)] = $annot; |
1550
|
|
|
} |
1551
|
|
|
|
1552
|
|
|
return $methodAnnotations; |
1553
|
|
|
} |
1554
|
|
|
|
1555
|
|
|
/** |
1556
|
|
|
* Factory method for the Annotation Driver. |
1557
|
|
|
* |
1558
|
|
|
* @param array|string $paths |
1559
|
|
|
* @param AnnotationReader|null $reader |
1560
|
|
|
* |
1561
|
|
|
* @return AnnotationDriver |
1562
|
|
|
*/ |
1563
|
|
|
static public function create($paths = [], AnnotationReader $reader = null) |
|
|
|
|
1564
|
|
|
{ |
1565
|
|
|
if ($reader == null) { |
1566
|
|
|
$reader = new AnnotationReader(); |
1567
|
|
|
} |
1568
|
|
|
|
1569
|
|
|
return new self($reader, $paths); |
1570
|
|
|
} |
1571
|
|
|
} |
1572
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.