1
|
|
|
<?php |
2
|
|
|
/* |
3
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
4
|
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
5
|
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
6
|
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
7
|
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
8
|
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
9
|
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
10
|
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
11
|
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
12
|
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
13
|
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
14
|
|
|
* |
15
|
|
|
* This software consists of voluntary contributions made by many individuals |
16
|
|
|
* and is licensed under the MIT license. For more information, see |
17
|
|
|
* <http://www.doctrine-project.org>. |
18
|
|
|
*/ |
19
|
|
|
|
20
|
|
|
namespace Doctrine\ORM\Mapping\Driver; |
21
|
|
|
|
22
|
|
|
use Doctrine\Common\Annotations\AnnotationReader; |
23
|
|
|
use Doctrine\Common\Persistence\Mapping\ClassMetadata; |
24
|
|
|
use Doctrine\Common\Persistence\Mapping\Driver\AnnotationDriver as AbstractAnnotationDriver; |
25
|
|
|
use Doctrine\ORM\Events; |
26
|
|
|
use Doctrine\ORM\Mapping; |
27
|
|
|
use Doctrine\ORM\Mapping\Builder\EntityListenerBuilder; |
28
|
|
|
use Doctrine\ORM\Mapping\MappingException; |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* The AnnotationDriver reads the mapping metadata from docblock annotations. |
32
|
|
|
* |
33
|
|
|
* @since 2.0 |
34
|
|
|
* @author Benjamin Eberlei <[email protected]> |
35
|
|
|
* @author Guilherme Blanco <[email protected]> |
36
|
|
|
* @author Jonathan H. Wage <[email protected]> |
37
|
|
|
* @author Roman Borschel <[email protected]> |
38
|
|
|
*/ |
39
|
|
|
class AnnotationDriver extends AbstractAnnotationDriver |
40
|
|
|
{ |
41
|
|
|
/** |
42
|
|
|
* {@inheritDoc} |
43
|
|
|
*/ |
44
|
|
|
protected $entityAnnotationClasses = [ |
45
|
|
|
Mapping\Entity::class => 1, |
46
|
|
|
Mapping\MappedSuperclass::class => 2, |
47
|
|
|
]; |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* {@inheritDoc} |
51
|
|
|
*/ |
52
|
393 |
|
public function loadMetadataForClass($className, ClassMetadata $metadata) |
53
|
|
|
{ |
54
|
|
|
/* @var $metadata \Doctrine\ORM\Mapping\ClassMetadataInfo */ |
55
|
393 |
|
$class = $metadata->getReflectionClass(); |
56
|
|
|
|
57
|
393 |
|
if ( ! $class) { |
58
|
|
|
// this happens when running annotation driver in combination with |
59
|
|
|
// static reflection services. This is not the nicest fix |
60
|
1 |
|
$class = new \ReflectionClass($metadata->name); |
61
|
|
|
} |
62
|
|
|
|
63
|
393 |
|
$classAnnotations = $this->reader->getClassAnnotations($class); |
64
|
|
|
|
65
|
393 |
|
if ($classAnnotations) { |
|
|
|
|
66
|
390 |
|
foreach ($classAnnotations as $key => $annot) { |
67
|
390 |
|
if ( ! is_numeric($key)) { |
68
|
|
|
continue; |
69
|
|
|
} |
70
|
|
|
|
71
|
390 |
|
$classAnnotations[get_class($annot)] = $annot; |
72
|
|
|
} |
73
|
|
|
} |
74
|
|
|
|
75
|
|
|
// Evaluate Entity annotation |
76
|
393 |
|
if (isset($classAnnotations[Mapping\Entity::class])) { |
77
|
385 |
|
$entityAnnot = $classAnnotations[Mapping\Entity::class]; |
78
|
385 |
|
if ($entityAnnot->repositoryClass !== null) { |
79
|
8 |
|
$metadata->setCustomRepositoryClass($entityAnnot->repositoryClass); |
80
|
|
|
} |
81
|
|
|
|
82
|
385 |
|
if ($entityAnnot->readOnly) { |
83
|
385 |
|
$metadata->markReadOnly(); |
84
|
|
|
} |
85
|
45 |
|
} else if (isset($classAnnotations[Mapping\MappedSuperclass::class])) { |
86
|
32 |
|
$mappedSuperclassAnnot = $classAnnotations[Mapping\MappedSuperclass::class]; |
87
|
|
|
|
88
|
32 |
|
$metadata->setCustomRepositoryClass($mappedSuperclassAnnot->repositoryClass); |
89
|
32 |
|
$metadata->isMappedSuperclass = true; |
90
|
15 |
|
} else if (isset($classAnnotations[Mapping\Embeddable::class])) { |
91
|
12 |
|
$metadata->isEmbeddedClass = true; |
92
|
|
|
} else { |
93
|
3 |
|
throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className); |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
// Evaluate Table annotation |
97
|
390 |
|
if (isset($classAnnotations[Mapping\Table::class])) { |
98
|
204 |
|
$tableAnnot = $classAnnotations[Mapping\Table::class]; |
99
|
|
|
$primaryTable = [ |
100
|
204 |
|
'name' => $tableAnnot->name, |
101
|
204 |
|
'schema' => $tableAnnot->schema |
102
|
|
|
]; |
103
|
|
|
|
104
|
204 |
|
if ($tableAnnot->indexes !== null) { |
105
|
16 |
|
foreach ($tableAnnot->indexes as $indexAnnot) { |
106
|
16 |
|
$index = ['columns' => $indexAnnot->columns]; |
107
|
|
|
|
108
|
16 |
|
if ( ! empty($indexAnnot->flags)) { |
109
|
1 |
|
$index['flags'] = $indexAnnot->flags; |
110
|
|
|
} |
111
|
|
|
|
112
|
16 |
|
if ( ! empty($indexAnnot->options)) { |
113
|
1 |
|
$index['options'] = $indexAnnot->options; |
114
|
|
|
} |
115
|
|
|
|
116
|
16 |
|
if ( ! empty($indexAnnot->name)) { |
117
|
15 |
|
$primaryTable['indexes'][$indexAnnot->name] = $index; |
118
|
|
|
} else { |
119
|
16 |
|
$primaryTable['indexes'][] = $index; |
120
|
|
|
} |
121
|
|
|
} |
122
|
|
|
} |
123
|
|
|
|
124
|
204 |
|
if ($tableAnnot->uniqueConstraints !== null) { |
125
|
9 |
|
foreach ($tableAnnot->uniqueConstraints as $uniqueConstraintAnnot) { |
126
|
9 |
|
$uniqueConstraint = ['columns' => $uniqueConstraintAnnot->columns]; |
127
|
|
|
|
128
|
9 |
|
if ( ! empty($uniqueConstraintAnnot->options)) { |
129
|
3 |
|
$uniqueConstraint['options'] = $uniqueConstraintAnnot->options; |
130
|
|
|
} |
131
|
|
|
|
132
|
9 |
|
if ( ! empty($uniqueConstraintAnnot->name)) { |
133
|
9 |
|
$primaryTable['uniqueConstraints'][$uniqueConstraintAnnot->name] = $uniqueConstraint; |
134
|
|
|
} else { |
135
|
9 |
|
$primaryTable['uniqueConstraints'][] = $uniqueConstraint; |
136
|
|
|
} |
137
|
|
|
} |
138
|
|
|
} |
139
|
|
|
|
140
|
204 |
|
if ($tableAnnot->options) { |
141
|
6 |
|
$primaryTable['options'] = $tableAnnot->options; |
142
|
|
|
} |
143
|
|
|
|
144
|
204 |
|
$metadata->setPrimaryTable($primaryTable); |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
// Evaluate @Cache annotation |
148
|
390 |
|
if (isset($classAnnotations[Mapping\Cache::class])) { |
149
|
16 |
|
$cacheAnnot = $classAnnotations[Mapping\Cache::class]; |
150
|
|
|
$cacheMap = [ |
151
|
16 |
|
'region' => $cacheAnnot->region, |
152
|
16 |
|
'usage' => constant('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $cacheAnnot->usage), |
153
|
|
|
]; |
154
|
|
|
|
155
|
16 |
|
$metadata->enableCache($cacheMap); |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
// Evaluate NamedNativeQueries annotation |
159
|
390 |
|
if (isset($classAnnotations[Mapping\NamedNativeQueries::class])) { |
160
|
16 |
|
$namedNativeQueriesAnnot = $classAnnotations[Mapping\NamedNativeQueries::class]; |
161
|
|
|
|
162
|
16 |
|
foreach ($namedNativeQueriesAnnot->value as $namedNativeQuery) { |
163
|
16 |
|
$metadata->addNamedNativeQuery( |
164
|
|
|
[ |
165
|
16 |
|
'name' => $namedNativeQuery->name, |
166
|
16 |
|
'query' => $namedNativeQuery->query, |
167
|
16 |
|
'resultClass' => $namedNativeQuery->resultClass, |
168
|
16 |
|
'resultSetMapping' => $namedNativeQuery->resultSetMapping, |
169
|
|
|
] |
170
|
|
|
); |
171
|
|
|
} |
172
|
|
|
} |
173
|
|
|
|
174
|
|
|
// Evaluate SqlResultSetMappings annotation |
175
|
390 |
|
if (isset($classAnnotations[Mapping\SqlResultSetMappings::class])) { |
176
|
16 |
|
$sqlResultSetMappingsAnnot = $classAnnotations[Mapping\SqlResultSetMappings::class]; |
177
|
|
|
|
178
|
16 |
|
foreach ($sqlResultSetMappingsAnnot->value as $resultSetMapping) { |
179
|
16 |
|
$entities = []; |
180
|
16 |
|
$columns = []; |
181
|
16 |
|
foreach ($resultSetMapping->entities as $entityResultAnnot) { |
182
|
|
|
$entityResult = [ |
183
|
16 |
|
'fields' => [], |
184
|
16 |
|
'entityClass' => $entityResultAnnot->entityClass, |
185
|
16 |
|
'discriminatorColumn' => $entityResultAnnot->discriminatorColumn, |
186
|
|
|
]; |
187
|
|
|
|
188
|
16 |
|
foreach ($entityResultAnnot->fields as $fieldResultAnnot) { |
189
|
16 |
|
$entityResult['fields'][] = [ |
190
|
16 |
|
'name' => $fieldResultAnnot->name, |
191
|
16 |
|
'column' => $fieldResultAnnot->column |
192
|
|
|
]; |
193
|
|
|
} |
194
|
|
|
|
195
|
16 |
|
$entities[] = $entityResult; |
196
|
|
|
} |
197
|
|
|
|
198
|
16 |
|
foreach ($resultSetMapping->columns as $columnResultAnnot) { |
199
|
10 |
|
$columns[] = [ |
200
|
10 |
|
'name' => $columnResultAnnot->name, |
201
|
|
|
]; |
202
|
|
|
} |
203
|
|
|
|
204
|
16 |
|
$metadata->addSqlResultSetMapping( |
205
|
|
|
[ |
206
|
16 |
|
'name' => $resultSetMapping->name, |
207
|
16 |
|
'entities' => $entities, |
208
|
16 |
|
'columns' => $columns |
209
|
|
|
] |
210
|
|
|
); |
211
|
|
|
} |
212
|
|
|
} |
213
|
|
|
|
214
|
|
|
// Evaluate NamedQueries annotation |
215
|
390 |
|
if (isset($classAnnotations[Mapping\NamedQueries::class])) { |
216
|
11 |
|
$namedQueriesAnnot = $classAnnotations[Mapping\NamedQueries::class]; |
217
|
|
|
|
218
|
11 |
|
if ( ! is_array($namedQueriesAnnot->value)) { |
219
|
|
|
throw new \UnexpectedValueException("@NamedQueries should contain an array of @NamedQuery annotations."); |
220
|
|
|
} |
221
|
|
|
|
222
|
11 |
|
foreach ($namedQueriesAnnot->value as $namedQuery) { |
223
|
11 |
|
if ( ! ($namedQuery instanceof Mapping\NamedQuery)) { |
224
|
|
|
throw new \UnexpectedValueException("@NamedQueries should contain an array of @NamedQuery annotations."); |
225
|
|
|
} |
226
|
11 |
|
$metadata->addNamedQuery( |
227
|
|
|
[ |
228
|
11 |
|
'name' => $namedQuery->name, |
229
|
11 |
|
'query' => $namedQuery->query |
230
|
|
|
] |
231
|
|
|
); |
232
|
|
|
} |
233
|
|
|
} |
234
|
|
|
|
235
|
|
|
// Evaluate InheritanceType annotation |
236
|
390 |
|
if (isset($classAnnotations[Mapping\InheritanceType::class])) { |
237
|
75 |
|
$inheritanceTypeAnnot = $classAnnotations[Mapping\InheritanceType::class]; |
238
|
|
|
|
239
|
75 |
|
$metadata->setInheritanceType( |
240
|
75 |
|
constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . $inheritanceTypeAnnot->value) |
241
|
|
|
); |
242
|
|
|
|
243
|
75 |
|
if ($metadata->inheritanceType != Mapping\ClassMetadata::INHERITANCE_TYPE_NONE) { |
244
|
|
|
// Evaluate DiscriminatorColumn annotation |
245
|
75 |
|
if (isset($classAnnotations[Mapping\DiscriminatorColumn::class])) { |
246
|
57 |
|
$discrColumnAnnot = $classAnnotations[Mapping\DiscriminatorColumn::class]; |
247
|
|
|
|
248
|
57 |
|
$metadata->setDiscriminatorColumn( |
249
|
|
|
[ |
250
|
57 |
|
'name' => $discrColumnAnnot->name, |
251
|
57 |
|
'type' => $discrColumnAnnot->type ?: 'string', |
252
|
57 |
|
'length' => $discrColumnAnnot->length ?: 255, |
253
|
57 |
|
'columnDefinition' => $discrColumnAnnot->columnDefinition, |
254
|
|
|
] |
255
|
|
|
); |
256
|
|
|
} else { |
257
|
21 |
|
$metadata->setDiscriminatorColumn(['name' => 'dtype', 'type' => 'string', 'length' => 255]); |
258
|
|
|
} |
259
|
|
|
|
260
|
|
|
// Evaluate DiscriminatorMap annotation |
261
|
75 |
|
if (isset($classAnnotations[Mapping\DiscriminatorMap::class])) { |
262
|
74 |
|
$discrMapAnnot = $classAnnotations[Mapping\DiscriminatorMap::class]; |
263
|
74 |
|
$metadata->setDiscriminatorMap($discrMapAnnot->value); |
264
|
|
|
} |
265
|
|
|
} |
266
|
|
|
} |
267
|
|
|
|
268
|
|
|
|
269
|
|
|
// Evaluate DoctrineChangeTrackingPolicy annotation |
270
|
390 |
|
if (isset($classAnnotations[Mapping\ChangeTrackingPolicy::class])) { |
271
|
5 |
|
$changeTrackingAnnot = $classAnnotations[Mapping\ChangeTrackingPolicy::class]; |
272
|
5 |
|
$metadata->setChangeTrackingPolicy(constant('Doctrine\ORM\Mapping\ClassMetadata::CHANGETRACKING_' . $changeTrackingAnnot->value)); |
273
|
|
|
} |
274
|
|
|
|
275
|
|
|
// Evaluate annotations on properties/fields |
276
|
|
|
/* @var $property \ReflectionProperty */ |
277
|
390 |
|
foreach ($class->getProperties() as $property) { |
278
|
388 |
|
if ($metadata->isMappedSuperclass && ! $property->isPrivate() |
279
|
|
|
|| |
280
|
388 |
|
$metadata->isInheritedField($property->name) |
281
|
|
|
|| |
282
|
388 |
|
$metadata->isInheritedAssociation($property->name) |
283
|
|
|
|| |
284
|
388 |
|
$metadata->isInheritedEmbeddedClass($property->name)) { |
285
|
78 |
|
continue; |
286
|
|
|
} |
287
|
|
|
|
288
|
388 |
|
$mapping = []; |
289
|
388 |
|
$mapping['fieldName'] = $property->getName(); |
290
|
|
|
|
291
|
|
|
// Evaluate @Cache annotation |
292
|
388 |
|
if (($cacheAnnot = $this->reader->getPropertyAnnotation($property, Mapping\Cache::class)) !== null) { |
293
|
13 |
|
$mapping['cache'] = $metadata->getAssociationCacheDefaults( |
294
|
13 |
|
$mapping['fieldName'], |
295
|
|
|
[ |
296
|
13 |
|
'usage' => constant('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $cacheAnnot->usage), |
297
|
13 |
|
'region' => $cacheAnnot->region, |
298
|
|
|
] |
299
|
|
|
); |
300
|
|
|
} |
301
|
|
|
// Check for JoinColumn/JoinColumns annotations |
302
|
387 |
|
$joinColumns = []; |
303
|
|
|
|
304
|
387 |
|
if ($joinColumnAnnot = $this->reader->getPropertyAnnotation($property, Mapping\JoinColumn::class)) { |
305
|
148 |
|
$joinColumns[] = $this->joinColumnToArray($joinColumnAnnot); |
306
|
386 |
|
} else if ($joinColumnsAnnot = $this->reader->getPropertyAnnotation($property, Mapping\JoinColumns::class)) { |
307
|
19 |
|
foreach ($joinColumnsAnnot->value as $joinColumn) { |
308
|
19 |
|
$joinColumns[] = $this->joinColumnToArray($joinColumn); |
309
|
|
|
} |
310
|
|
|
} |
311
|
|
|
|
312
|
|
|
// Field can only be annotated with one of: |
313
|
|
|
// @Column, @OneToOne, @OneToMany, @ManyToOne, @ManyToMany |
|
|
|
|
314
|
387 |
|
if ($columnAnnot = $this->reader->getPropertyAnnotation($property, Mapping\Column::class)) { |
315
|
379 |
|
if ($columnAnnot->type == null) { |
316
|
|
|
throw MappingException::propertyTypeIsRequired($className, $property->getName()); |
317
|
|
|
} |
318
|
|
|
|
319
|
379 |
|
$mapping = $this->columnToArray($property->getName(), $columnAnnot); |
320
|
|
|
|
321
|
379 |
|
if ($idAnnot = $this->reader->getPropertyAnnotation($property, Mapping\Id::class)) { |
|
|
|
|
322
|
372 |
|
$mapping['id'] = true; |
323
|
|
|
} |
324
|
|
|
|
325
|
379 |
|
if ($generatedValueAnnot = $this->reader->getPropertyAnnotation($property, Mapping\GeneratedValue::class)) { |
326
|
318 |
|
$metadata->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_' . $generatedValueAnnot->strategy)); |
327
|
|
|
} |
328
|
|
|
|
329
|
379 |
|
if ($this->reader->getPropertyAnnotation($property, Mapping\Version::class)) { |
330
|
16 |
|
$metadata->setVersionMapping($mapping); |
331
|
|
|
} |
332
|
|
|
|
333
|
379 |
|
$metadata->mapField($mapping); |
334
|
|
|
|
335
|
|
|
// Check for SequenceGenerator/TableGenerator definition |
336
|
379 |
|
if ($seqGeneratorAnnot = $this->reader->getPropertyAnnotation($property, Mapping\SequenceGenerator::class)) { |
337
|
9 |
|
$metadata->setSequenceGeneratorDefinition( |
338
|
|
|
[ |
339
|
9 |
|
'sequenceName' => $seqGeneratorAnnot->sequenceName, |
340
|
9 |
|
'allocationSize' => $seqGeneratorAnnot->allocationSize, |
341
|
9 |
|
'initialValue' => $seqGeneratorAnnot->initialValue |
342
|
|
|
] |
343
|
|
|
); |
344
|
376 |
|
} else if ($this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\TableGenerator')) { |
345
|
|
|
throw MappingException::tableIdGeneratorNotImplemented($className); |
346
|
376 |
|
} else if ($customGeneratorAnnot = $this->reader->getPropertyAnnotation($property, Mapping\CustomIdGenerator::class)) { |
347
|
3 |
|
$metadata->setCustomGeneratorDefinition( |
348
|
|
|
[ |
349
|
379 |
|
'class' => $customGeneratorAnnot->class |
350
|
|
|
] |
351
|
|
|
); |
352
|
|
|
} |
353
|
273 |
|
} else if ($oneToOneAnnot = $this->reader->getPropertyAnnotation($property, Mapping\OneToOne::class)) { |
354
|
118 |
|
if ($idAnnot = $this->reader->getPropertyAnnotation($property, Mapping\Id::class)) { |
|
|
|
|
355
|
9 |
|
$mapping['id'] = true; |
356
|
|
|
} |
357
|
|
|
|
358
|
118 |
|
$mapping['targetEntity'] = $oneToOneAnnot->targetEntity; |
359
|
118 |
|
$mapping['joinColumns'] = $joinColumns; |
360
|
118 |
|
$mapping['mappedBy'] = $oneToOneAnnot->mappedBy; |
361
|
118 |
|
$mapping['inversedBy'] = $oneToOneAnnot->inversedBy; |
362
|
118 |
|
$mapping['cascade'] = $oneToOneAnnot->cascade; |
363
|
118 |
|
$mapping['orphanRemoval'] = $oneToOneAnnot->orphanRemoval; |
364
|
118 |
|
$mapping['fetch'] = $this->getFetchMode($className, $oneToOneAnnot->fetch); |
365
|
118 |
|
$metadata->mapOneToOne($mapping); |
366
|
222 |
|
} else if ($oneToManyAnnot = $this->reader->getPropertyAnnotation($property, Mapping\OneToMany::class)) { |
367
|
111 |
|
$mapping['mappedBy'] = $oneToManyAnnot->mappedBy; |
368
|
111 |
|
$mapping['targetEntity'] = $oneToManyAnnot->targetEntity; |
369
|
111 |
|
$mapping['cascade'] = $oneToManyAnnot->cascade; |
370
|
111 |
|
$mapping['indexBy'] = $oneToManyAnnot->indexBy; |
371
|
111 |
|
$mapping['orphanRemoval'] = $oneToManyAnnot->orphanRemoval; |
372
|
111 |
|
$mapping['fetch'] = $this->getFetchMode($className, $oneToManyAnnot->fetch); |
373
|
|
|
|
374
|
111 |
|
if ($orderByAnnot = $this->reader->getPropertyAnnotation($property, Mapping\OrderBy::class)) { |
375
|
16 |
|
$mapping['orderBy'] = $orderByAnnot->value; |
376
|
|
|
} |
377
|
|
|
|
378
|
111 |
|
$metadata->mapOneToMany($mapping); |
379
|
219 |
|
} else if ($manyToOneAnnot = $this->reader->getPropertyAnnotation($property, Mapping\ManyToOne::class)) { |
380
|
139 |
|
if ($idAnnot = $this->reader->getPropertyAnnotation($property, Mapping\Id::class)) { |
|
|
|
|
381
|
31 |
|
$mapping['id'] = true; |
382
|
|
|
} |
383
|
|
|
|
384
|
139 |
|
$mapping['joinColumns'] = $joinColumns; |
385
|
139 |
|
$mapping['cascade'] = $manyToOneAnnot->cascade; |
386
|
139 |
|
$mapping['inversedBy'] = $manyToOneAnnot->inversedBy; |
387
|
139 |
|
$mapping['targetEntity'] = $manyToOneAnnot->targetEntity; |
388
|
139 |
|
$mapping['fetch'] = $this->getFetchMode($className, $manyToOneAnnot->fetch); |
389
|
139 |
|
$metadata->mapManyToOne($mapping); |
390
|
126 |
|
} else if ($manyToManyAnnot = $this->reader->getPropertyAnnotation($property, Mapping\ManyToMany::class)) { |
391
|
95 |
|
$joinTable = []; |
392
|
|
|
|
393
|
95 |
|
if ($joinTableAnnot = $this->reader->getPropertyAnnotation($property, Mapping\JoinTable::class)) { |
394
|
|
|
$joinTable = [ |
395
|
75 |
|
'name' => $joinTableAnnot->name, |
396
|
75 |
|
'schema' => $joinTableAnnot->schema |
397
|
|
|
]; |
398
|
|
|
|
399
|
75 |
|
foreach ($joinTableAnnot->joinColumns as $joinColumn) { |
400
|
74 |
|
$joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumn); |
401
|
|
|
} |
402
|
|
|
|
403
|
75 |
|
foreach ($joinTableAnnot->inverseJoinColumns as $joinColumn) { |
404
|
74 |
|
$joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumn); |
405
|
|
|
} |
406
|
|
|
} |
407
|
|
|
|
408
|
95 |
|
$mapping['joinTable'] = $joinTable; |
409
|
95 |
|
$mapping['targetEntity'] = $manyToManyAnnot->targetEntity; |
410
|
95 |
|
$mapping['mappedBy'] = $manyToManyAnnot->mappedBy; |
411
|
95 |
|
$mapping['inversedBy'] = $manyToManyAnnot->inversedBy; |
412
|
95 |
|
$mapping['cascade'] = $manyToManyAnnot->cascade; |
413
|
95 |
|
$mapping['indexBy'] = $manyToManyAnnot->indexBy; |
414
|
95 |
|
$mapping['orphanRemoval'] = $manyToManyAnnot->orphanRemoval; |
415
|
95 |
|
$mapping['fetch'] = $this->getFetchMode($className, $manyToManyAnnot->fetch); |
416
|
|
|
|
417
|
95 |
|
if ($orderByAnnot = $this->reader->getPropertyAnnotation($property, Mapping\OrderBy::class)) { |
418
|
3 |
|
$mapping['orderBy'] = $orderByAnnot->value; |
419
|
|
|
} |
420
|
|
|
|
421
|
95 |
|
$metadata->mapManyToMany($mapping); |
422
|
51 |
|
} else if ($embeddedAnnot = $this->reader->getPropertyAnnotation($property, Mapping\Embedded::class)) { |
423
|
13 |
|
$mapping['class'] = $embeddedAnnot->class; |
424
|
13 |
|
$mapping['columnPrefix'] = $embeddedAnnot->columnPrefix; |
425
|
|
|
|
426
|
386 |
|
$metadata->mapEmbedded($mapping); |
427
|
|
|
} |
428
|
|
|
} |
429
|
|
|
|
430
|
|
|
// Evaluate AssociationOverrides annotation |
431
|
388 |
|
if (isset($classAnnotations[Mapping\AssociationOverrides::class])) { |
432
|
5 |
|
$associationOverridesAnnot = $classAnnotations[Mapping\AssociationOverrides::class]; |
433
|
|
|
|
434
|
5 |
|
foreach ($associationOverridesAnnot->value as $associationOverride) { |
435
|
5 |
|
$override = []; |
436
|
5 |
|
$fieldName = $associationOverride->name; |
437
|
|
|
|
438
|
|
|
// Check for JoinColumn/JoinColumns annotations |
439
|
5 |
|
if ($associationOverride->joinColumns) { |
440
|
3 |
|
$joinColumns = []; |
441
|
|
|
|
442
|
3 |
|
foreach ($associationOverride->joinColumns as $joinColumn) { |
443
|
3 |
|
$joinColumns[] = $this->joinColumnToArray($joinColumn); |
444
|
|
|
} |
445
|
|
|
|
446
|
3 |
|
$override['joinColumns'] = $joinColumns; |
447
|
|
|
} |
448
|
|
|
|
449
|
|
|
// Check for JoinTable annotations |
450
|
5 |
|
if ($associationOverride->joinTable) { |
451
|
2 |
|
$joinTableAnnot = $associationOverride->joinTable; |
452
|
|
|
$joinTable = [ |
453
|
2 |
|
'name' => $joinTableAnnot->name, |
454
|
2 |
|
'schema' => $joinTableAnnot->schema |
455
|
|
|
]; |
456
|
|
|
|
457
|
2 |
|
foreach ($joinTableAnnot->joinColumns as $joinColumn) { |
458
|
2 |
|
$joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumn); |
459
|
|
|
} |
460
|
|
|
|
461
|
2 |
|
foreach ($joinTableAnnot->inverseJoinColumns as $joinColumn) { |
462
|
2 |
|
$joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumn); |
463
|
|
|
} |
464
|
|
|
|
465
|
2 |
|
$override['joinTable'] = $joinTable; |
466
|
|
|
} |
467
|
|
|
|
468
|
|
|
// Check for inversedBy |
469
|
5 |
|
if ($associationOverride->inversedBy) { |
470
|
1 |
|
$override['inversedBy'] = $associationOverride->inversedBy; |
471
|
|
|
} |
472
|
|
|
|
473
|
|
|
// Check for `fetch` |
|
|
|
|
474
|
5 |
|
if ($associationOverride->fetch) { |
475
|
1 |
|
$override['fetch'] = constant(Mapping\ClassMetadata::class . '::FETCH_' . $associationOverride->fetch); |
476
|
|
|
} |
477
|
|
|
|
478
|
5 |
|
$metadata->setAssociationOverride($fieldName, $override); |
479
|
|
|
} |
480
|
|
|
} |
481
|
|
|
|
482
|
|
|
// Evaluate AttributeOverrides annotation |
483
|
388 |
|
if (isset($classAnnotations[Mapping\AttributeOverrides::class])) { |
484
|
3 |
|
$attributeOverridesAnnot = $classAnnotations[Mapping\AttributeOverrides::class]; |
485
|
|
|
|
486
|
3 |
|
foreach ($attributeOverridesAnnot->value as $attributeOverrideAnnot) { |
487
|
3 |
|
$attributeOverride = $this->columnToArray($attributeOverrideAnnot->name, $attributeOverrideAnnot->column); |
488
|
|
|
|
489
|
3 |
|
$metadata->setAttributeOverride($attributeOverrideAnnot->name, $attributeOverride); |
490
|
|
|
} |
491
|
|
|
} |
492
|
|
|
|
493
|
|
|
// Evaluate EntityListeners annotation |
494
|
388 |
|
if (isset($classAnnotations[Mapping\EntityListeners::class])) { |
495
|
12 |
|
$entityListenersAnnot = $classAnnotations[Mapping\EntityListeners::class]; |
496
|
|
|
|
497
|
12 |
|
foreach ($entityListenersAnnot->value as $item) { |
498
|
12 |
|
$listenerClassName = $metadata->fullyQualifiedClassName($item); |
499
|
|
|
|
500
|
12 |
|
if ( ! class_exists($listenerClassName)) { |
501
|
|
|
throw MappingException::entityListenerClassNotFound($listenerClassName, $className); |
502
|
|
|
} |
503
|
|
|
|
504
|
12 |
|
$hasMapping = false; |
505
|
12 |
|
$listenerClass = new \ReflectionClass($listenerClassName); |
506
|
|
|
|
507
|
|
|
/* @var $method \ReflectionMethod */ |
508
|
12 |
|
foreach ($listenerClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) { |
509
|
|
|
// find method callbacks. |
510
|
12 |
|
$callbacks = $this->getMethodCallbacks($method); |
511
|
12 |
|
$hasMapping = $hasMapping ?: ( ! empty($callbacks)); |
512
|
|
|
|
513
|
12 |
|
foreach ($callbacks as $value) { |
514
|
12 |
|
$metadata->addEntityListener($value[1], $listenerClassName, $value[0]); |
515
|
|
|
} |
516
|
|
|
} |
517
|
|
|
|
518
|
|
|
// Evaluate the listener using naming convention. |
519
|
12 |
|
if ( ! $hasMapping ) { |
520
|
12 |
|
EntityListenerBuilder::bindEntityListener($metadata, $listenerClassName); |
|
|
|
|
521
|
|
|
} |
522
|
|
|
} |
523
|
|
|
} |
524
|
|
|
|
525
|
|
|
// Evaluate @HasLifecycleCallbacks annotation |
526
|
388 |
|
if (isset($classAnnotations[Mapping\HasLifecycleCallbacks::class])) { |
527
|
|
|
/* @var $method \ReflectionMethod */ |
528
|
19 |
|
foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) { |
529
|
18 |
|
foreach ($this->getMethodCallbacks($method) as $value) { |
530
|
18 |
|
$metadata->addLifecycleCallback($value[0], $value[1]); |
531
|
|
|
} |
532
|
|
|
} |
533
|
|
|
} |
534
|
388 |
|
} |
535
|
|
|
|
536
|
|
|
/** |
537
|
|
|
* Attempts to resolve the fetch mode. |
538
|
|
|
* |
539
|
|
|
* @param string $className The class name. |
540
|
|
|
* @param string $fetchMode The fetch mode. |
541
|
|
|
* |
542
|
|
|
* @return integer The fetch mode as defined in ClassMetadata. |
543
|
|
|
* |
544
|
|
|
* @throws MappingException If the fetch mode is not valid. |
545
|
|
|
*/ |
546
|
259 |
|
private function getFetchMode($className, $fetchMode) |
547
|
|
|
{ |
548
|
259 |
|
if ( ! defined('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $fetchMode)) { |
549
|
|
|
throw MappingException::invalidFetchMode($className, $fetchMode); |
550
|
|
|
} |
551
|
|
|
|
552
|
259 |
|
return constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $fetchMode); |
553
|
|
|
} |
554
|
|
|
|
555
|
|
|
/** |
556
|
|
|
* Parses the given method. |
557
|
|
|
* |
558
|
|
|
* @param \ReflectionMethod $method |
559
|
|
|
* |
560
|
|
|
* @return array |
561
|
|
|
*/ |
562
|
28 |
|
private function getMethodCallbacks(\ReflectionMethod $method) |
563
|
|
|
{ |
564
|
28 |
|
$callbacks = []; |
565
|
28 |
|
$annotations = $this->reader->getMethodAnnotations($method); |
566
|
|
|
|
567
|
28 |
|
foreach ($annotations as $annot) { |
568
|
22 |
|
if ($annot instanceof Mapping\PrePersist) { |
569
|
15 |
|
$callbacks[] = [$method->name, Events::prePersist]; |
570
|
|
|
} |
571
|
|
|
|
572
|
22 |
|
if ($annot instanceof Mapping\PostPersist) { |
573
|
11 |
|
$callbacks[] = [$method->name, Events::postPersist]; |
574
|
|
|
} |
575
|
|
|
|
576
|
22 |
|
if ($annot instanceof Mapping\PreUpdate) { |
577
|
11 |
|
$callbacks[] = [$method->name, Events::preUpdate]; |
578
|
|
|
} |
579
|
|
|
|
580
|
22 |
|
if ($annot instanceof Mapping\PostUpdate) { |
581
|
7 |
|
$callbacks[] = [$method->name, Events::postUpdate]; |
582
|
|
|
} |
583
|
|
|
|
584
|
22 |
|
if ($annot instanceof Mapping\PreRemove) { |
585
|
8 |
|
$callbacks[] = [$method->name, Events::preRemove]; |
586
|
|
|
} |
587
|
|
|
|
588
|
22 |
|
if ($annot instanceof Mapping\PostRemove) { |
589
|
6 |
|
$callbacks[] = [$method->name, Events::postRemove]; |
590
|
|
|
} |
591
|
|
|
|
592
|
22 |
|
if ($annot instanceof Mapping\PostLoad) { |
593
|
11 |
|
$callbacks[] = [$method->name, Events::postLoad]; |
594
|
|
|
} |
595
|
|
|
|
596
|
22 |
|
if ($annot instanceof Mapping\PreFlush) { |
597
|
22 |
|
$callbacks[] = [$method->name, Events::preFlush]; |
598
|
|
|
} |
599
|
|
|
} |
600
|
|
|
|
601
|
28 |
|
return $callbacks; |
602
|
|
|
} |
603
|
|
|
|
604
|
|
|
/** |
605
|
|
|
* Parse the given JoinColumn as array |
606
|
|
|
* |
607
|
|
|
* @param Mapping\JoinColumn $joinColumn |
608
|
|
|
* @return array |
609
|
|
|
*/ |
610
|
186 |
|
private function joinColumnToArray(Mapping\JoinColumn $joinColumn) |
611
|
|
|
{ |
612
|
|
|
return [ |
613
|
186 |
|
'name' => $joinColumn->name, |
614
|
186 |
|
'unique' => $joinColumn->unique, |
615
|
186 |
|
'nullable' => $joinColumn->nullable, |
616
|
186 |
|
'onDelete' => $joinColumn->onDelete, |
617
|
186 |
|
'columnDefinition' => $joinColumn->columnDefinition, |
618
|
186 |
|
'referencedColumnName' => $joinColumn->referencedColumnName, |
619
|
|
|
]; |
620
|
|
|
} |
621
|
|
|
|
622
|
|
|
/** |
623
|
|
|
* Parse the given Column as array |
624
|
|
|
* |
625
|
|
|
* @param string $fieldName |
626
|
|
|
* @param Mapping\Column $column |
627
|
|
|
* |
628
|
|
|
* @return array |
629
|
|
|
*/ |
630
|
379 |
|
private function columnToArray($fieldName, Mapping\Column $column) |
631
|
|
|
{ |
632
|
|
|
$mapping = [ |
633
|
379 |
|
'fieldName' => $fieldName, |
634
|
379 |
|
'type' => $column->type, |
635
|
379 |
|
'scale' => $column->scale, |
636
|
379 |
|
'length' => $column->length, |
637
|
379 |
|
'unique' => $column->unique, |
638
|
379 |
|
'nullable' => $column->nullable, |
639
|
379 |
|
'precision' => $column->precision |
640
|
|
|
]; |
641
|
|
|
|
642
|
379 |
|
if ($column->options) { |
|
|
|
|
643
|
8 |
|
$mapping['options'] = $column->options; |
644
|
|
|
} |
645
|
|
|
|
646
|
379 |
|
if (isset($column->name)) { |
647
|
84 |
|
$mapping['columnName'] = $column->name; |
648
|
|
|
} |
649
|
|
|
|
650
|
379 |
|
if (isset($column->columnDefinition)) { |
651
|
6 |
|
$mapping['columnDefinition'] = $column->columnDefinition; |
652
|
|
|
} |
653
|
|
|
|
654
|
379 |
|
return $mapping; |
655
|
|
|
} |
656
|
|
|
|
657
|
|
|
/** |
658
|
|
|
* Factory method for the Annotation Driver. |
659
|
|
|
* |
660
|
|
|
* @param array|string $paths |
661
|
|
|
* @param AnnotationReader|null $reader |
662
|
|
|
* |
663
|
|
|
* @return AnnotationDriver |
664
|
|
|
*/ |
665
|
|
|
static public function create($paths = [], AnnotationReader $reader = null) |
|
|
|
|
666
|
|
|
{ |
667
|
|
|
if ($reader == null) { |
668
|
|
|
$reader = new AnnotationReader(); |
669
|
|
|
} |
670
|
|
|
|
671
|
|
|
return new self($reader, $paths); |
672
|
|
|
} |
673
|
|
|
} |
674
|
|
|
|
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.