bankiru /
doctrine-api-client
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 | 25 | public function __construct($entityName) |
|
| 74 | { |
||
| 75 | 25 | $this->name = $entityName; |
|
| 76 | 25 | $this->rootEntityName = $entityName; |
|
| 77 | 25 | } |
|
| 78 | |||
| 79 | /** |
||
| 80 | * @return boolean |
||
| 81 | */ |
||
| 82 | public function containsForeignIdentifier() |
||
| 83 | { |
||
| 84 | return $this->containsForeignIdentifier; |
||
| 85 | } |
||
| 86 | |||
| 87 | /** {@inheritdoc} */ |
||
| 88 | 10 | public function getReflectionProperties() |
|
| 89 | { |
||
| 90 | 10 | return $this->reflFields; |
|
| 91 | } |
||
| 92 | |||
| 93 | /** |
||
| 94 | * {@inheritdoc} |
||
| 95 | */ |
||
| 96 | 22 | public function getReflectionProperty($name) |
|
| 97 | { |
||
| 98 | 22 | if (!array_key_exists($name, $this->reflFields)) { |
|
| 99 | throw MappingException::noSuchProperty($name, $this->getName()); |
||
| 100 | } |
||
| 101 | |||
| 102 | 22 | return $this->reflFields[$name]; |
|
| 103 | } |
||
| 104 | |||
| 105 | /** {@inheritdoc} */ |
||
| 106 | 25 | public function getName() |
|
| 107 | { |
||
| 108 | 25 | return $this->name; |
|
| 109 | } |
||
| 110 | |||
| 111 | /** {@inheritdoc} */ |
||
| 112 | 24 | public function getMethodContainer() |
|
| 113 | { |
||
| 114 | 24 | return $this->methodProvider; |
|
| 115 | } |
||
| 116 | |||
| 117 | /** {@inheritdoc} */ |
||
| 118 | 16 | public function getRepositoryClass() |
|
| 119 | { |
||
| 120 | 16 | 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 | 16 | public function getReflectionClass() |
|
| 137 | { |
||
| 138 | 16 | if (null === $this->reflClass) { |
|
| 139 | $this->reflClass = new \ReflectionClass($this->getName()); |
||
| 140 | } |
||
| 141 | |||
| 142 | 16 | return $this->reflClass; |
|
| 143 | } |
||
| 144 | |||
| 145 | /** {@inheritdoc} */ |
||
| 146 | 6 | public function isIdentifier($fieldName) |
|
| 147 | { |
||
| 148 | 6 | 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 | 16 | public function getFieldNames() |
|
| 159 | { |
||
| 160 | 16 | return array_keys($this->fields); |
|
| 161 | } |
||
| 162 | |||
| 163 | /** {@inheritdoc} */ |
||
| 164 | 23 | public function hasAssociation($fieldName) |
|
| 165 | { |
||
| 166 | 23 | return in_array($fieldName, $this->getAssociationNames(), true); |
|
| 167 | } |
||
| 168 | |||
| 169 | /** {@inheritdoc} */ |
||
| 170 | 23 | public function getAssociationNames() |
|
| 171 | { |
||
| 172 | 23 | return array_keys($this->associations); |
|
| 173 | } |
||
| 174 | |||
| 175 | /** {@inheritdoc} */ |
||
| 176 | 11 | public function isSingleValuedAssociation($fieldName) |
|
| 177 | { |
||
| 178 | 11 | return $this->hasAssociation($fieldName) && $this->associations[$fieldName]['type'] & self::TO_ONE; |
|
| 179 | } |
||
| 180 | |||
| 181 | /** {@inheritdoc} */ |
||
| 182 | 14 | public function isCollectionValuedAssociation($fieldName) |
|
| 183 | { |
||
| 184 | 14 | return $this->hasAssociation($fieldName) && $this->associations[$fieldName]['type'] & self::TO_MANY; |
|
| 185 | } |
||
| 186 | |||
| 187 | /** {@inheritdoc} */ |
||
| 188 | 19 | public function getIdentifierFieldNames() |
|
| 189 | { |
||
| 190 | 19 | return $this->identifier; |
|
| 191 | } |
||
| 192 | |||
| 193 | /** {@inheritdoc} */ |
||
| 194 | 23 | public function getTypeOfField($fieldName) |
|
| 195 | { |
||
| 196 | 23 | return $this->fields[$fieldName]['type']; |
|
| 197 | } |
||
| 198 | |||
| 199 | /** {@inheritdoc} */ |
||
| 200 | 11 | public function getAssociationTargetClass($assocName) |
|
| 201 | { |
||
| 202 | 11 | 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 | 20 | public function getIdentifierValues($object) |
|
| 221 | { |
||
| 222 | 20 | 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 | 19 | $id = $this->identifier[0]; |
|
| 234 | 19 | $value = $this->reflFields[$id]->getValue($object); |
|
| 235 | 19 | if (null === $value) { |
|
| 236 | return []; |
||
| 237 | } |
||
| 238 | |||
| 239 | 19 | return [$id => $value]; |
|
| 240 | } |
||
| 241 | |||
| 242 | /** {@inheritdoc} */ |
||
| 243 | 25 | public function wakeupReflection(ReflectionService $reflService) |
|
| 244 | { |
||
| 245 | // Restore ReflectionClass and properties |
||
| 246 | 25 | $this->reflClass = $reflService->getClass($this->name); |
|
| 247 | 25 | $this->instantiator = $this->instantiator ?: new Instantiator(); |
|
| 248 | |||
| 249 | 25 | View Code Duplication | foreach ($this->fields as $field => $mapping) { |
| 250 | 25 | $class = array_key_exists('declared', $mapping) ? $mapping['declared'] : $this->name; |
|
| 251 | 25 | $this->reflFields[$field] = $reflService->getAccessibleProperty($class, $field); |
|
| 252 | 25 | } |
|
| 253 | |||
| 254 | 25 | View Code Duplication | foreach ($this->associations as $field => $mapping) { |
| 255 | 17 | $class = array_key_exists('declared', $mapping) ? $mapping['declared'] : $this->name; |
|
| 256 | 17 | $this->reflFields[$field] = $reflService->getAccessibleProperty($class, $field); |
|
| 257 | 25 | } |
|
| 258 | 25 | } |
|
| 259 | |||
| 260 | /** {@inheritdoc} */ |
||
| 261 | 25 | public function initializeReflection(ReflectionService $reflService) |
|
| 262 | { |
||
| 263 | 25 | $this->reflClass = $reflService->getClass($this->name); |
|
| 264 | 25 | $this->namespace = $reflService->getClassNamespace($this->name); |
|
| 265 | 25 | if ($this->reflClass) { |
|
| 266 | 25 | $this->name = $this->rootEntityName = $this->reflClass->getName(); |
|
| 267 | 25 | } |
|
| 268 | 25 | } |
|
| 269 | |||
| 270 | /** {@inheritdoc} */ |
||
| 271 | 23 | public function getApiFactory() |
|
| 272 | { |
||
| 273 | 23 | if (null === $this->apiFactory) { |
|
| 274 | throw MappingException::noApiSpecified($this->getName()); |
||
| 275 | } |
||
| 276 | |||
| 277 | 23 | return $this->apiFactory; |
|
| 278 | } |
||
| 279 | |||
| 280 | /** {@inheritdoc} */ |
||
| 281 | 24 | public function getClientName() |
|
| 282 | { |
||
| 283 | 24 | if (null === $this->clientName) { |
|
| 284 | throw MappingException::noClientSpecified($this->getName()); |
||
| 285 | } |
||
| 286 | |||
| 287 | 24 | return $this->clientName; |
|
| 288 | } |
||
| 289 | |||
| 290 | 25 | public function mapField(array $mapping) |
|
| 291 | { |
||
| 292 | 25 | $this->validateAndCompleteFieldMapping($mapping); |
|
| 293 | 25 | $this->assertFieldNotMapped($mapping['field']); |
|
| 294 | 25 | $this->fields[$mapping['field']] = $mapping; |
|
| 295 | 25 | } |
|
| 296 | |||
| 297 | /** {@inheritdoc} */ |
||
| 298 | 4 | public function getFieldMapping($fieldName) |
|
| 299 | { |
||
| 300 | 4 | if (!isset($this->fields[$fieldName])) { |
|
| 301 | throw MappingException::unknownField($fieldName, $this->getName()); |
||
| 302 | } |
||
| 303 | |||
| 304 | 4 | return $this->fields[$fieldName]; |
|
| 305 | } |
||
| 306 | |||
| 307 | /** {@inheritdoc} */ |
||
| 308 | 16 | public function getAssociationMapping($fieldName) |
|
| 309 | { |
||
| 310 | 16 | if (!isset($this->associations[$fieldName])) { |
|
| 311 | throw MappingException::unknownAssociation($fieldName, $this->getName()); |
||
| 312 | } |
||
| 313 | |||
| 314 | 16 | return $this->associations[$fieldName]; |
|
| 315 | } |
||
| 316 | |||
| 317 | 2 | public function setCustomRepositoryClass($customRepositoryClassName) |
|
| 318 | { |
||
| 319 | 2 | $this->repositoryClass = $customRepositoryClassName; |
|
| 320 | 2 | } |
|
| 321 | |||
| 322 | /** |
||
| 323 | * @internal |
||
| 324 | * |
||
| 325 | * @param array $mapping |
||
| 326 | * |
||
| 327 | * @return void |
||
| 328 | */ |
||
| 329 | 14 | public function addInheritedFieldMapping(array $mapping) |
|
| 330 | { |
||
| 331 | 14 | $this->fields[$mapping['field']] = $mapping; |
|
| 332 | 14 | $this->apiFieldNames[$mapping['field']] = $mapping['api_field']; |
|
| 333 | 14 | $this->fieldNames[$mapping['api_field']] = $mapping['field']; |
|
| 334 | 14 | } |
|
| 335 | |||
| 336 | /** {@inheritdoc} */ |
||
| 337 | 6 | public function getFieldName($apiFieldName) |
|
| 338 | { |
||
| 339 | 6 | return $this->fieldNames[$apiFieldName]; |
|
| 340 | } |
||
| 341 | |||
| 342 | /** {@inheritdoc} */ |
||
| 343 | 23 | public function getApiFieldName($fieldName) |
|
| 344 | { |
||
| 345 | 23 | return $this->apiFieldNames[$fieldName]; |
|
| 346 | } |
||
| 347 | |||
| 348 | public function hasApiField($apiFieldName) |
||
| 349 | { |
||
| 350 | return array_key_exists($apiFieldName, $this->fieldNames); |
||
| 351 | } |
||
| 352 | |||
| 353 | 17 | public function mapOneToMany(array $mapping) |
|
| 354 | { |
||
| 355 | 17 | $mapping = $this->validateAndCompleteOneToManyMapping($mapping); |
|
| 356 | |||
| 357 | 17 | $this->storeMapping($mapping); |
|
| 358 | 17 | } |
|
| 359 | |||
| 360 | 17 | public function mapManyToOne(array $mapping) |
|
| 361 | { |
||
| 362 | 17 | $mapping = $this->validateAndCompleteOneToOneMapping($mapping); |
|
| 363 | |||
| 364 | 17 | $this->storeMapping($mapping); |
|
| 365 | 17 | } |
|
| 366 | |||
| 367 | public function mapOneToOne(array $mapping) |
||
| 368 | { |
||
| 369 | $mapping = $this->validateAndCompleteOneToOneMapping($mapping); |
||
| 370 | |||
| 371 | $this->storeMapping($mapping); |
||
| 372 | } |
||
| 373 | |||
| 374 | /** {@inheritdoc} */ |
||
| 375 | 15 | public function newInstance() |
|
| 376 | { |
||
| 377 | 15 | return $this->instantiator->instantiate($this->name); |
|
| 378 | } |
||
| 379 | |||
| 380 | 7 | public function isIdentifierComposite() |
|
| 381 | { |
||
| 382 | 7 | return $this->isIdentifierComposite; |
|
| 383 | } |
||
| 384 | |||
| 385 | /** {@inheritdoc} */ |
||
| 386 | 25 | public function getRootEntityName() |
|
| 387 | { |
||
| 388 | 25 | return $this->rootEntityName; |
|
| 389 | } |
||
| 390 | |||
| 391 | /** |
||
| 392 | * Populates the entity identifier of an entity. |
||
| 393 | * |
||
| 394 | * @param object $entity |
||
| 395 | * @param array $id |
||
| 396 | * |
||
| 397 | * @return void |
||
| 398 | */ |
||
| 399 | public function assignIdentifier($entity, array $id) |
||
| 400 | { |
||
| 401 | foreach ($id as $idField => $idValue) { |
||
| 402 | $this->reflFields[$idField]->setValue($entity, $idValue); |
||
| 403 | } |
||
| 404 | } |
||
| 405 | |||
| 406 | 10 | public function addInheritedAssociationMapping(array $mapping) |
|
| 407 | { |
||
| 408 | 10 | $this->associations[$mapping['field']] = $mapping; |
|
| 409 | 10 | $this->apiFieldNames[$mapping['field']] = $mapping['api_field']; |
|
| 410 | 10 | $this->fieldNames[$mapping['api_field']] = $mapping['field']; |
|
| 411 | 10 | } |
|
| 412 | |||
| 413 | /** {@inheritdoc} */ |
||
| 414 | 7 | public function getSubclasses() |
|
| 415 | { |
||
| 416 | 7 | return $this->subclasses; |
|
| 417 | } |
||
| 418 | |||
| 419 | /** {@inheritdoc} */ |
||
| 420 | 6 | public function getAssociationMappings() |
|
| 421 | { |
||
| 422 | 6 | return $this->associations; |
|
| 423 | } |
||
| 424 | |||
| 425 | 3 | public function isReadOnly() |
|
| 426 | { |
||
| 427 | 3 | return false; |
|
| 428 | } |
||
| 429 | |||
| 430 | /** |
||
| 431 | * Sets the change tracking policy used by this class. |
||
| 432 | * |
||
| 433 | * @param integer $policy |
||
| 434 | * |
||
| 435 | * @return void |
||
| 436 | */ |
||
| 437 | public function setChangeTrackingPolicy($policy) |
||
| 438 | { |
||
| 439 | $this->changeTrackingPolicy = $policy; |
||
| 440 | } |
||
| 441 | |||
| 442 | /** |
||
| 443 | * Whether the change tracking policy of this class is "deferred explicit". |
||
| 444 | * |
||
| 445 | * @return boolean |
||
| 446 | */ |
||
| 447 | public function isChangeTrackingDeferredExplicit() |
||
| 448 | { |
||
| 449 | return $this->changeTrackingPolicy === self::CHANGETRACKING_DEFERRED_EXPLICIT; |
||
| 450 | } |
||
| 451 | |||
| 452 | /** |
||
| 453 | * Whether the change tracking policy of this class is "deferred implicit". |
||
| 454 | * |
||
| 455 | * @return boolean |
||
| 456 | */ |
||
| 457 | 3 | public function isChangeTrackingDeferredImplicit() |
|
| 458 | { |
||
| 459 | 3 | return $this->changeTrackingPolicy === self::CHANGETRACKING_DEFERRED_IMPLICIT; |
|
| 460 | } |
||
| 461 | |||
| 462 | /** |
||
| 463 | * Whether the change tracking policy of this class is "notify". |
||
| 464 | * |
||
| 465 | * @return boolean |
||
| 466 | */ |
||
| 467 | public function isChangeTrackingNotify() |
||
| 468 | { |
||
| 469 | return $this->changeTrackingPolicy === self::CHANGETRACKING_NOTIFY; |
||
| 470 | } |
||
| 471 | |||
| 472 | 25 | public function mapIdentifier(array $mapping) |
|
| 473 | { |
||
| 474 | 25 | $this->setIdGeneratorType($mapping['generator']['strategy']); |
|
| 475 | |||
| 476 | 25 | $this->mapField($mapping); |
|
| 477 | 25 | } |
|
| 478 | |||
| 479 | /** |
||
| 480 | * Sets the type of Id generator to use for the mapped class. |
||
| 481 | * |
||
| 482 | * @param int $generatorType |
||
| 483 | * |
||
| 484 | * @return void |
||
| 485 | */ |
||
| 486 | 25 | public function setIdGeneratorType($generatorType) |
|
| 487 | { |
||
| 488 | 25 | $this->generatorType = $generatorType; |
|
| 489 | 25 | } |
|
| 490 | |||
| 491 | 6 | public function isIdentifierNatural() |
|
| 492 | { |
||
| 493 | 6 | return $this->generatorType === self::GENERATOR_TYPE_NATURAL; |
|
| 494 | } |
||
| 495 | |||
| 496 | 6 | public function isIdentifierRemote() |
|
| 497 | { |
||
| 498 | 6 | return $this->generatorType === self::GENERATOR_TYPE_REMOTE; |
|
| 499 | } |
||
| 500 | |||
| 501 | /** |
||
| 502 | * Populates the entity identifier of an entity. |
||
| 503 | * |
||
| 504 | * @param object $entity |
||
| 505 | * @param array $id |
||
| 506 | * |
||
| 507 | * @return void |
||
| 508 | * |
||
| 509 | * @todo Rename to assignIdentifier() |
||
| 510 | */ |
||
| 511 | public function setIdentifierValues($entity, array $id) |
||
| 512 | { |
||
| 513 | foreach ($id as $idField => $idValue) { |
||
| 514 | $this->reflFields[$idField]->setValue($entity, $idValue); |
||
| 515 | } |
||
| 516 | } |
||
| 517 | |||
| 518 | 25 | public function isRootEntity() |
|
| 519 | { |
||
| 520 | 25 | return $this->getRootEntityName() === $this->getName(); |
|
| 521 | } |
||
| 522 | |||
| 523 | /** |
||
| 524 | * Sets the discriminator column definition. |
||
| 525 | * |
||
| 526 | * @param array $columnDef |
||
| 527 | * |
||
| 528 | * @return void |
||
| 529 | * |
||
| 530 | * @throws MappingException |
||
| 531 | * |
||
| 532 | * @see getDiscriminatorColumn() |
||
| 533 | */ |
||
| 534 | 14 | public function setDiscriminatorField(array $columnDef = null) |
|
| 535 | { |
||
| 536 | 14 | if ($columnDef !== null) { |
|
| 537 | 4 | if (!isset($columnDef['name'])) { |
|
| 538 | throw MappingException::nameIsMandatoryForDiscriminatorColumns($this->name); |
||
| 539 | } |
||
| 540 | 4 | if (isset($this->fieldNames[$columnDef['name']])) { |
|
| 541 | throw MappingException::duplicateColumnName($this->name, $columnDef['name']); |
||
| 542 | } |
||
| 543 | 4 | if (!isset($columnDef['fieldName'])) { |
|
| 544 | 4 | $columnDef['fieldName'] = $columnDef['name']; |
|
| 545 | 4 | } |
|
| 546 | 4 | if (!isset($columnDef['type'])) { |
|
| 547 | $columnDef['type'] = 'string'; |
||
| 548 | } |
||
| 549 | 4 | if (in_array($columnDef['type'], ['boolean', 'array', 'object', 'datetime', 'time', 'date'], true)) { |
|
| 550 | throw MappingException::invalidDiscriminatorColumnType($this->name, $columnDef['type']); |
||
| 551 | } |
||
| 552 | 4 | $this->discriminatorField = $columnDef; |
|
|
0 ignored issues
–
show
|
|||
| 553 | 4 | } |
|
| 554 | 14 | } |
|
| 555 | |||
| 556 | /** |
||
| 557 | * Sets the discriminator values used by this class. |
||
| 558 | * Used for JOINED and SINGLE_TABLE inheritance mapping strategies. |
||
| 559 | * |
||
| 560 | * @param array $map |
||
| 561 | * |
||
| 562 | * @return void |
||
| 563 | */ |
||
| 564 | 25 | public function setDiscriminatorMap(array $map) |
|
| 565 | { |
||
| 566 | 25 | foreach ($map as $value => $className) { |
|
| 567 | 25 | $this->addDiscriminatorMapClass($value, $className); |
|
| 568 | 25 | } |
|
| 569 | 25 | } |
|
| 570 | |||
| 571 | /** |
||
| 572 | * Adds one entry of the discriminator map with a new class and corresponding name. |
||
| 573 | * |
||
| 574 | * @param string $name |
||
| 575 | * @param string $className |
||
| 576 | * |
||
| 577 | * @return void |
||
| 578 | * |
||
| 579 | * @throws MappingException |
||
| 580 | */ |
||
| 581 | 25 | public function addDiscriminatorMapClass($name, $className) |
|
| 582 | { |
||
| 583 | 25 | $className = $this->fullyQualifiedClassName($className); |
|
| 584 | 25 | $className = ltrim($className, '\\'); |
|
| 585 | 25 | $this->discriminatorMap[$name] = $className; |
|
| 586 | 25 | if ($this->name === $className) { |
|
| 587 | 25 | $this->discriminatorValue = $name; |
|
| 588 | |||
| 589 | 25 | return; |
|
| 590 | } |
||
| 591 | 20 | if (!(class_exists($className) || interface_exists($className))) { |
|
| 592 | throw MappingException::invalidClassInDiscriminatorMap($className, $this->name); |
||
| 593 | } |
||
| 594 | 20 | if (is_subclass_of($className, $this->name) && !in_array($className, $this->subclasses)) { |
|
|
1 ignored issue
–
show
|
|||
| 595 | 20 | $this->subclasses[] = $className; |
|
| 596 | 20 | } |
|
| 597 | 20 | } |
|
| 598 | |||
| 599 | /** |
||
| 600 | * @param string|null $className |
||
| 601 | * |
||
| 602 | * @return string|null null if the input value is null |
||
| 603 | */ |
||
| 604 | 25 | public function fullyQualifiedClassName($className) |
|
| 605 | { |
||
| 606 | 25 | if (empty($className)) { |
|
| 607 | return $className; |
||
| 608 | } |
||
| 609 | 25 | if ($className !== null && strpos($className, '\\') === false && $this->namespace) { |
|
| 610 | return $this->namespace . '\\' . $className; |
||
| 611 | } |
||
| 612 | 25 | return $className; |
|
| 613 | } |
||
| 614 | |||
| 615 | /** |
||
| 616 | * Validates & completes the basic mapping information that is common to all |
||
| 617 | * association mappings (one-to-one, many-ot-one, one-to-many, many-to-many). |
||
| 618 | * |
||
| 619 | * @param array $mapping The mapping. |
||
| 620 | * |
||
| 621 | * @return array The updated mapping. |
||
| 622 | * |
||
| 623 | * @throws MappingException If something is wrong with the mapping. |
||
| 624 | */ |
||
| 625 | 17 | protected function validateAndCompleteAssociationMapping(array $mapping) |
|
| 626 | { |
||
| 627 | 17 | if (!array_key_exists('api_field', $mapping)) { |
|
| 628 | 17 | $mapping['api_field'] = $mapping['field']; |
|
| 629 | 17 | } |
|
| 630 | |||
| 631 | 17 | if (!isset($mapping['mappedBy'])) { |
|
| 632 | 17 | $mapping['mappedBy'] = null; |
|
| 633 | 17 | } |
|
| 634 | |||
| 635 | 17 | if (!isset($mapping['inversedBy'])) { |
|
| 636 | 17 | $mapping['inversedBy'] = null; |
|
| 637 | 17 | } |
|
| 638 | |||
| 639 | 17 | $mapping['isOwningSide'] = true; // assume owning side until we hit mappedBy |
|
| 640 | |||
| 641 | // unset optional indexBy attribute if its empty |
||
| 642 | 17 | if (!isset($mapping['indexBy']) || !$mapping['indexBy']) { |
|
| 643 | 17 | unset($mapping['indexBy']); |
|
| 644 | 17 | } |
|
| 645 | |||
| 646 | // If targetEntity is unqualified, assume it is in the same namespace as |
||
| 647 | // the sourceEntity. |
||
| 648 | 17 | $mapping['source'] = $this->name; |
|
| 649 | 17 | if (isset($mapping['target'])) { |
|
| 650 | 17 | $mapping['target'] = ltrim($mapping['target'], '\\'); |
|
| 651 | 17 | } |
|
| 652 | |||
| 653 | 17 | if (($mapping['type'] & self::MANY_TO_ONE) > 0 && |
|
| 654 | 17 | isset($mapping['orphanRemoval']) && |
|
| 655 | $mapping['orphanRemoval'] == true |
||
| 656 | 17 | ) { |
|
| 657 | throw new MappingException( |
||
| 658 | sprintf('Illegal orphanRemoval %s for %s', $mapping['field'], $this->name) |
||
| 659 | ); |
||
| 660 | } |
||
| 661 | |||
| 662 | // Complete id mapping |
||
| 663 | 17 | if (isset($mapping['id']) && $mapping['id'] === true) { |
|
| 664 | View Code Duplication | if (isset($mapping['orphanRemoval']) && $mapping['orphanRemoval'] == true) { |
|
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. Loading history...
|
|||
| 665 | throw new MappingException( |
||
| 666 | sprintf('Illegal orphanRemoval on identifier association %s for %s', $mapping['field'], $this->name) |
||
| 667 | ); |
||
| 668 | } |
||
| 669 | |||
| 670 | if (!in_array($mapping['field'], $this->identifier, true)) { |
||
| 671 | $this->identifier[] = $mapping['field']; |
||
| 672 | $this->containsForeignIdentifier = true; |
||
| 673 | } |
||
| 674 | |||
| 675 | // Check for composite key |
||
| 676 | if (!$this->isIdentifierComposite && count($this->identifier) > 1) { |
||
| 677 | $this->isIdentifierComposite = true; |
||
| 678 | } |
||
| 679 | } |
||
| 680 | |||
| 681 | // Mandatory and optional attributes for either side |
||
| 682 | 17 | if (null !== $mapping['mappedBy']) { |
|
| 683 | 17 | $mapping['isOwningSide'] = false; |
|
| 684 | 17 | } |
|
| 685 | |||
| 686 | 17 | View Code Duplication | if (isset($mapping['id']) && $mapping['id'] === true && $mapping['type'] & self::TO_MANY) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. Loading history...
|
|||
| 687 | throw new MappingException( |
||
| 688 | sprintf('Illegal toMany identifier association %s for %s', $mapping['field'], $this->name) |
||
| 689 | ); |
||
| 690 | } |
||
| 691 | |||
| 692 | // Fetch mode. Default fetch mode to LAZY, if not set. |
||
| 693 | 17 | if (!isset($mapping['fetch'])) { |
|
| 694 | 17 | $mapping['fetch'] = self::FETCH_LAZY; |
|
| 695 | 17 | } |
|
| 696 | |||
| 697 | // Cascades |
||
| 698 | 17 | $cascades = isset($mapping['cascade']) ? array_map('strtolower', $mapping['cascade']) : []; |
|
| 699 | 17 | $allCascades = ['remove', 'persist', 'refresh', 'merge', 'detach']; |
|
| 700 | 17 | if (in_array('all', $cascades, true)) { |
|
| 701 | $cascades = $allCascades; |
||
| 702 | 17 | } elseif (count($cascades) !== count(array_intersect($cascades, $allCascades))) { |
|
| 703 | throw new MappingException('Invalid cascades: ' . implode(', ', $cascades)); |
||
| 704 | } |
||
| 705 | 17 | $mapping['cascade'] = $cascades; |
|
| 706 | 17 | $mapping['isCascadeRemove'] = in_array('remove', $cascades, true); |
|
| 707 | 17 | $mapping['isCascadePersist'] = in_array('persist', $cascades, true); |
|
| 708 | 17 | $mapping['isCascadeRefresh'] = in_array('refresh', $cascades, true); |
|
| 709 | 17 | $mapping['isCascadeMerge'] = in_array('merge', $cascades, true); |
|
| 710 | 17 | $mapping['isCascadeDetach'] = in_array('detach', $cascades, true); |
|
| 711 | |||
| 712 | 17 | return $mapping; |
|
| 713 | } |
||
| 714 | |||
| 715 | 17 | private function storeMapping(array $mapping) |
|
| 716 | { |
||
| 717 | 17 | $this->assertFieldNotMapped($mapping['field']); |
|
| 718 | |||
| 719 | 17 | $this->apiFieldNames[$mapping['field']] = $mapping['api_field']; |
|
| 720 | 17 | $this->fieldNames[$mapping['api_field']] = $mapping['field']; |
|
| 721 | 17 | $this->associations[$mapping['field']] = $mapping; |
|
| 722 | 17 | } |
|
| 723 | |||
| 724 | 25 | private function validateAndCompleteFieldMapping(array &$mapping) |
|
| 725 | { |
||
| 726 | 25 | if (!array_key_exists('api_field', $mapping)) { |
|
| 727 | 24 | $mapping['api_field'] = $mapping['field']; //todo: invent naming strategy |
|
| 728 | 24 | } |
|
| 729 | |||
| 730 | 25 | $this->apiFieldNames[$mapping['field']] = $mapping['api_field']; |
|
| 731 | 25 | $this->fieldNames[$mapping['api_field']] = $mapping['field']; |
|
| 732 | |||
| 733 | // if (isset($this->fieldNames[$mapping['columnName']]) || ($this->discriminatorField && $this->discriminatorField['name'] === $mapping['api_field'])) { |
||
|
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
67% of this comment could be valid code. Did you maybe forget this after debugging?
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it. The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production. This check looks for comments that seem to be mostly valid code and reports them. Loading history...
|
|||
| 734 | // throw MappingException::duplicateColumnName($this->name, $mapping['columnName']); |
||
| 735 | // } |
||
| 736 | |||
| 737 | // Complete id mapping |
||
| 738 | 25 | if (isset($mapping['id']) && $mapping['id'] === true) { |
|
| 739 | 25 | if (!in_array($mapping['field'], $this->identifier, true)) { |
|
| 740 | 25 | $this->identifier[] = $mapping['field']; |
|
| 741 | 25 | } |
|
| 742 | // Check for composite key |
||
| 743 | 25 | if (!$this->isIdentifierComposite && count($this->identifier) > 1) { |
|
| 744 | 1 | $this->isIdentifierComposite = true; |
|
| 745 | 1 | } |
|
| 746 | 25 | } |
|
| 747 | 25 | } |
|
| 748 | |||
| 749 | /** |
||
| 750 | * @param string $fieldName |
||
| 751 | * |
||
| 752 | * @throws MappingException |
||
| 753 | */ |
||
| 754 | 25 | private function assertFieldNotMapped($fieldName) |
|
| 755 | { |
||
| 756 | 25 | if (array_key_exists($fieldName, $this->fields) || |
|
| 757 | 25 | array_key_exists($fieldName, $this->associations) || |
|
| 758 | 25 | array_key_exists($fieldName, $this->identifier) |
|
| 759 | 25 | ) { |
|
| 760 | throw new MappingException('Field already mapped'); |
||
| 761 | } |
||
| 762 | 25 | } |
|
| 763 | |||
| 764 | /** |
||
| 765 | * @param array $mapping |
||
| 766 | * |
||
| 767 | * @return array |
||
| 768 | * @throws MappingException |
||
| 769 | * @throws \InvalidArgumentException |
||
| 770 | */ |
||
| 771 | 17 | private function validateAndCompleteOneToManyMapping(array $mapping) |
|
| 772 | { |
||
| 773 | 17 | $mapping = $this->validateAndCompleteAssociationMapping($mapping); |
|
| 774 | |||
| 775 | // OneToMany-side MUST be inverse (must have mappedBy) |
||
| 776 | 17 | if (!isset($mapping['mappedBy'])) { |
|
| 777 | throw new MappingException( |
||
| 778 | sprintf('Many to many requires mapped by: %s', $mapping['field']) |
||
| 779 | ); |
||
| 780 | } |
||
| 781 | 17 | $mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) && $mapping['orphanRemoval']; |
|
| 782 | 17 | $mapping['isCascadeRemove'] = $mapping['orphanRemoval'] || $mapping['isCascadeRemove']; |
|
| 783 | 17 | $this->assertMappingOrderBy($mapping); |
|
| 784 | |||
| 785 | 17 | return $mapping; |
|
| 786 | } |
||
| 787 | |||
| 788 | /** |
||
| 789 | * @param array $mapping |
||
| 790 | * |
||
| 791 | * @throws \InvalidArgumentException |
||
| 792 | */ |
||
| 793 | 17 | private function assertMappingOrderBy(array $mapping) |
|
| 794 | { |
||
| 795 | 17 | if (array_key_exists('orderBy', $mapping) && !is_array($mapping['orderBy'])) { |
|
| 796 | throw new \InvalidArgumentException( |
||
| 797 | "'orderBy' is expected to be an array, not " . gettype($mapping['orderBy']) |
||
| 798 | ); |
||
| 799 | } |
||
| 800 | 17 | } |
|
| 801 | |||
| 802 | /** |
||
| 803 | * @param array $mapping |
||
| 804 | * |
||
| 805 | * @return array |
||
| 806 | */ |
||
| 807 | 17 | private function validateAndCompleteOneToOneMapping(array $mapping) |
|
| 808 | { |
||
| 809 | 17 | $mapping = $this->validateAndCompleteAssociationMapping($mapping); |
|
| 810 | |||
| 811 | 17 | $mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) && $mapping['orphanRemoval']; |
|
| 812 | 17 | $mapping['isCascadeRemove'] = $mapping['orphanRemoval'] || $mapping['isCascadeRemove']; |
|
| 813 | 17 | if ($mapping['orphanRemoval']) { |
|
| 814 | unset($mapping['unique']); |
||
| 815 | } |
||
| 816 | |||
| 817 | 17 | return $mapping; |
|
| 818 | } |
||
| 819 | |||
| 820 | /** {@inheritdoc} */ |
||
| 821 | 23 | public function getDiscriminatorField() |
|
| 822 | { |
||
| 823 | 23 | return $this->discriminatorField; |
|
|
0 ignored issues
–
show
The return type of
return $this->discriminatorField; (string[]) is incompatible with the return type declared by the interface Bankiru\Api\Doctrine\Map...::getDiscriminatorField of type string[]|null.
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design. Let’s take a look at an example: class Author {
private $name;
public function __construct($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
}
abstract class Post {
public function getAuthor() {
return 'Johannes';
}
}
class BlogPost extends Post {
public function getAuthor() {
return new Author('Johannes');
}
}
class ForumPost extends Post { /* ... */ }
function my_function(Post $post) {
echo strtoupper($post->getAuthor());
}
Our function Loading history...
|
|||
| 824 | } |
||
| 825 | |||
| 826 | /** {@inheritdoc} */ |
||
| 827 | 16 | public function getDiscriminatorMap() |
|
| 828 | { |
||
| 829 | 16 | return $this->discriminatorMap; |
|
|
0 ignored issues
–
show
The return type of
return $this->discriminatorMap; (string[]) is incompatible with the return type declared by the interface Bankiru\Api\Doctrine\Map...ta::getDiscriminatorMap of type string[].
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design. Let’s take a look at an example: class Author {
private $name;
public function __construct($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
}
abstract class Post {
public function getAuthor() {
return 'Johannes';
}
}
class BlogPost extends Post {
public function getAuthor() {
return new Author('Johannes');
}
}
class ForumPost extends Post { /* ... */ }
function my_function(Post $post) {
echo strtoupper($post->getAuthor());
}
Our function Loading history...
|
|||
| 830 | } |
||
| 831 | |||
| 832 | /** {@inheritdoc} */ |
||
| 833 | 17 | public function getDiscriminatorValue() |
|
| 834 | { |
||
| 835 | 17 | return $this->discriminatorValue; |
|
| 836 | } |
||
| 837 | } |
||
| 838 |
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.
Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..