diff --git a/src/Maker/MakeEntity.php b/src/Maker/MakeEntity.php index b93b8d42a..f8825610f 100644 --- a/src/Maker/MakeEntity.php +++ b/src/Maker/MakeEntity.php @@ -779,6 +779,12 @@ private function askRelationType(ConsoleStyle $io, string $entityClass, string $ $originalEntityShort = Str::getShortClassName($entityClass); $targetEntityShort = Str::getShortClassName($targetEntityClass); + if ($originalEntityShort === $targetEntityShort) { + [$originalDiscriminator, $targetDiscriminator] = Str::getHumanDiscriminatorBetweenTwoClasses($entityClass, $targetEntityClass); + $originalEntityShort = trim($originalDiscriminator.'\\'.$originalEntityShort, '\\'); + $targetEntityShort = trim($targetDiscriminator.'\\'.$targetEntityShort, '\\'); + } + $rows = []; $rows[] = [ EntityRelation::MANY_TO_ONE, diff --git a/src/Str.php b/src/Str.php index 177919d67..21c557623 100644 --- a/src/Str.php +++ b/src/Str.php @@ -129,6 +129,38 @@ public static function getShortClassName(string $fullClassName): string return substr($fullClassName, strrpos($fullClassName, '\\') + 1); } + /** + * @return array{0: string, 1: string} + */ + public static function getHumanDiscriminatorBetweenTwoClasses(string $className, string $classNameOther): array + { + $namespace = self::getNamespace($className); + $namespaceOther = self::getNamespace($classNameOther); + if (empty($namespace) || empty($namespaceOther)) { + return [$namespace, $namespaceOther]; + } + + $namespaceParts = explode('\\', $namespace); + $namespacePartsOther = explode('\\', $namespaceOther); + + $min = min(\count($namespaceParts), \count($namespacePartsOther)); + for ($i = 0; $i < $min; ++$i) { + $part = $namespaceParts[$i]; + $partOther = $namespacePartsOther[$i]; + if ($part !== $partOther) { + break; + } + + $namespaceParts[$i] = null; + $namespacePartsOther[$i] = null; + } + + return [ + implode('\\', array_filter($namespaceParts)), + implode('\\', array_filter($namespacePartsOther)), + ]; + } + public static function getNamespace(string $fullClassName): string { return substr($fullClassName, 0, strrpos($fullClassName, '\\')); diff --git a/tests/Maker/MakeEntityTest.php b/tests/Maker/MakeEntityTest.php index 49bb908e7..87fbb1c9e 100644 --- a/tests/Maker/MakeEntityTest.php +++ b/tests/Maker/MakeEntityTest.php @@ -376,6 +376,45 @@ public function getTestDetails(): \Generator }), ]; + yield 'it_adds_many_to_many_between_same_entity_name_different_namespace' => [$this->createMakeEntityTest() + ->run(function (MakerTestRunner $runner) { + $this->copyEntity($runner, 'User-basic.php'); + $this->copyEntity($runner, 'Friend/User-sub-namespace.php'); + + $output = $runner->runMaker([ + // entity class name + 'User', + // field name + 'friends', + // add a relationship field + 'relation', + // the target entity + 'Friend\\User', + // relation type + 'ManyToMany', + // inverse side? + 'y', + // field name on opposite side - use default 'courses' + '', + // finish adding fields + '', + ]); + + $this->assertStringContainsString('src/Entity/User.php', $output); + $this->assertStringContainsString('src/Entity/Friend/User.php', $output); + $this->assertStringContainsString('ManyToOne Each User relates to (has) one Friend\User.', $output); + $this->assertStringContainsString('Each Friend\User can relate to (can have) many User objects.', $output); + $this->assertStringContainsString('OneToMany Each User can relate to (can have) many Friend\User objects.', $output); + $this->assertStringContainsString('Each Friend\User relates to (has) one User.', $output); + $this->assertStringContainsString('ManyToMany Each User can relate to (can have) many Friend\User objects.', $output); + $this->assertStringContainsString('Each Friend\User can also relate to (can also have) many User objects.', $output); + $this->assertStringContainsString('OneToOne Each User relates to (has) exactly one Friend\User.', $output); + $this->assertStringContainsString('Each Friend\User also relates to (has) exactly one User.', $output); + + // $this->runCustomTest($runner, 'it_adds_many_to_many_between_same_entity_name_different_namespace.php'); + }), + ]; + yield 'it_adds_one_to_one_simple' => [$this->createMakeEntityTest() ->run(function (MakerTestRunner $runner) { $this->copyEntity($runner, 'User-basic.php'); diff --git a/tests/StrTest.php b/tests/StrTest.php index 5c639624e..8d7610658 100644 --- a/tests/StrTest.php +++ b/tests/StrTest.php @@ -185,6 +185,24 @@ public function getShortClassNameCaseTests() yield ['Foo', 'Foo']; } + /** + * @dataProvider getHumanDiscriminatorBetweenTwoClassesTests + */ + public function testHumanDiscriminatorBetweenTwoClasses(string $className, string $classNameOther, array $expected) + { + $this->assertSame($expected, Str::getHumanDiscriminatorBetweenTwoClasses($className, $classNameOther)); + } + + public function getHumanDiscriminatorBetweenTwoClassesTests() + { + yield ['\\User', 'App\\Entity\\User', ['', 'App\\Entity']]; + yield ['App\\Entity\\User', 'App\\Entity\\Friend\\User', ['', 'Friend']]; + yield ['App\\Entity\\User', 'Custom\\Entity\\User', ['App\\Entity', 'Custom\\Entity']]; + yield ['App\\Entity\\User', 'App\\Bundle\\Entity\\User', ['Entity', 'Bundle\\Entity']]; + yield ['App\\Entity\\User', 'App\\Bundle\\User', ['Entity', 'Bundle']]; + yield ['App\\Entity\\User', 'Custom\\Bundle\\Friend\\Entity\\User', ['App\\Entity', 'Custom\\Bundle\\Friend\\Entity']]; + } + /** * @dataProvider asHumanWordsTests */ diff --git a/tests/fixtures/make-entity/entities/attributes/Friend/User-sub-namespace.php b/tests/fixtures/make-entity/entities/attributes/Friend/User-sub-namespace.php new file mode 100644 index 000000000..542994ad6 --- /dev/null +++ b/tests/fixtures/make-entity/entities/attributes/Friend/User-sub-namespace.php @@ -0,0 +1,45 @@ +id; + } + + public function getFirstName() + { + return $this->firstName; + } + + public function setFirstName(?string $firstName) + { + $this->firstName = $firstName; + } + + public function getCreatedAt(): ?\DateTimeInterface + { + return $this->createdAt; + } + + public function setCreatedAt(?\DateTimeInterface $createdAt) + { + $this->createdAt = $createdAt; + } +}