edmondscommerce /
doctrine-static-meta
| 1 | <?php declare(strict_types=1); |
||||
| 2 | |||||
| 3 | namespace EdmondsCommerce\DoctrineStaticMeta\CodeGeneration; |
||||
| 4 | |||||
| 5 | use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Command\AbstractCommand; |
||||
| 6 | use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\AbstractGenerator; |
||||
| 7 | use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\RelationsGenerator; |
||||
| 8 | use EdmondsCommerce\DoctrineStaticMeta\Config; |
||||
| 9 | use EdmondsCommerce\DoctrineStaticMeta\Exception\DoctrineStaticMetaException; |
||||
| 10 | use EdmondsCommerce\DoctrineStaticMeta\MappingHelper; |
||||
| 11 | |||||
| 12 | /** |
||||
| 13 | * Class NamespaceHelper |
||||
| 14 | * |
||||
| 15 | * Pure functions for working with namespaces and to calculate namespaces |
||||
| 16 | * |
||||
| 17 | * @package EdmondsCommerce\DoctrineStaticMeta\CodeGeneration |
||||
| 18 | * @SuppressWarnings(PHPMD.TooManyPublicMethods) |
||||
| 19 | * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) |
||||
| 20 | * @SuppressWarnings(PHPMD.ExcessivePublicCount) |
||||
| 21 | */ |
||||
| 22 | class NamespaceHelper |
||||
| 23 | { |
||||
| 24 | public function swapSuffix(string $fqn, string $currentSuffix, string $newSuffix): string |
||||
| 25 | { |
||||
| 26 | return $this->cropSuffix($fqn, $currentSuffix) . $newSuffix; |
||||
| 27 | } |
||||
| 28 | |||||
| 29 | /** |
||||
| 30 | * Crop a suffix from an FQN if it is there. |
||||
| 31 | * |
||||
| 32 | * If it is not there, do nothing and return the FQN as is |
||||
| 33 | * |
||||
| 34 | * @param string $fqn |
||||
| 35 | * @param string $suffix |
||||
| 36 | * |
||||
| 37 | * @return string |
||||
| 38 | */ |
||||
| 39 | public function cropSuffix(string $fqn, string $suffix): string |
||||
| 40 | { |
||||
| 41 | if ($suffix === \substr($fqn, -\strlen($suffix))) { |
||||
| 42 | return \substr($fqn, 0, -\strlen($suffix)); |
||||
| 43 | } |
||||
| 44 | |||||
| 45 | return $fqn; |
||||
| 46 | } |
||||
| 47 | |||||
| 48 | public function getEmbeddableObjectFqnFromEmbeddableObjectInterfaceFqn(string $interfaceFqn): string |
||||
| 49 | { |
||||
| 50 | return \str_replace( |
||||
| 51 | ['\\Interfaces\\', 'Interface'], |
||||
| 52 | ['\\', ''], |
||||
| 53 | $interfaceFqn |
||||
| 54 | ); |
||||
| 55 | } |
||||
| 56 | |||||
| 57 | /** |
||||
| 58 | * @param mixed|object $object |
||||
| 59 | * |
||||
| 60 | * @return string |
||||
| 61 | */ |
||||
| 62 | public function getObjectShortName($object): string |
||||
| 63 | { |
||||
| 64 | return $this->getClassShortName($this->getObjectFqn($object)); |
||||
| 65 | } |
||||
| 66 | |||||
| 67 | /** |
||||
| 68 | * @param string $className |
||||
| 69 | * |
||||
| 70 | * @return string |
||||
| 71 | */ |
||||
| 72 | public function getClassShortName(string $className): string |
||||
| 73 | { |
||||
| 74 | $exp = explode('\\', $className); |
||||
| 75 | |||||
| 76 | return end($exp); |
||||
| 77 | } |
||||
| 78 | |||||
| 79 | /** |
||||
| 80 | * @param mixed|object $object |
||||
| 81 | * |
||||
| 82 | * @see https://gist.github.com/ludofleury/1708784 |
||||
| 83 | * @return string |
||||
| 84 | */ |
||||
| 85 | public function getObjectFqn($object): string |
||||
| 86 | { |
||||
| 87 | return \get_class($object); |
||||
| 88 | } |
||||
| 89 | |||||
| 90 | /** |
||||
| 91 | * Get the basename of a namespace |
||||
| 92 | * |
||||
| 93 | * @param string $namespace |
||||
| 94 | * |
||||
| 95 | * @return string |
||||
| 96 | */ |
||||
| 97 | public function basename(string $namespace): string |
||||
| 98 | { |
||||
| 99 | $strrpos = \strrpos($namespace, '\\'); |
||||
| 100 | if (false === $strrpos) { |
||||
| 101 | return $namespace; |
||||
| 102 | } |
||||
| 103 | |||||
| 104 | return $this->tidy(\substr($namespace, $strrpos + 1)); |
||||
| 105 | } |
||||
| 106 | |||||
| 107 | /** |
||||
| 108 | * Checks and tidies up a given namespace |
||||
| 109 | * |
||||
| 110 | * @param string $namespace |
||||
| 111 | * |
||||
| 112 | * @return string |
||||
| 113 | * @throws \RuntimeException |
||||
| 114 | */ |
||||
| 115 | public function tidy(string $namespace): string |
||||
| 116 | { |
||||
| 117 | if (\ts\stringContains($namespace, '/')) { |
||||
| 118 | throw new \RuntimeException('Invalid namespace ' . $namespace); |
||||
| 119 | } |
||||
| 120 | #remove repeated separators |
||||
| 121 | $namespace = preg_replace( |
||||
| 122 | '#' . '\\\\' . '+#', |
||||
| 123 | '\\', |
||||
| 124 | $namespace |
||||
| 125 | ); |
||||
| 126 | |||||
| 127 | return $namespace; |
||||
| 128 | } |
||||
| 129 | |||||
| 130 | /** |
||||
| 131 | * Get the fully qualified name of the Fixture class for a specified Entity fully qualified name |
||||
| 132 | * |
||||
| 133 | * @param string $entityFqn |
||||
| 134 | * |
||||
| 135 | * @return string |
||||
| 136 | */ |
||||
| 137 | public function getFixtureFqnFromEntityFqn(string $entityFqn): string |
||||
| 138 | { |
||||
| 139 | return \str_replace( |
||||
| 140 | '\\Entities', |
||||
| 141 | '\\Assets\\Entity\\Fixtures', |
||||
| 142 | $entityFqn |
||||
| 143 | ) . 'Fixture'; |
||||
| 144 | } |
||||
| 145 | |||||
| 146 | /** |
||||
| 147 | * Get the fully qualified name of the Entity for a specified Entity fully qualified name |
||||
| 148 | * |
||||
| 149 | * @param string $fixtureFqn |
||||
| 150 | * |
||||
| 151 | * @return string |
||||
| 152 | */ |
||||
| 153 | public function getEntityFqnFromFixtureFqn(string $fixtureFqn): string |
||||
| 154 | { |
||||
| 155 | return \substr( |
||||
| 156 | \str_replace( |
||||
| 157 | '\\Assets\\Entity\\Fixtures', |
||||
| 158 | '\\Entities', |
||||
| 159 | $fixtureFqn |
||||
| 160 | ), |
||||
| 161 | 0, |
||||
| 162 | -\strlen('Fixture') |
||||
| 163 | ); |
||||
| 164 | } |
||||
| 165 | |||||
| 166 | /** |
||||
| 167 | * Get the namespace root up to and including a specified directory |
||||
| 168 | * |
||||
| 169 | * @param string $fqn |
||||
| 170 | * @param string $directory |
||||
| 171 | * |
||||
| 172 | * @return null|string |
||||
| 173 | */ |
||||
| 174 | public function getNamespaceRootToDirectoryFromFqn(string $fqn, string $directory): ?string |
||||
| 175 | { |
||||
| 176 | $strPos = \strrpos( |
||||
| 177 | $fqn, |
||||
| 178 | $directory |
||||
| 179 | ); |
||||
| 180 | if (false !== $strPos) { |
||||
| 181 | return $this->tidy(\substr($fqn, 0, $strPos + \strlen($directory))); |
||||
| 182 | } |
||||
| 183 | |||||
| 184 | return null; |
||||
| 185 | } |
||||
| 186 | |||||
| 187 | /** |
||||
| 188 | * Get the sub path for an Entity file, start from the Entities path - normally `/path/to/project/src/Entities` |
||||
| 189 | * |
||||
| 190 | * @param string $entityFqn |
||||
| 191 | * |
||||
| 192 | * @return string |
||||
| 193 | */ |
||||
| 194 | public function getEntityFileSubPath( |
||||
| 195 | string $entityFqn |
||||
| 196 | ): string { |
||||
| 197 | return $this->getEntitySubPath($entityFqn) . '.php'; |
||||
| 198 | } |
||||
| 199 | |||||
| 200 | /** |
||||
| 201 | * Get the folder structure for an Entity, start from the Entities path - normally `/path/to/project/src/Entities` |
||||
| 202 | * |
||||
| 203 | * This is not the path to the file, but the sub path of directories for storing entity related items. |
||||
| 204 | * |
||||
| 205 | * @param string $entityFqn |
||||
| 206 | * |
||||
| 207 | * @return string |
||||
| 208 | */ |
||||
| 209 | public function getEntitySubPath( |
||||
| 210 | string $entityFqn |
||||
| 211 | ): string { |
||||
| 212 | $entityPath = str_replace( |
||||
| 213 | '\\', |
||||
| 214 | '/', |
||||
| 215 | $this->getEntitySubNamespace($entityFqn) |
||||
| 216 | ); |
||||
| 217 | |||||
| 218 | return '/' . $entityPath; |
||||
| 219 | } |
||||
| 220 | |||||
| 221 | /** |
||||
| 222 | * Get the Namespace for an Entity, start from the Entities Fully Qualified Name base - normally |
||||
| 223 | * `\My\Project\Entities\` |
||||
| 224 | * |
||||
| 225 | * @param string $entityFqn |
||||
| 226 | * |
||||
| 227 | * @return string |
||||
| 228 | */ |
||||
| 229 | public function getEntitySubNamespace( |
||||
| 230 | string $entityFqn |
||||
| 231 | ): string { |
||||
| 232 | return $this->tidy( |
||||
| 233 | \substr( |
||||
| 234 | $entityFqn, |
||||
| 235 | \strrpos( |
||||
| 236 | $entityFqn, |
||||
| 237 | '\\' . AbstractGenerator::ENTITIES_FOLDER_NAME . '\\' |
||||
| 238 | ) |
||||
| 239 | + \strlen('\\' . AbstractGenerator::ENTITIES_FOLDER_NAME . '\\') |
||||
| 240 | ) |
||||
| 241 | ); |
||||
| 242 | } |
||||
| 243 | |||||
| 244 | /** |
||||
| 245 | * Get the Fully Qualified Namespace root for Traits for the specified Entity |
||||
| 246 | * |
||||
| 247 | * @param string $entityFqn |
||||
| 248 | * |
||||
| 249 | * @return string |
||||
| 250 | */ |
||||
| 251 | public function getTraitsNamespaceForEntity( |
||||
| 252 | string $entityFqn |
||||
| 253 | ): string { |
||||
| 254 | $traitsNamespace = $this->getProjectNamespaceRootFromEntityFqn($entityFqn) |
||||
| 255 | . AbstractGenerator::ENTITY_RELATIONS_NAMESPACE |
||||
| 256 | . '\\' . $this->getEntitySubNamespace($entityFqn) |
||||
| 257 | . '\\Traits'; |
||||
| 258 | |||||
| 259 | return $traitsNamespace; |
||||
| 260 | } |
||||
| 261 | |||||
| 262 | /** |
||||
| 263 | * Use the fully qualified name of two Entities to calculate the Project Namespace Root |
||||
| 264 | * |
||||
| 265 | * - note: this assumes a single namespace level for entities, eg `Entities` |
||||
| 266 | * |
||||
| 267 | * @param string $entityFqn |
||||
| 268 | * |
||||
| 269 | * @return string |
||||
| 270 | */ |
||||
| 271 | public function getProjectNamespaceRootFromEntityFqn(string $entityFqn): string |
||||
| 272 | { |
||||
| 273 | return $this->tidy( |
||||
| 274 | \substr( |
||||
| 275 | $entityFqn, |
||||
| 276 | 0, |
||||
| 277 | \strrpos( |
||||
| 278 | $entityFqn, |
||||
| 279 | '\\' . AbstractGenerator::ENTITIES_FOLDER_NAME . '\\' |
||||
| 280 | ) |
||||
| 281 | ) |
||||
| 282 | ); |
||||
| 283 | } |
||||
| 284 | |||||
| 285 | /** |
||||
| 286 | * Get the Fully Qualified Namespace for the "HasEntities" interface for the specified Entity |
||||
| 287 | * |
||||
| 288 | * @param string $entityFqn |
||||
| 289 | * |
||||
| 290 | * @return string |
||||
| 291 | */ |
||||
| 292 | public function getHasPluralInterfaceFqnForEntity( |
||||
| 293 | string $entityFqn |
||||
| 294 | ): string { |
||||
| 295 | $interfaceNamespace = $this->getInterfacesNamespaceForEntity($entityFqn); |
||||
| 296 | |||||
| 297 | return $interfaceNamespace . '\\Has' . ucfirst($entityFqn::getDoctrineStaticMeta()->getPlural()) . 'Interface'; |
||||
| 298 | } |
||||
| 299 | |||||
| 300 | /** |
||||
| 301 | * Get the Fully Qualified Namespace root for Interfaces for the specified Entity |
||||
| 302 | * |
||||
| 303 | * @param string $entityFqn |
||||
| 304 | * |
||||
| 305 | * @return string |
||||
| 306 | */ |
||||
| 307 | public function getInterfacesNamespaceForEntity( |
||||
| 308 | string $entityFqn |
||||
| 309 | ): string { |
||||
| 310 | $interfacesNamespace = $this->getProjectNamespaceRootFromEntityFqn($entityFqn) |
||||
| 311 | . AbstractGenerator::ENTITY_RELATIONS_NAMESPACE |
||||
| 312 | . '\\' . $this->getEntitySubNamespace($entityFqn) |
||||
| 313 | . '\\Interfaces'; |
||||
| 314 | |||||
| 315 | return $this->tidy($interfacesNamespace); |
||||
| 316 | } |
||||
| 317 | |||||
| 318 | /** |
||||
| 319 | * Get the Fully Qualified Namespace for the "HasEntity" interface for the specified Entity |
||||
| 320 | * |
||||
| 321 | * @param string $entityFqn |
||||
| 322 | * |
||||
| 323 | * @return string |
||||
| 324 | * @throws DoctrineStaticMetaException |
||||
| 325 | */ |
||||
| 326 | public function getHasSingularInterfaceFqnForEntity( |
||||
| 327 | string $entityFqn |
||||
| 328 | ): string { |
||||
| 329 | try { |
||||
| 330 | $interfaceNamespace = $this->getInterfacesNamespaceForEntity($entityFqn); |
||||
| 331 | |||||
| 332 | return $interfaceNamespace . '\\Has' . ucfirst($entityFqn::getDoctrineStaticMeta()->getSingular()) |
||||
| 333 | . 'Interface'; |
||||
| 334 | } catch (\Exception $e) { |
||||
| 335 | throw new DoctrineStaticMetaException( |
||||
| 336 | 'Exception in ' . __METHOD__ . ': ' . $e->getMessage(), |
||||
| 337 | $e->getCode(), |
||||
| 338 | $e |
||||
| 339 | ); |
||||
| 340 | } |
||||
| 341 | } |
||||
| 342 | |||||
| 343 | /** |
||||
| 344 | * Get the Fully Qualified Namespace for the Relation Trait for a specific Entity and hasType |
||||
| 345 | * |
||||
| 346 | * @param string $hasType |
||||
| 347 | * @param string $ownedEntityFqn |
||||
| 348 | * @param string|null $projectRootNamespace |
||||
| 349 | * @param string $srcFolder |
||||
| 350 | * |
||||
| 351 | * @return string |
||||
| 352 | * @throws DoctrineStaticMetaException |
||||
| 353 | */ |
||||
| 354 | public function getOwningTraitFqn( |
||||
| 355 | string $hasType, |
||||
| 356 | string $ownedEntityFqn, |
||||
| 357 | ?string $projectRootNamespace = null, |
||||
| 358 | string $srcFolder = AbstractCommand::DEFAULT_SRC_SUBFOLDER |
||||
| 359 | ): string { |
||||
| 360 | try { |
||||
| 361 | $ownedHasName = $this->getOwnedHasName($hasType, $ownedEntityFqn, $srcFolder, $projectRootNamespace); |
||||
|
0 ignored issues
–
show
Bug
introduced
by
Loading history...
|
|||||
| 362 | if (null === $projectRootNamespace) { |
||||
| 363 | $projectRootNamespace = $this->getProjectRootNamespaceFromComposerJson($srcFolder); |
||||
| 364 | } |
||||
| 365 | list($ownedClassName, , $ownedSubDirectories) = $this->parseFullyQualifiedName( |
||||
| 366 | $ownedEntityFqn, |
||||
| 367 | $srcFolder, |
||||
| 368 | $projectRootNamespace |
||||
| 369 | ); |
||||
| 370 | $traitSubDirectories = \array_slice($ownedSubDirectories, 2); |
||||
| 371 | $owningTraitFqn = $this->getOwningRelationsRootFqn( |
||||
| 372 | $projectRootNamespace, |
||||
| 373 | $traitSubDirectories |
||||
| 374 | ); |
||||
| 375 | $required = \ts\stringContains($hasType, RelationsGenerator::PREFIX_REQUIRED) |
||||
| 376 | ? RelationsGenerator::PREFIX_REQUIRED |
||||
| 377 | : ''; |
||||
| 378 | $owningTraitFqn .= $ownedClassName . '\\Traits\\Has' . $required . $ownedHasName |
||||
| 379 | . '\\' . $this->getBaseHasTypeTraitFqn($ownedHasName, $hasType); |
||||
| 380 | |||||
| 381 | return $this->tidy($owningTraitFqn); |
||||
| 382 | } catch (\Exception $e) { |
||||
| 383 | throw new DoctrineStaticMetaException( |
||||
| 384 | 'Exception in ' . __METHOD__ . ': ' . $e->getMessage(), |
||||
| 385 | $e->getCode(), |
||||
| 386 | $e |
||||
| 387 | ); |
||||
| 388 | } |
||||
| 389 | } |
||||
| 390 | |||||
| 391 | /** |
||||
| 392 | * Based on the $hasType, we calculate exactly what type of `Has` we have |
||||
| 393 | * |
||||
| 394 | * @param string $hasType |
||||
| 395 | * @param string $ownedEntityFqn |
||||
| 396 | * @param string $srcOrTestSubFolder |
||||
| 397 | * |
||||
| 398 | * @param string $projectRootNamespace |
||||
| 399 | * |
||||
| 400 | * @return string |
||||
| 401 | * @SuppressWarnings(PHPMD.StaticAccess) |
||||
| 402 | * @throws \EdmondsCommerce\DoctrineStaticMeta\Exception\DoctrineStaticMetaException |
||||
| 403 | */ |
||||
| 404 | public function getOwnedHasName( |
||||
| 405 | string $hasType, |
||||
| 406 | string $ownedEntityFqn, |
||||
| 407 | string $srcOrTestSubFolder, |
||||
| 408 | string $projectRootNamespace |
||||
| 409 | ): string { |
||||
| 410 | $parsedFqn = $this->parseFullyQualifiedName( |
||||
| 411 | $ownedEntityFqn, |
||||
| 412 | $srcOrTestSubFolder, |
||||
| 413 | $projectRootNamespace |
||||
| 414 | ); |
||||
| 415 | |||||
| 416 | $subDirectories = $parsedFqn[2]; |
||||
| 417 | |||||
| 418 | if (\in_array( |
||||
| 419 | $hasType, |
||||
| 420 | RelationsGenerator::HAS_TYPES_PLURAL, |
||||
| 421 | true |
||||
| 422 | )) { |
||||
| 423 | return $this->getPluralNamespacedName($ownedEntityFqn, $subDirectories); |
||||
| 424 | } |
||||
| 425 | |||||
| 426 | return $this->getSingularNamespacedName($ownedEntityFqn, $subDirectories); |
||||
| 427 | } |
||||
| 428 | |||||
| 429 | /** |
||||
| 430 | * From the fully qualified name, parse out: |
||||
| 431 | * - class name, |
||||
| 432 | * - namespace |
||||
| 433 | * - the namespace parts not including the project root namespace |
||||
| 434 | * |
||||
| 435 | * @param string $fqn |
||||
| 436 | * |
||||
| 437 | * @param string $srcOrTestSubFolder eg 'src' or 'test' |
||||
| 438 | * |
||||
| 439 | * @param string|null $projectRootNamespace |
||||
| 440 | * |
||||
| 441 | * @return array [$className,$namespace,$subDirectories] |
||||
| 442 | * @throws DoctrineStaticMetaException |
||||
| 443 | */ |
||||
| 444 | public function parseFullyQualifiedName( |
||||
| 445 | string $fqn, |
||||
| 446 | string $srcOrTestSubFolder = AbstractCommand::DEFAULT_SRC_SUBFOLDER, |
||||
| 447 | string $projectRootNamespace = null |
||||
| 448 | ): array { |
||||
| 449 | try { |
||||
| 450 | $fqn = $this->root($fqn); |
||||
| 451 | if (null === $projectRootNamespace) { |
||||
| 452 | $projectRootNamespace = $this->getProjectRootNamespaceFromComposerJson($srcOrTestSubFolder); |
||||
| 453 | } |
||||
| 454 | $projectRootNamespace = $this->root($projectRootNamespace); |
||||
| 455 | if (false === \ts\stringContains($fqn, $projectRootNamespace)) { |
||||
| 456 | throw new DoctrineStaticMetaException( |
||||
| 457 | 'The $fqn [' . $fqn . '] does not contain the project root namespace' |
||||
| 458 | . ' [' . $projectRootNamespace . '] - are you sure it is the correct FQN?' |
||||
| 459 | ); |
||||
| 460 | } |
||||
| 461 | $fqnParts = explode('\\', $fqn); |
||||
| 462 | $className = array_pop($fqnParts); |
||||
| 463 | $namespace = implode('\\', $fqnParts); |
||||
| 464 | $rootParts = explode('\\', $projectRootNamespace); |
||||
| 465 | $subDirectories = []; |
||||
| 466 | foreach ($fqnParts as $k => $fqnPart) { |
||||
| 467 | if (isset($rootParts[$k]) && $rootParts[$k] === $fqnPart) { |
||||
| 468 | continue; |
||||
| 469 | } |
||||
| 470 | $subDirectories[] = $fqnPart; |
||||
| 471 | } |
||||
| 472 | array_unshift($subDirectories, $srcOrTestSubFolder); |
||||
| 473 | |||||
| 474 | return [ |
||||
| 475 | $className, |
||||
| 476 | $this->root($namespace), |
||||
| 477 | $subDirectories, |
||||
| 478 | ]; |
||||
| 479 | } catch (\Exception $e) { |
||||
| 480 | throw new DoctrineStaticMetaException( |
||||
| 481 | 'Exception in ' . __METHOD__ . ': ' . $e->getMessage(), |
||||
| 482 | $e->getCode(), |
||||
| 483 | $e |
||||
| 484 | ); |
||||
| 485 | } |
||||
| 486 | } |
||||
| 487 | |||||
| 488 | /** |
||||
| 489 | * Generate a tidy root namespace without a leading \ |
||||
| 490 | * |
||||
| 491 | * @param string $namespace |
||||
| 492 | * |
||||
| 493 | * @return string |
||||
| 494 | */ |
||||
| 495 | public function root(string $namespace): string |
||||
| 496 | { |
||||
| 497 | return $this->tidy(ltrim($namespace, '\\')); |
||||
| 498 | } |
||||
| 499 | |||||
| 500 | /** |
||||
| 501 | * Read src autoloader from composer json |
||||
| 502 | * |
||||
| 503 | * @param string $dirForNamespace |
||||
| 504 | * |
||||
| 505 | * @return string |
||||
| 506 | * @throws DoctrineStaticMetaException |
||||
| 507 | * @SuppressWarnings(PHPMD.StaticAccess) |
||||
| 508 | */ |
||||
| 509 | public function getProjectRootNamespaceFromComposerJson( |
||||
| 510 | string $dirForNamespace = 'src' |
||||
| 511 | ): string { |
||||
| 512 | try { |
||||
| 513 | $dirForNamespace = trim($dirForNamespace, '/'); |
||||
| 514 | $jsonPath = Config::getProjectRootDirectory() . '/composer.json'; |
||||
| 515 | $json = json_decode(\ts\file_get_contents($jsonPath), true); |
||||
| 516 | if (JSON_ERROR_NONE !== json_last_error()) { |
||||
| 517 | throw new \RuntimeException( |
||||
| 518 | 'Error decoding json from path ' . $jsonPath . ' , ' . json_last_error_msg() |
||||
| 519 | ); |
||||
| 520 | } |
||||
| 521 | /** |
||||
| 522 | * @var string[][][][] $json |
||||
| 523 | */ |
||||
| 524 | if (isset($json['autoload']['psr-4'])) { |
||||
| 525 | foreach ($json['autoload']['psr-4'] as $namespace => $dirs) { |
||||
| 526 | foreach ($dirs as $dir) { |
||||
| 527 | $dir = trim($dir, '/'); |
||||
| 528 | if ($dir === $dirForNamespace) { |
||||
| 529 | return $this->tidy(rtrim($namespace, '\\')); |
||||
| 530 | } |
||||
| 531 | } |
||||
| 532 | } |
||||
| 533 | } |
||||
| 534 | } catch (\Exception $e) { |
||||
| 535 | throw new DoctrineStaticMetaException( |
||||
| 536 | 'Exception in ' . __METHOD__ . ': ' . $e->getMessage(), |
||||
| 537 | $e->getCode(), |
||||
| 538 | $e |
||||
| 539 | ); |
||||
| 540 | } |
||||
| 541 | throw new DoctrineStaticMetaException('Failed to find psr-4 namespace root'); |
||||
| 542 | } |
||||
| 543 | |||||
| 544 | /** |
||||
| 545 | * @param string $entityFqn |
||||
| 546 | * @param array $subDirectories |
||||
| 547 | * |
||||
| 548 | * @return string |
||||
| 549 | * @SuppressWarnings(PHPMD.StaticAccess) |
||||
| 550 | */ |
||||
| 551 | public function getPluralNamespacedName(string $entityFqn, array $subDirectories): string |
||||
| 552 | { |
||||
| 553 | $plural = \ucfirst(MappingHelper::getPluralForFqn($entityFqn)); |
||||
| 554 | |||||
| 555 | return $this->getNamespacedName($plural, $subDirectories); |
||||
| 556 | } |
||||
| 557 | |||||
| 558 | /** |
||||
| 559 | * @param string $entityName |
||||
| 560 | * @param array $subDirectories |
||||
| 561 | * |
||||
| 562 | * @return string |
||||
| 563 | */ |
||||
| 564 | public function getNamespacedName(string $entityName, array $subDirectories): string |
||||
| 565 | { |
||||
| 566 | $noEntitiesDirectory = \array_slice($subDirectories, 2); |
||||
| 567 | $namespacedName = \array_merge($noEntitiesDirectory, [$entityName]); |
||||
| 568 | |||||
| 569 | return \ucfirst(\implode('', $namespacedName)); |
||||
| 570 | } |
||||
| 571 | |||||
| 572 | /** |
||||
| 573 | * @param string $entityFqn |
||||
| 574 | * @param array $subDirectories |
||||
| 575 | * |
||||
| 576 | * @return string |
||||
| 577 | * @SuppressWarnings(PHPMD.StaticAccess) |
||||
| 578 | */ |
||||
| 579 | public function getSingularNamespacedName(string $entityFqn, array $subDirectories): string |
||||
| 580 | { |
||||
| 581 | $singular = \ucfirst(MappingHelper::getSingularForFqn($entityFqn)); |
||||
| 582 | |||||
| 583 | return $this->getNamespacedName($singular, $subDirectories); |
||||
| 584 | } |
||||
| 585 | |||||
| 586 | /** |
||||
| 587 | * Get the Namespace root for Entity Relations |
||||
| 588 | * |
||||
| 589 | * @param string $projectRootNamespace |
||||
| 590 | * @param array $subDirectories |
||||
| 591 | * |
||||
| 592 | * @return string |
||||
| 593 | */ |
||||
| 594 | public function getOwningRelationsRootFqn( |
||||
| 595 | string $projectRootNamespace, |
||||
| 596 | array $subDirectories |
||||
| 597 | ): string { |
||||
| 598 | $relationsRootFqn = $projectRootNamespace |
||||
| 599 | . AbstractGenerator::ENTITY_RELATIONS_NAMESPACE . '\\'; |
||||
| 600 | if (count($subDirectories) > 0) { |
||||
| 601 | $relationsRootFqn .= implode('\\', $subDirectories) . '\\'; |
||||
| 602 | } |
||||
| 603 | |||||
| 604 | return $this->tidy($relationsRootFqn); |
||||
| 605 | } |
||||
| 606 | |||||
| 607 | /** |
||||
| 608 | * Normalise a has type, removing prefixes that are not required |
||||
| 609 | * |
||||
| 610 | * Inverse hasTypes use the standard template without the prefix |
||||
| 611 | * The exclusion ot this are the ManyToMany and OneToOne relations |
||||
| 612 | * |
||||
| 613 | * @param string $ownedHasName |
||||
| 614 | * @param string $hasType |
||||
| 615 | * |
||||
| 616 | * @return string |
||||
| 617 | */ |
||||
| 618 | public function getBaseHasTypeTraitFqn( |
||||
| 619 | string $ownedHasName, |
||||
| 620 | string $hasType |
||||
| 621 | ): string { |
||||
| 622 | $required = \ts\stringContains($hasType, RelationsGenerator::PREFIX_REQUIRED) |
||||
| 623 | ? RelationsGenerator::PREFIX_REQUIRED |
||||
| 624 | : ''; |
||||
| 625 | |||||
| 626 | $hasType = \str_replace(RelationsGenerator::PREFIX_REQUIRED, '', $hasType); |
||||
| 627 | foreach ([ |
||||
| 628 | RelationsGenerator::INTERNAL_TYPE_MANY_TO_MANY, |
||||
| 629 | RelationsGenerator::INTERNAL_TYPE_ONE_TO_ONE, |
||||
| 630 | ] as $noStrip) { |
||||
| 631 | if (\ts\stringContains($hasType, $noStrip)) { |
||||
| 632 | return 'Has' . $required . $ownedHasName . $hasType; |
||||
| 633 | } |
||||
| 634 | } |
||||
| 635 | |||||
| 636 | foreach ([ |
||||
| 637 | RelationsGenerator::INTERNAL_TYPE_ONE_TO_MANY, |
||||
| 638 | RelationsGenerator::INTERNAL_TYPE_MANY_TO_ONE, |
||||
| 639 | ] as $stripAll) { |
||||
| 640 | if (\ts\stringContains($hasType, $stripAll)) { |
||||
| 641 | return str_replace( |
||||
| 642 | [ |
||||
| 643 | RelationsGenerator::PREFIX_OWNING, |
||||
| 644 | RelationsGenerator::PREFIX_INVERSE, |
||||
| 645 | ], |
||||
| 646 | '', |
||||
| 647 | 'Has' . $required . $ownedHasName . $hasType |
||||
| 648 | ); |
||||
| 649 | } |
||||
| 650 | } |
||||
| 651 | |||||
| 652 | return str_replace( |
||||
| 653 | [ |
||||
| 654 | RelationsGenerator::PREFIX_INVERSE, |
||||
| 655 | ], |
||||
| 656 | '', |
||||
| 657 | 'Has' . $required . $ownedHasName . $hasType |
||||
| 658 | ); |
||||
| 659 | } |
||||
| 660 | |||||
| 661 | public function getFactoryFqnFromEntityFqn(string $entityFqn): string |
||||
| 662 | { |
||||
| 663 | return $this->tidy( |
||||
| 664 | \str_replace( |
||||
| 665 | '\\' . AbstractGenerator::ENTITIES_FOLDER_NAME . '\\', |
||||
| 666 | '\\' . AbstractGenerator::ENTITY_FACTORIES_NAMESPACE . '\\', |
||||
| 667 | $entityFqn |
||||
| 668 | ) . 'Factory' |
||||
| 669 | ); |
||||
| 670 | } |
||||
| 671 | |||||
| 672 | public function getDtoFactoryFqnFromEntityFqn(string $entityFqn): string |
||||
| 673 | { |
||||
| 674 | return $this->tidy( |
||||
| 675 | \str_replace( |
||||
| 676 | '\\' . AbstractGenerator::ENTITIES_FOLDER_NAME . '\\', |
||||
| 677 | '\\' . AbstractGenerator::ENTITY_FACTORIES_NAMESPACE . '\\', |
||||
| 678 | $entityFqn |
||||
| 679 | ) . 'DtoFactory' |
||||
| 680 | ); |
||||
| 681 | } |
||||
| 682 | |||||
| 683 | public function getRepositoryqnFromEntityFqn(string $entityFqn): string |
||||
| 684 | { |
||||
| 685 | return $this->tidy( |
||||
| 686 | \str_replace( |
||||
| 687 | '\\' . AbstractGenerator::ENTITIES_FOLDER_NAME . '\\', |
||||
| 688 | '\\' . AbstractGenerator::ENTITY_REPOSITORIES_NAMESPACE . '\\', |
||||
| 689 | $entityFqn |
||||
| 690 | ) . 'Repository' |
||||
| 691 | ); |
||||
| 692 | } |
||||
| 693 | |||||
| 694 | /** |
||||
| 695 | * @param string $ownedEntityFqn |
||||
| 696 | * @param string $srcOrTestSubFolder |
||||
| 697 | * @param string $projectRootNamespace |
||||
| 698 | * |
||||
| 699 | * @return string |
||||
| 700 | * @throws \EdmondsCommerce\DoctrineStaticMeta\Exception\DoctrineStaticMetaException |
||||
| 701 | */ |
||||
| 702 | public function getReciprocatedHasName( |
||||
| 703 | string $ownedEntityFqn, |
||||
| 704 | string $srcOrTestSubFolder, |
||||
| 705 | string $projectRootNamespace |
||||
| 706 | ): string { |
||||
| 707 | $parsedFqn = $this->parseFullyQualifiedName( |
||||
| 708 | $ownedEntityFqn, |
||||
| 709 | $srcOrTestSubFolder, |
||||
| 710 | $projectRootNamespace |
||||
| 711 | ); |
||||
| 712 | |||||
| 713 | $subDirectories = $parsedFqn[2]; |
||||
| 714 | |||||
| 715 | return $this->getSingularNamespacedName($ownedEntityFqn, $subDirectories); |
||||
| 716 | } |
||||
| 717 | |||||
| 718 | /** |
||||
| 719 | * Get the Fully Qualified Namespace for the Relation Interface for a specific Entity and hasType |
||||
| 720 | * |
||||
| 721 | * @param string $hasType |
||||
| 722 | * @param string $ownedEntityFqn |
||||
| 723 | * @param string|null $projectRootNamespace |
||||
| 724 | * @param string $srcFolder |
||||
| 725 | * |
||||
| 726 | * @return string |
||||
| 727 | * @throws DoctrineStaticMetaException |
||||
| 728 | */ |
||||
| 729 | public function getOwningInterfaceFqn( |
||||
| 730 | string $hasType, |
||||
| 731 | string $ownedEntityFqn, |
||||
| 732 | string $projectRootNamespace = null, |
||||
| 733 | string $srcFolder = AbstractCommand::DEFAULT_SRC_SUBFOLDER |
||||
| 734 | ): string { |
||||
| 735 | try { |
||||
| 736 | $ownedHasName = $this->getOwnedHasName($hasType, $ownedEntityFqn, $srcFolder, $projectRootNamespace); |
||||
|
0 ignored issues
–
show
It seems like
$projectRootNamespace can also be of type null; however, parameter $projectRootNamespace of EdmondsCommerce\Doctrine...lper::getOwnedHasName() does only seem to accept string, maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 737 | if (null === $projectRootNamespace) { |
||||
| 738 | $projectRootNamespace = $this->getProjectRootNamespaceFromComposerJson($srcFolder); |
||||
| 739 | } |
||||
| 740 | list($ownedClassName, , $ownedSubDirectories) = $this->parseFullyQualifiedName( |
||||
| 741 | $ownedEntityFqn, |
||||
| 742 | $srcFolder, |
||||
| 743 | $projectRootNamespace |
||||
| 744 | ); |
||||
| 745 | $interfaceSubDirectories = \array_slice($ownedSubDirectories, 2); |
||||
| 746 | $owningInterfaceFqn = $this->getOwningRelationsRootFqn( |
||||
| 747 | $projectRootNamespace, |
||||
| 748 | $interfaceSubDirectories |
||||
| 749 | ); |
||||
| 750 | $required = \ts\stringContains($hasType, RelationsGenerator::PREFIX_REQUIRED) |
||||
| 751 | ? 'Required' |
||||
| 752 | : ''; |
||||
| 753 | $owningInterfaceFqn .= '\\' . |
||||
| 754 | $ownedClassName . |
||||
| 755 | '\\Interfaces\\Has' . |
||||
| 756 | $required . |
||||
| 757 | $ownedHasName . |
||||
| 758 | 'Interface'; |
||||
| 759 | |||||
| 760 | return $this->tidy($owningInterfaceFqn); |
||||
| 761 | } catch (\Exception $e) { |
||||
| 762 | throw new DoctrineStaticMetaException( |
||||
| 763 | 'Exception in ' . __METHOD__ . ': ' . $e->getMessage(), |
||||
| 764 | $e->getCode(), |
||||
| 765 | $e |
||||
| 766 | ); |
||||
| 767 | } |
||||
| 768 | } |
||||
| 769 | |||||
| 770 | public function getEntityInterfaceFromEntityFqn(string $entityFqn): string |
||||
| 771 | { |
||||
| 772 | return \str_replace( |
||||
| 773 | '\\Entities\\', |
||||
| 774 | '\\Entity\\Interfaces\\', |
||||
| 775 | $entityFqn |
||||
| 776 | ) . 'Interface'; |
||||
| 777 | } |
||||
| 778 | |||||
| 779 | public function getEntityFqnFromEntityInterfaceFqn(string $entityInterfaceFqn): string |
||||
| 780 | { |
||||
| 781 | return substr( |
||||
| 782 | \str_replace( |
||||
| 783 | '\\Entity\\Interfaces\\', |
||||
| 784 | '\\Entities\\', |
||||
| 785 | $entityInterfaceFqn |
||||
| 786 | ), |
||||
| 787 | 0, |
||||
| 788 | -\strlen('Interface') |
||||
| 789 | ); |
||||
| 790 | } |
||||
| 791 | |||||
| 792 | public function getEntityFqnFromEntityFactoryFqn(string $entityFactoryFqn): string |
||||
| 793 | { |
||||
| 794 | return substr( |
||||
| 795 | \str_replace( |
||||
| 796 | '\\Entity\\Factories\\', |
||||
| 797 | '\\Entities\\', |
||||
| 798 | $entityFactoryFqn |
||||
| 799 | ), |
||||
| 800 | 0, |
||||
| 801 | -\strlen('Factory') |
||||
| 802 | ); |
||||
| 803 | } |
||||
| 804 | |||||
| 805 | public function getEntityFqnFromEntityDtoFactoryFqn(string $entityDtoFactoryFqn): string |
||||
| 806 | { |
||||
| 807 | return substr( |
||||
| 808 | \str_replace( |
||||
| 809 | '\\Entity\\Factories\\', |
||||
| 810 | '\\Entities\\', |
||||
| 811 | $entityDtoFactoryFqn |
||||
| 812 | ), |
||||
| 813 | 0, |
||||
| 814 | -\strlen('DtoFactory') |
||||
| 815 | ); |
||||
| 816 | } |
||||
| 817 | |||||
| 818 | public function getEntityDtoFqnFromEntityFqn(string $entityFqn): string |
||||
| 819 | { |
||||
| 820 | return \str_replace( |
||||
| 821 | '\\Entities\\', |
||||
| 822 | '\\Entity\\DataTransferObjects\\', |
||||
| 823 | $entityFqn |
||||
| 824 | ) . 'Dto'; |
||||
| 825 | } |
||||
| 826 | |||||
| 827 | /** |
||||
| 828 | * @deprecated please use the static method on the DTO directly |
||||
| 829 | * |
||||
| 830 | * @param string $entityDtoFqn |
||||
| 831 | * |
||||
| 832 | * @return string |
||||
| 833 | */ |
||||
| 834 | public function getEntityFqnFromEntityDtoFqn(string $entityDtoFqn): string |
||||
| 835 | { |
||||
| 836 | return $entityDtoFqn::getEntityFqn(); |
||||
| 837 | } |
||||
| 838 | |||||
| 839 | public function getEntityFqnFromEntityRepositoryFqn(string $entityRepositoryFqn): string |
||||
| 840 | { |
||||
| 841 | return substr( |
||||
| 842 | \str_replace( |
||||
| 843 | '\\Entity\\Repositories\\', |
||||
| 844 | '\\Entities\\', |
||||
| 845 | $entityRepositoryFqn |
||||
| 846 | ), |
||||
| 847 | 0, |
||||
| 848 | -\strlen('Repository') |
||||
| 849 | ); |
||||
| 850 | } |
||||
| 851 | |||||
| 852 | public function getEntityFqnFromEntitySaverFqn(string $entitySaverFqn): string |
||||
| 853 | { |
||||
| 854 | return substr( |
||||
| 855 | \str_replace( |
||||
| 856 | '\\Entity\\Savers\\', |
||||
| 857 | '\\Entities\\', |
||||
| 858 | $entitySaverFqn |
||||
| 859 | ), |
||||
| 860 | 0, |
||||
| 861 | -\strlen('Saver') |
||||
| 862 | ); |
||||
| 863 | } |
||||
| 864 | |||||
| 865 | public function getEntitySaverFqnFromEntityFqn(string $entityFqn): string |
||||
| 866 | { |
||||
| 867 | return \str_replace( |
||||
| 868 | '\\Entities\\', |
||||
| 869 | '\\Entity\\Savers\\', |
||||
| 870 | $entityFqn |
||||
| 871 | ) . 'Saver'; |
||||
| 872 | } |
||||
| 873 | |||||
| 874 | public function getEntityFqnFromEntityTestFqn(string $entityTestFqn): string |
||||
| 875 | { |
||||
| 876 | return \substr( |
||||
| 877 | $entityTestFqn, |
||||
| 878 | 0, |
||||
| 879 | -\strlen('Test') |
||||
| 880 | ); |
||||
| 881 | } |
||||
| 882 | |||||
| 883 | public function getEntityTestFqnFromEntityFqn(string $entityFqn): string |
||||
| 884 | { |
||||
| 885 | return $entityFqn . 'Test'; |
||||
| 886 | } |
||||
| 887 | } |
||||
| 888 |