These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | namespace Bankiru\Api\Doctrine\Mapping; |
||
4 | |||
5 | use Bankiru\Api\Doctrine\EntityRepository; |
||
6 | use Bankiru\Api\Doctrine\Exception\MappingException; |
||
7 | use Bankiru\Api\Doctrine\Rpc\Method\MethodProviderInterface; |
||
8 | use Doctrine\Common\Persistence\Mapping\ReflectionService; |
||
9 | use Doctrine\Instantiator\Instantiator; |
||
10 | use Doctrine\Instantiator\InstantiatorInterface; |
||
11 | |||
12 | class EntityMetadata implements ApiMetadata |
||
13 | { |
||
14 | /** |
||
15 | * The ReflectionProperty instances of the mapped class. |
||
16 | * |
||
17 | * @var \ReflectionProperty[] |
||
18 | */ |
||
19 | public $reflFields = []; |
||
20 | /** @var string */ |
||
21 | public $name; |
||
22 | /** @var string */ |
||
23 | public $namespace; |
||
24 | /** @var string */ |
||
25 | public $rootEntityName; |
||
26 | /** @var string[] */ |
||
27 | public $identifier = []; |
||
28 | /** @var array */ |
||
29 | public $fields = []; |
||
30 | /** @var array */ |
||
31 | public $associations = []; |
||
32 | /** @var string */ |
||
33 | public $repositoryClass = EntityRepository::class; |
||
34 | /** @var \ReflectionClass */ |
||
35 | public $reflClass; |
||
36 | /** @var MethodProviderInterface */ |
||
37 | public $methodProvider; |
||
38 | /** @var string */ |
||
39 | public $clientName; |
||
40 | /** @var string */ |
||
41 | public $apiFactory; |
||
42 | /** @var string[] */ |
||
43 | public $apiFieldNames = []; |
||
44 | /** @var string[] */ |
||
45 | public $fieldNames = []; |
||
46 | /** @var bool */ |
||
47 | public $isMappedSuperclass = false; |
||
48 | /** @var bool */ |
||
49 | public $containsForeignIdentifier; |
||
50 | /** @var bool */ |
||
51 | public $isIdentifierComposite = false; |
||
52 | /** @var int */ |
||
53 | public $generatorType = self::GENERATOR_TYPE_NATURAL; |
||
54 | /** @var string[] */ |
||
55 | public $discriminatorField; |
||
56 | /** @var string[] */ |
||
57 | public $discriminatorMap; |
||
58 | /** @var string */ |
||
59 | public $discriminatorValue; |
||
60 | /** @var string[] */ |
||
61 | public $subclasses = []; |
||
62 | /** @var InstantiatorInterface */ |
||
63 | private $instantiator; |
||
64 | /** @var int */ |
||
65 | private $changeTrackingPolicy = self::CHANGETRACKING_DEFERRED_IMPLICIT; |
||
66 | |||
67 | /** |
||
68 | * Initializes a new ClassMetadata instance that will hold the object-relational mapping |
||
69 | * metadata of the class with the given name. |
||
70 | * |
||
71 | * @param string $entityName The name of the entity class the new instance is used for. |
||
72 | */ |
||
73 | 28 | public function __construct($entityName) |
|
74 | { |
||
75 | 28 | $this->name = $entityName; |
|
76 | 28 | $this->rootEntityName = $entityName; |
|
77 | 28 | } |
|
78 | |||
79 | /** |
||
80 | * @return boolean |
||
81 | */ |
||
82 | public function containsForeignIdentifier() |
||
83 | { |
||
84 | return $this->containsForeignIdentifier; |
||
85 | } |
||
86 | |||
87 | /** {@inheritdoc} */ |
||
88 | 11 | public function getReflectionProperties() |
|
89 | { |
||
90 | 11 | return $this->reflFields; |
|
91 | } |
||
92 | |||
93 | /** |
||
94 | * {@inheritdoc} |
||
95 | */ |
||
96 | 25 | public function getReflectionProperty($name) |
|
97 | { |
||
98 | 25 | if (!array_key_exists($name, $this->reflFields)) { |
|
99 | throw MappingException::noSuchProperty($name, $this->getName()); |
||
100 | } |
||
101 | |||
102 | 25 | return $this->reflFields[$name]; |
|
103 | } |
||
104 | |||
105 | /** {@inheritdoc} */ |
||
106 | 28 | public function getName() |
|
107 | { |
||
108 | 28 | return $this->name; |
|
109 | } |
||
110 | |||
111 | /** {@inheritdoc} */ |
||
112 | 27 | public function getMethodContainer() |
|
113 | { |
||
114 | 27 | return $this->methodProvider; |
|
115 | } |
||
116 | |||
117 | /** {@inheritdoc} */ |
||
118 | 18 | public function getRepositoryClass() |
|
119 | { |
||
120 | 18 | return $this->repositoryClass; |
|
121 | } |
||
122 | |||
123 | /** {@inheritdoc} */ |
||
124 | 2 | public function getIdentifier() |
|
125 | { |
||
126 | 2 | return $this->identifier; |
|
127 | } |
||
128 | |||
129 | 14 | public function setIdentifier($identifier) |
|
130 | { |
||
131 | 14 | $this->identifier = $identifier; |
|
132 | 14 | $this->isIdentifierComposite = (count($this->identifier) > 1); |
|
133 | 14 | } |
|
134 | |||
135 | /** {@inheritdoc} */ |
||
136 | 18 | public function getReflectionClass() |
|
137 | { |
||
138 | 18 | if (null === $this->reflClass) { |
|
139 | $this->reflClass = new \ReflectionClass($this->getName()); |
||
140 | } |
||
141 | |||
142 | 18 | return $this->reflClass; |
|
143 | } |
||
144 | |||
145 | /** {@inheritdoc} */ |
||
146 | 7 | public function isIdentifier($fieldName) |
|
147 | { |
||
148 | 7 | return in_array($fieldName, $this->identifier, true); |
|
149 | } |
||
150 | |||
151 | /** {@inheritdoc} */ |
||
152 | 2 | public function hasField($fieldName) |
|
153 | { |
||
154 | 2 | return in_array($fieldName, $this->getFieldNames(), true); |
|
155 | } |
||
156 | |||
157 | /** {@inheritdoc} */ |
||
158 | 18 | public function getFieldNames() |
|
159 | { |
||
160 | 18 | return array_keys($this->fields); |
|
161 | } |
||
162 | |||
163 | /** {@inheritdoc} */ |
||
164 | 26 | public function hasAssociation($fieldName) |
|
165 | { |
||
166 | 26 | return in_array($fieldName, $this->getAssociationNames(), true); |
|
167 | } |
||
168 | |||
169 | /** {@inheritdoc} */ |
||
170 | 26 | public function getAssociationNames() |
|
171 | { |
||
172 | 26 | return array_keys($this->associations); |
|
173 | } |
||
174 | |||
175 | /** {@inheritdoc} */ |
||
176 | 12 | public function isSingleValuedAssociation($fieldName) |
|
177 | { |
||
178 | 12 | return $this->hasAssociation($fieldName) && $this->associations[$fieldName]['type'] & self::TO_ONE; |
|
179 | } |
||
180 | |||
181 | /** {@inheritdoc} */ |
||
182 | 16 | public function isCollectionValuedAssociation($fieldName) |
|
183 | { |
||
184 | 16 | return $this->hasAssociation($fieldName) && $this->associations[$fieldName]['type'] & self::TO_MANY; |
|
185 | } |
||
186 | |||
187 | /** {@inheritdoc} */ |
||
188 | 21 | public function getIdentifierFieldNames() |
|
189 | { |
||
190 | 21 | return $this->identifier; |
|
191 | } |
||
192 | |||
193 | /** {@inheritdoc} */ |
||
194 | 26 | public function getTypeOfField($fieldName) |
|
195 | { |
||
196 | 26 | return $this->fields[$fieldName]['type']; |
|
197 | } |
||
198 | |||
199 | /** {@inheritdoc} */ |
||
200 | 12 | public function getAssociationTargetClass($assocName) |
|
201 | { |
||
202 | 12 | return $this->associations[$assocName]['target']; |
|
203 | } |
||
204 | |||
205 | /** {@inheritdoc} */ |
||
206 | public function isAssociationInverseSide($assocName) |
||
207 | { |
||
208 | $assoc = $this->associations[$assocName]; |
||
209 | |||
210 | return array_key_exists('mappedBy', $assoc); |
||
211 | } |
||
212 | |||
213 | /** {@inheritdoc} */ |
||
214 | public function getAssociationMappedByTargetField($assocName) |
||
215 | { |
||
216 | return $this->associations[$assocName]['mappedBy']; |
||
217 | } |
||
218 | |||
219 | /** {@inheritdoc} */ |
||
220 | 22 | public function getIdentifierValues($object) |
|
221 | { |
||
222 | 22 | if ($this->isIdentifierComposite) { |
|
223 | 1 | $id = []; |
|
224 | 1 | foreach ($this->identifier as $idField) { |
|
225 | 1 | $value = $this->reflFields[$idField]->getValue($object); |
|
226 | 1 | if ($value !== null) { |
|
227 | 1 | $id[$idField] = $value; |
|
228 | 1 | } |
|
229 | 1 | } |
|
230 | |||
231 | 1 | return $id; |
|
232 | } |
||
233 | 21 | $id = $this->identifier[0]; |
|
234 | 21 | $value = $this->reflFields[$id]->getValue($object); |
|
235 | 21 | if (null === $value) { |
|
236 | return []; |
||
237 | } |
||
238 | |||
239 | 21 | return [$id => $value]; |
|
240 | } |
||
241 | |||
242 | /** {@inheritdoc} */ |
||
243 | 28 | public function wakeupReflection(ReflectionService $reflService) |
|
244 | { |
||
245 | // Restore ReflectionClass and properties |
||
246 | 28 | $this->reflClass = $reflService->getClass($this->name); |
|
247 | 28 | $this->instantiator = $this->instantiator ?: new Instantiator(); |
|
248 | |||
249 | 28 | View Code Duplication | foreach ($this->fields as $field => $mapping) { |
250 | 28 | $class = array_key_exists('declared', $mapping) ? $mapping['declared'] : $this->name; |
|
251 | 28 | $this->reflFields[$field] = $reflService->getAccessibleProperty($class, $field); |
|
252 | 28 | } |
|
253 | |||
254 | 28 | View Code Duplication | foreach ($this->associations as $field => $mapping) { |
255 | 18 | $class = array_key_exists('declared', $mapping) ? $mapping['declared'] : $this->name; |
|
256 | 18 | $this->reflFields[$field] = $reflService->getAccessibleProperty($class, $field); |
|
257 | 28 | } |
|
258 | 28 | } |
|
259 | |||
260 | /** {@inheritdoc} */ |
||
261 | 28 | public function initializeReflection(ReflectionService $reflService) |
|
262 | { |
||
263 | 28 | $this->reflClass = $reflService->getClass($this->name); |
|
264 | 28 | $this->namespace = $reflService->getClassNamespace($this->name); |
|
265 | 28 | if ($this->reflClass) { |
|
266 | 28 | $this->name = $this->rootEntityName = $this->reflClass->getName(); |
|
267 | 28 | } |
|
268 | 28 | } |
|
269 | |||
270 | /** {@inheritdoc} */ |
||
271 | 26 | public function getApiFactory() |
|
272 | { |
||
273 | 26 | if (null === $this->apiFactory) { |
|
274 | throw MappingException::noApiSpecified($this->getName()); |
||
275 | } |
||
276 | |||
277 | 26 | return $this->apiFactory; |
|
278 | } |
||
279 | |||
280 | /** {@inheritdoc} */ |
||
281 | 27 | public function getClientName() |
|
282 | { |
||
283 | 27 | if (null === $this->clientName) { |
|
284 | throw MappingException::noClientSpecified($this->getName()); |
||
285 | } |
||
286 | |||
287 | 27 | return $this->clientName; |
|
288 | } |
||
289 | |||
290 | 28 | public function mapField(array $mapping) |
|
291 | { |
||
292 | 28 | $this->validateAndCompleteFieldMapping($mapping); |
|
293 | 28 | $this->assertFieldNotMapped($mapping['field']); |
|
294 | 28 | $this->fields[$mapping['field']] = $mapping; |
|
295 | 28 | } |
|
296 | |||
297 | /** {@inheritdoc} */ |
||
298 | 26 | public function getFieldMapping($fieldName) |
|
299 | { |
||
300 | 26 | if (!isset($this->fields[$fieldName])) { |
|
301 | throw MappingException::unknownField($fieldName, $this->getName()); |
||
302 | } |
||
303 | |||
304 | 26 | return $this->fields[$fieldName]; |
|
305 | } |
||
306 | |||
307 | /** {@inheritdoc} */ |
||
308 | 26 | public function getFieldOptions($fieldName) |
|
309 | { |
||
310 | 26 | return $this->getFieldMapping($fieldName)['options']; |
|
311 | } |
||
312 | |||
313 | /** {@inheritdoc} */ |
||
314 | 17 | public function getAssociationMapping($fieldName) |
|
315 | { |
||
316 | 17 | if (!isset($this->associations[$fieldName])) { |
|
317 | throw MappingException::unknownAssociation($fieldName, $this->getName()); |
||
318 | } |
||
319 | |||
320 | 17 | return $this->associations[$fieldName]; |
|
321 | } |
||
322 | |||
323 | 2 | public function setCustomRepositoryClass($customRepositoryClassName) |
|
324 | { |
||
325 | 2 | $this->repositoryClass = $customRepositoryClassName; |
|
326 | 2 | } |
|
327 | |||
328 | /** |
||
329 | * @internal |
||
330 | * |
||
331 | * @param array $mapping |
||
332 | * |
||
333 | * @return void |
||
334 | */ |
||
335 | 14 | public function addInheritedFieldMapping(array $mapping) |
|
336 | { |
||
337 | 14 | $this->fields[$mapping['field']] = $mapping; |
|
338 | 14 | $this->apiFieldNames[$mapping['field']] = $mapping['api_field']; |
|
339 | 14 | $this->fieldNames[$mapping['api_field']] = $mapping['field']; |
|
340 | 14 | } |
|
341 | |||
342 | /** {@inheritdoc} */ |
||
343 | 7 | public function getFieldName($apiFieldName) |
|
344 | { |
||
345 | 7 | return $this->fieldNames[$apiFieldName]; |
|
346 | } |
||
347 | |||
348 | /** {@inheritdoc} */ |
||
349 | 26 | public function getApiFieldName($fieldName) |
|
350 | { |
||
351 | 26 | return $this->apiFieldNames[$fieldName]; |
|
352 | } |
||
353 | |||
354 | public function hasApiField($apiFieldName) |
||
355 | { |
||
356 | return array_key_exists($apiFieldName, $this->fieldNames); |
||
357 | } |
||
358 | |||
359 | 17 | public function mapOneToMany(array $mapping) |
|
360 | { |
||
361 | 17 | $mapping = $this->validateAndCompleteOneToManyMapping($mapping); |
|
362 | |||
363 | 17 | $this->storeMapping($mapping); |
|
364 | 17 | } |
|
365 | |||
366 | 17 | public function mapManyToOne(array $mapping) |
|
367 | { |
||
368 | 17 | $mapping = $this->validateAndCompleteOneToOneMapping($mapping); |
|
369 | |||
370 | 17 | $this->storeMapping($mapping); |
|
371 | 17 | } |
|
372 | |||
373 | public function mapOneToOne(array $mapping) |
||
374 | { |
||
375 | $mapping = $this->validateAndCompleteOneToOneMapping($mapping); |
||
376 | |||
377 | $this->storeMapping($mapping); |
||
378 | } |
||
379 | |||
380 | /** {@inheritdoc} */ |
||
381 | 17 | public function newInstance() |
|
382 | { |
||
383 | 17 | return $this->instantiator->instantiate($this->name); |
|
384 | } |
||
385 | |||
386 | 8 | public function isIdentifierComposite() |
|
387 | { |
||
388 | 8 | return $this->isIdentifierComposite; |
|
389 | } |
||
390 | |||
391 | /** {@inheritdoc} */ |
||
392 | 28 | public function getRootEntityName() |
|
393 | { |
||
394 | 28 | return $this->rootEntityName; |
|
395 | } |
||
396 | |||
397 | /** |
||
398 | * Populates the entity identifier of an entity. |
||
399 | * |
||
400 | * @param object $entity |
||
401 | * @param array $id |
||
402 | * |
||
403 | * @return void |
||
404 | */ |
||
405 | public function assignIdentifier($entity, array $id) |
||
406 | { |
||
407 | foreach ($id as $idField => $idValue) { |
||
408 | $this->reflFields[$idField]->setValue($entity, $idValue); |
||
409 | } |
||
410 | } |
||
411 | |||
412 | 10 | public function addInheritedAssociationMapping(array $mapping) |
|
413 | { |
||
414 | 10 | $this->associations[$mapping['field']] = $mapping; |
|
415 | 10 | $this->apiFieldNames[$mapping['field']] = $mapping['api_field']; |
|
416 | 10 | $this->fieldNames[$mapping['api_field']] = $mapping['field']; |
|
417 | 10 | } |
|
418 | |||
419 | /** {@inheritdoc} */ |
||
420 | 7 | public function getSubclasses() |
|
421 | { |
||
422 | 7 | return $this->subclasses; |
|
423 | } |
||
424 | |||
425 | /** {@inheritdoc} */ |
||
426 | 7 | public function getAssociationMappings() |
|
427 | { |
||
428 | 7 | return $this->associations; |
|
429 | } |
||
430 | |||
431 | 3 | public function isReadOnly() |
|
432 | { |
||
433 | 3 | return false; |
|
434 | } |
||
435 | |||
436 | /** |
||
437 | * Sets the change tracking policy used by this class. |
||
438 | * |
||
439 | * @param integer $policy |
||
440 | * |
||
441 | * @return void |
||
442 | */ |
||
443 | public function setChangeTrackingPolicy($policy) |
||
444 | { |
||
445 | $this->changeTrackingPolicy = $policy; |
||
446 | } |
||
447 | |||
448 | /** |
||
449 | * Whether the change tracking policy of this class is "deferred explicit". |
||
450 | * |
||
451 | * @return boolean |
||
452 | */ |
||
453 | public function isChangeTrackingDeferredExplicit() |
||
454 | { |
||
455 | return $this->changeTrackingPolicy === self::CHANGETRACKING_DEFERRED_EXPLICIT; |
||
456 | } |
||
457 | |||
458 | /** |
||
459 | * Whether the change tracking policy of this class is "deferred implicit". |
||
460 | * |
||
461 | * @return boolean |
||
462 | */ |
||
463 | 3 | public function isChangeTrackingDeferredImplicit() |
|
464 | { |
||
465 | 3 | return $this->changeTrackingPolicy === self::CHANGETRACKING_DEFERRED_IMPLICIT; |
|
466 | } |
||
467 | |||
468 | /** |
||
469 | * Whether the change tracking policy of this class is "notify". |
||
470 | * |
||
471 | * @return boolean |
||
472 | */ |
||
473 | public function isChangeTrackingNotify() |
||
474 | { |
||
475 | return $this->changeTrackingPolicy === self::CHANGETRACKING_NOTIFY; |
||
476 | } |
||
477 | |||
478 | 28 | public function mapIdentifier(array $mapping) |
|
479 | { |
||
480 | 28 | $this->setIdGeneratorType($mapping['generator']['strategy']); |
|
481 | |||
482 | 28 | $this->mapField($mapping); |
|
483 | 28 | } |
|
484 | |||
485 | /** |
||
486 | * Sets the type of Id generator to use for the mapped class. |
||
487 | * |
||
488 | * @param int $generatorType |
||
489 | * |
||
490 | * @return void |
||
491 | */ |
||
492 | 28 | public function setIdGeneratorType($generatorType) |
|
493 | { |
||
494 | 28 | $this->generatorType = $generatorType; |
|
495 | 28 | } |
|
496 | |||
497 | 7 | public function isIdentifierNatural() |
|
498 | { |
||
499 | 7 | return $this->generatorType === self::GENERATOR_TYPE_NATURAL; |
|
500 | } |
||
501 | |||
502 | 7 | public function isIdentifierRemote() |
|
503 | { |
||
504 | 7 | return $this->generatorType === self::GENERATOR_TYPE_REMOTE; |
|
505 | } |
||
506 | |||
507 | /** |
||
508 | * Populates the entity identifier of an entity. |
||
509 | * |
||
510 | * @param object $entity |
||
511 | * @param array $id |
||
512 | * |
||
513 | * @return void |
||
514 | * |
||
515 | * @todo Rename to assignIdentifier() |
||
516 | */ |
||
517 | public function setIdentifierValues($entity, array $id) |
||
518 | { |
||
519 | foreach ($id as $idField => $idValue) { |
||
520 | $this->reflFields[$idField]->setValue($entity, $idValue); |
||
521 | } |
||
522 | } |
||
523 | |||
524 | 28 | public function isRootEntity() |
|
525 | { |
||
526 | 28 | return $this->getRootEntityName() === $this->getName(); |
|
527 | } |
||
528 | |||
529 | /** |
||
530 | * Adds one entry of the discriminator map with a new class and corresponding name. |
||
531 | * |
||
532 | * @param string $name |
||
533 | * @param string $className |
||
534 | * |
||
535 | * @return void |
||
536 | * |
||
537 | * @throws MappingException |
||
538 | */ |
||
539 | 28 | public function addDiscriminatorMapClass($name, $className) |
|
540 | { |
||
541 | 28 | $className = $this->fullyQualifiedClassName($className); |
|
542 | 28 | $className = ltrim($className, '\\'); |
|
543 | |||
544 | 28 | if (!(class_exists($className) || interface_exists($className))) { |
|
545 | throw MappingException::invalidClassInDiscriminatorMap($className, $this->name); |
||
546 | } |
||
547 | |||
548 | 28 | $refl = new \ReflectionClass($className); |
|
549 | 28 | if ($refl->isAbstract()) { |
|
550 | 4 | return; |
|
551 | } |
||
552 | |||
553 | 28 | $this->discriminatorMap[$name] = $className; |
|
554 | |||
555 | 28 | if ($this->name === $className) { |
|
556 | 28 | $this->discriminatorValue = $name; |
|
557 | |||
558 | 28 | return; |
|
559 | } |
||
560 | |||
561 | 20 | if (is_subclass_of($className, $this->name) && !in_array($className, $this->subclasses)) { |
|
1 ignored issue
–
show
Bug
introduced
by
Loading history...
|
|||
562 | 20 | $this->subclasses[] = $className; |
|
563 | 20 | } |
|
564 | 20 | } |
|
565 | |||
566 | /** |
||
567 | * @param string|null $className |
||
568 | * |
||
569 | * @return string|null null if the input value is null |
||
570 | */ |
||
571 | 28 | public function fullyQualifiedClassName($className) |
|
572 | { |
||
573 | 28 | if (empty($className)) { |
|
574 | return $className; |
||
575 | } |
||
576 | 28 | if ($className !== null && strpos($className, '\\') === false && $this->namespace) { |
|
577 | return $this->namespace . '\\' . $className; |
||
578 | } |
||
579 | |||
580 | 28 | return $className; |
|
581 | } |
||
582 | |||
583 | /** {@inheritdoc} */ |
||
584 | 26 | public function getDiscriminatorField() |
|
585 | { |
||
586 | 26 | return $this->discriminatorField; |
|
587 | } |
||
588 | |||
589 | /** |
||
590 | * Sets the discriminator column definition. |
||
591 | * |
||
592 | * @param array $columnDef |
||
593 | * |
||
594 | * @return void |
||
595 | * |
||
596 | * @throws MappingException |
||
597 | * |
||
598 | * @see getDiscriminatorColumn() |
||
599 | */ |
||
600 | 14 | public function setDiscriminatorField(array $columnDef = null) |
|
601 | { |
||
602 | 14 | if ($columnDef !== null) { |
|
603 | 4 | if (!isset($columnDef['name'])) { |
|
604 | throw MappingException::nameIsMandatoryForDiscriminatorColumns($this->name); |
||
605 | } |
||
606 | 4 | if (isset($this->fieldNames[$columnDef['name']])) { |
|
607 | throw MappingException::duplicateColumnName($this->name, $columnDef['name']); |
||
608 | } |
||
609 | 4 | if (!isset($columnDef['fieldName'])) { |
|
610 | 4 | $columnDef['fieldName'] = $columnDef['name']; |
|
611 | 4 | } |
|
612 | 4 | if (!isset($columnDef['type'])) { |
|
613 | $columnDef['type'] = 'string'; |
||
614 | } |
||
615 | 4 | if (in_array($columnDef['type'], ['boolean', 'array', 'object', 'datetime', 'time', 'date'], true)) { |
|
616 | throw MappingException::invalidDiscriminatorColumnType($this->name, $columnDef['type']); |
||
617 | } |
||
618 | 4 | $this->discriminatorField = $columnDef; |
|
619 | 4 | } |
|
620 | 14 | } |
|
621 | |||
622 | /** {@inheritdoc} */ |
||
623 | 18 | public function getDiscriminatorMap() |
|
624 | { |
||
625 | 18 | return $this->discriminatorMap; |
|
626 | } |
||
627 | |||
628 | /** |
||
629 | * Sets the discriminator values used by this class. |
||
630 | * |
||
631 | * @param array $map |
||
632 | * |
||
633 | * @return void |
||
634 | */ |
||
635 | 28 | public function setDiscriminatorMap(array $map) |
|
636 | { |
||
637 | 28 | foreach ($map as $value => $className) { |
|
638 | 28 | $this->addDiscriminatorMapClass($value, $className); |
|
639 | 28 | } |
|
640 | 28 | } |
|
641 | |||
642 | /** {@inheritdoc} */ |
||
643 | 19 | public function getDiscriminatorValue() |
|
644 | { |
||
645 | 19 | return $this->discriminatorValue; |
|
646 | } |
||
647 | |||
648 | 1 | public function mapManyToMany($mapping) |
|
649 | { |
||
650 | 1 | $mapping = $this->validateAndCompleteManyToManyMapping($mapping); |
|
651 | |||
652 | 1 | $this->storeMapping($mapping); |
|
653 | 1 | } |
|
654 | |||
655 | /** |
||
656 | * Validates & completes the basic mapping information that is common to all |
||
657 | * association mappings (one-to-one, many-ot-one, one-to-many, many-to-many). |
||
658 | * |
||
659 | * @param array $mapping The mapping. |
||
660 | * |
||
661 | * @return array The updated mapping. |
||
662 | * |
||
663 | * @throws MappingException If something is wrong with the mapping. |
||
664 | */ |
||
665 | 18 | protected function validateAndCompleteAssociationMapping(array $mapping) |
|
666 | { |
||
667 | 18 | if (!array_key_exists('api_field', $mapping)) { |
|
668 | 17 | $mapping['api_field'] = $mapping['field']; |
|
669 | 17 | } |
|
670 | |||
671 | 18 | if (!isset($mapping['mappedBy'])) { |
|
672 | 18 | $mapping['mappedBy'] = null; |
|
673 | 18 | } |
|
674 | |||
675 | 18 | if (!isset($mapping['inversedBy'])) { |
|
676 | 18 | $mapping['inversedBy'] = null; |
|
677 | 18 | } |
|
678 | |||
679 | 18 | if (!isset($mapping['orderBy'])) { |
|
680 | 18 | $mapping['orderBy'] = []; |
|
681 | 18 | } |
|
682 | |||
683 | 18 | $mapping['isOwningSide'] = true; // assume owning side until we hit mappedBy |
|
684 | |||
685 | // unset optional indexBy attribute if its empty |
||
686 | 18 | if (!isset($mapping['indexBy']) || !$mapping['indexBy']) { |
|
687 | 18 | unset($mapping['indexBy']); |
|
688 | 18 | } |
|
689 | |||
690 | // If targetEntity is unqualified, assume it is in the same namespace as |
||
691 | // the sourceEntity. |
||
692 | 18 | $mapping['source'] = $this->name; |
|
693 | 18 | if (isset($mapping['target'])) { |
|
694 | 18 | $mapping['target'] = ltrim($mapping['target'], '\\'); |
|
695 | 18 | } |
|
696 | |||
697 | 18 | if (($mapping['type'] & self::MANY_TO_ONE) > 0 && |
|
698 | 18 | isset($mapping['orphanRemoval']) && |
|
699 | $mapping['orphanRemoval'] == true |
||
700 | 18 | ) { |
|
701 | throw new MappingException( |
||
702 | sprintf('Illegal orphanRemoval %s for %s', $mapping['field'], $this->name) |
||
703 | ); |
||
704 | } |
||
705 | |||
706 | // Complete id mapping |
||
707 | 18 | if (isset($mapping['id']) && $mapping['id'] === true) { |
|
708 | View Code Duplication | if (isset($mapping['orphanRemoval']) && $mapping['orphanRemoval'] == true) { |
|
709 | throw new MappingException( |
||
710 | sprintf('Illegal orphanRemoval on identifier association %s for %s', $mapping['field'], $this->name) |
||
711 | ); |
||
712 | } |
||
713 | |||
714 | if (!in_array($mapping['field'], $this->identifier, true)) { |
||
715 | $this->identifier[] = $mapping['field']; |
||
716 | $this->containsForeignIdentifier = true; |
||
717 | } |
||
718 | |||
719 | // Check for composite key |
||
720 | if (!$this->isIdentifierComposite && count($this->identifier) > 1) { |
||
721 | $this->isIdentifierComposite = true; |
||
722 | } |
||
723 | } |
||
724 | |||
725 | // Mandatory and optional attributes for either side |
||
726 | 18 | if (null !== $mapping['mappedBy']) { |
|
727 | 17 | $mapping['isOwningSide'] = false; |
|
728 | 17 | } |
|
729 | |||
730 | 18 | View Code Duplication | if (isset($mapping['id']) && $mapping['id'] === true && $mapping['type'] & self::TO_MANY) { |
731 | throw new MappingException( |
||
732 | sprintf('Illegal toMany identifier association %s for %s', $mapping['field'], $this->name) |
||
733 | ); |
||
734 | } |
||
735 | |||
736 | // Fetch mode. Default fetch mode to LAZY, if not set. |
||
737 | 18 | if (!isset($mapping['fetch'])) { |
|
738 | 18 | $mapping['fetch'] = self::FETCH_LAZY; |
|
739 | 18 | } |
|
740 | |||
741 | // Cascades |
||
742 | 18 | $cascades = isset($mapping['cascade']) ? array_map('strtolower', $mapping['cascade']) : []; |
|
743 | 18 | $allCascades = ['remove', 'persist', 'refresh', 'merge', 'detach']; |
|
744 | 18 | if (in_array('all', $cascades, true)) { |
|
745 | $cascades = $allCascades; |
||
746 | 18 | } elseif (count($cascades) !== count(array_intersect($cascades, $allCascades))) { |
|
747 | throw new MappingException('Invalid cascades: ' . implode(', ', $cascades)); |
||
748 | } |
||
749 | 18 | $mapping['cascade'] = $cascades; |
|
750 | 18 | $mapping['isCascadeRemove'] = in_array('remove', $cascades, true); |
|
751 | 18 | $mapping['isCascadePersist'] = in_array('persist', $cascades, true); |
|
752 | 18 | $mapping['isCascadeRefresh'] = in_array('refresh', $cascades, true); |
|
753 | 18 | $mapping['isCascadeMerge'] = in_array('merge', $cascades, true); |
|
754 | 18 | $mapping['isCascadeDetach'] = in_array('detach', $cascades, true); |
|
755 | |||
756 | 18 | return $mapping; |
|
757 | } |
||
758 | |||
759 | 18 | private function storeMapping(array $mapping) |
|
760 | { |
||
761 | 18 | $this->assertFieldNotMapped($mapping['field']); |
|
762 | |||
763 | 18 | $this->apiFieldNames[$mapping['field']] = $mapping['api_field']; |
|
764 | 18 | $this->fieldNames[$mapping['api_field']] = $mapping['field']; |
|
765 | 18 | $this->associations[$mapping['field']] = $mapping; |
|
766 | 18 | } |
|
767 | |||
768 | 28 | private function validateAndCompleteFieldMapping(array &$mapping) |
|
769 | { |
||
770 | 28 | if (!array_key_exists('api_field', $mapping)) { |
|
771 | 27 | $mapping['api_field'] = $mapping['field']; //todo: invent naming strategy |
|
772 | 27 | } |
|
773 | |||
774 | 28 | if (!array_key_exists('options', $mapping)) { |
|
775 | $mapping['options'] = []; |
||
776 | } |
||
777 | |||
778 | 28 | $this->apiFieldNames[$mapping['field']] = $mapping['api_field']; |
|
779 | 28 | $this->fieldNames[$mapping['api_field']] = $mapping['field']; |
|
780 | |||
781 | // if (isset($this->fieldNames[$mapping['columnName']]) || ($this->discriminatorField && $this->discriminatorField['name'] === $mapping['api_field'])) { |
||
782 | // throw MappingException::duplicateColumnName($this->name, $mapping['columnName']); |
||
783 | // } |
||
784 | |||
785 | // Complete id mapping |
||
786 | 28 | if (isset($mapping['id']) && $mapping['id'] === true) { |
|
787 | 28 | if (!in_array($mapping['field'], $this->identifier, true)) { |
|
788 | 28 | $this->identifier[] = $mapping['field']; |
|
789 | 28 | } |
|
790 | // Check for composite key |
||
791 | 28 | if (!$this->isIdentifierComposite && count($this->identifier) > 1) { |
|
792 | 1 | $this->isIdentifierComposite = true; |
|
793 | 1 | } |
|
794 | 28 | } |
|
795 | 28 | } |
|
796 | |||
797 | /** |
||
798 | * @param string $fieldName |
||
799 | * |
||
800 | * @throws MappingException |
||
801 | */ |
||
802 | 28 | private function assertFieldNotMapped($fieldName) |
|
803 | { |
||
804 | 28 | if (array_key_exists($fieldName, $this->fields) || |
|
805 | 28 | array_key_exists($fieldName, $this->associations) || |
|
806 | 28 | array_key_exists($fieldName, $this->identifier) |
|
807 | 28 | ) { |
|
808 | throw new MappingException('Field already mapped'); |
||
809 | } |
||
810 | 28 | } |
|
811 | |||
812 | /** |
||
813 | * @param array $mapping |
||
814 | * |
||
815 | * @return array |
||
816 | * @throws MappingException |
||
817 | * @throws \InvalidArgumentException |
||
818 | */ |
||
819 | 17 | private function validateAndCompleteOneToManyMapping(array $mapping) |
|
820 | { |
||
821 | 17 | $mapping = $this->validateAndCompleteAssociationMapping($mapping); |
|
822 | |||
823 | // OneToMany-side MUST be inverse (must have mappedBy) |
||
824 | 17 | if (!isset($mapping['mappedBy'])) { |
|
825 | throw new MappingException( |
||
826 | sprintf('Many to many requires mapped by: %s', $mapping['field']) |
||
827 | ); |
||
828 | } |
||
829 | 17 | $mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) && $mapping['orphanRemoval']; |
|
830 | 17 | $mapping['isCascadeRemove'] = $mapping['orphanRemoval'] || $mapping['isCascadeRemove']; |
|
831 | 17 | $this->assertMappingOrderBy($mapping); |
|
832 | |||
833 | 17 | return $mapping; |
|
834 | } |
||
835 | |||
836 | /** |
||
837 | * @param array $mapping |
||
838 | * |
||
839 | * @return array |
||
840 | * @throws MappingException |
||
841 | * @throws \InvalidArgumentException |
||
842 | */ |
||
843 | 1 | private function validateAndCompleteManyToManyMapping(array $mapping) |
|
844 | { |
||
845 | 1 | $mapping = $this->validateAndCompleteAssociationMapping($mapping); |
|
846 | |||
847 | 1 | $this->assertMappingOrderBy($mapping); |
|
848 | |||
849 | 1 | return $mapping; |
|
850 | } |
||
851 | |||
852 | /** |
||
853 | * @param array $mapping |
||
854 | * |
||
855 | * @throws \InvalidArgumentException |
||
856 | */ |
||
857 | 18 | private function assertMappingOrderBy(array $mapping) |
|
858 | { |
||
859 | 18 | if (array_key_exists('orderBy', $mapping) && !is_array($mapping['orderBy'])) { |
|
860 | throw new \InvalidArgumentException( |
||
861 | "'orderBy' is expected to be an array, not " . gettype($mapping['orderBy']) |
||
862 | ); |
||
863 | } |
||
864 | 18 | } |
|
865 | |||
866 | /** |
||
867 | * @param array $mapping |
||
868 | * |
||
869 | * @return array |
||
870 | */ |
||
871 | 17 | private function validateAndCompleteOneToOneMapping(array $mapping) |
|
872 | { |
||
873 | 17 | $mapping = $this->validateAndCompleteAssociationMapping($mapping); |
|
874 | |||
875 | 17 | $mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) && $mapping['orphanRemoval']; |
|
876 | 17 | $mapping['isCascadeRemove'] = $mapping['orphanRemoval'] || $mapping['isCascadeRemove']; |
|
877 | 17 | if ($mapping['orphanRemoval']) { |
|
878 | unset($mapping['unique']); |
||
879 | } |
||
880 | |||
881 | 17 | return $mapping; |
|
882 | } |
||
883 | } |
||
884 |