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