diff --git a/src/Doctrine/DoctrineMetadataFactory.php b/src/Doctrine/DoctrineMetadataFactory.php index b5a616539..d1b7e067e 100644 --- a/src/Doctrine/DoctrineMetadataFactory.php +++ b/src/Doctrine/DoctrineMetadataFactory.php @@ -12,6 +12,9 @@ namespace Symfony\Bundle\MakerBundle\Doctrine; use Doctrine\Common\Persistence\ManagerRegistry; +use Doctrine\Common\Persistence\Mapping\Driver\MappingDriver; +use Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain; +use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Tools\DisconnectedClassMetadataFactory; @@ -69,6 +72,30 @@ public function getMetadataForClass(string $entity): ?ClassMetadata return null; } + public function getMappingDriverForClass(string $className): ?MappingDriver + { + /** @var EntityManagerInterface $em */ + $em = $this->registry->getManagerForClass($className); + + if (null === $em) { + throw new \InvalidArgumentException(sprintf('Cannot find the entity manager for class "%s"', $className)); + } + + $metadataDriver = $em->getConfiguration()->getMetadataDriverImpl(); + + if (!$metadataDriver instanceof MappingDriverChain) { + return $metadataDriver; + } + + foreach ($metadataDriver->getDrivers() as $namespace => $driver) { + if (0 === strpos($className, $namespace)) { + return $driver; + } + } + + return $metadataDriver->getDefaultDriver(); + } + /** * @return array */ diff --git a/src/Maker/MakeEntity.php b/src/Maker/MakeEntity.php index b7e252770..76ff2f779 100644 --- a/src/Maker/MakeEntity.php +++ b/src/Maker/MakeEntity.php @@ -15,8 +15,11 @@ use Doctrine\Common\Persistence\ManagerRegistry; use Doctrine\DBAL\Types\Type; use Doctrine\ORM\Mapping\Column; +use Doctrine\ORM\Mapping\Driver\AnnotationDriver; use Symfony\Bundle\MakerBundle\ConsoleStyle; use Symfony\Bundle\MakerBundle\DependencyBuilder; +use Symfony\Bundle\MakerBundle\Doctrine\DoctrineMetadataFactory; +use Symfony\Bundle\MakerBundle\Exception\RuntimeCommandException; use Symfony\Bundle\MakerBundle\Generator; use Symfony\Bundle\MakerBundle\InputConfiguration; use Symfony\Bundle\MakerBundle\Str; @@ -122,7 +125,8 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen 'Repository' ); - if (!class_exists($entityClassDetails->getFullName())) { + $classExists = class_exists($entityClassDetails->getFullName()); + if (!$classExists) { $entityPath = $generator->generateClass( $entityClassDetails->getFullName(), 'doctrine/Entity.tpl.php', @@ -143,21 +147,26 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen ); $generator->writeChanges(); + } + + if (!$this->doesEntityUseAnnotationMapping($entityClassDetails->getFullName())) { + throw new RuntimeCommandException(sprintf('Only annotation mapping is supported by make:entity, but the %s class uses a different format. If you would like this command to generate the properties & getter/setter methods, add your mapping configuration, and then re-run this command with the --regenerate flag.', $entityClassDetails->getFullName())); + } + if ($classExists) { + $entityPath = $this->getPathOfClass($entityClassDetails->getFullName()); $io->text([ - '', - 'Entity generated! Now let\'s add some fields!', - 'You can always add more fields later manually or by re-running this command.', + 'Your entity already exists! So let\'s add some new fields!', ]); } else { - $entityPath = $this->getPathOfClass($entityClassDetails->getFullName()); $io->text([ - 'Your entity already exists! So let\'s add some new fields!', + '', + 'Entity generated! Now let\'s add some fields!', + 'You can always add more fields later manually or by re-running this command.', ]); } $currentFields = $this->getPropertyNames($entityClassDetails->getFullName()); - $manipulator = $this->createClassManipulator($entityPath, $io, $overwrite); $isFirstField = true; @@ -782,4 +791,23 @@ private function getPropertyNames(string $class): array return $prop->getName(); }, $reflClass->getProperties()); } + + private function doesEntityUseAnnotationMapping(string $className): bool + { + $metadataFactory = new DoctrineMetadataFactory($this->registry); + if (!class_exists($className)) { + $otherClassMetadatas = $metadataFactory->getMetadataForNamespace(Str::getNamespace($className)); + + // if we have no metadata, we should assume this is the first class being mapped + if (empty($otherClassMetadatas)) { + return false; + } + + $className = $otherClassMetadatas[0]->name; + } + + $metadataFactory->getMappingDriverForClass($className); + + return $metadataFactory instanceof AnnotationDriver; + } } diff --git a/src/Test/MakerTestCase.php b/src/Test/MakerTestCase.php index fdef87019..c51a7a35c 100644 --- a/src/Test/MakerTestCase.php +++ b/src/Test/MakerTestCase.php @@ -93,7 +93,7 @@ protected function executeMakerCommand(MakerTestDetails $testDetails) $makerProcess->run(); - if (!$makerProcess->isSuccessful()) { + if (!$makerProcess->isSuccessful() && !$testDetails->isCommandAllowedToFail()) { throw new \Exception(sprintf('Running maker command failed: "%s" "%s"', $makerProcess->getOutput(), $makerProcess->getErrorOutput())); } diff --git a/src/Test/MakerTestDetails.php b/src/Test/MakerTestDetails.php index 6c8a84200..241517de7 100644 --- a/src/Test/MakerTestDetails.php +++ b/src/Test/MakerTestDetails.php @@ -34,6 +34,8 @@ final class MakerTestDetails private $argumentsString = ''; + private $commandAllowedToFail = false; + /** * @param MakerInterface $maker * @param array $inputs @@ -153,6 +155,13 @@ public function setArgumentsString(string $argumentsString): self return $this; } + public function setCommandAllowedToFail(bool $commandAllowedToFail): self + { + $this->commandAllowedToFail = $commandAllowedToFail; + + return $this; + } + public function getInputs(): array { return $this->inputs; @@ -216,4 +225,9 @@ public function getArgumentsString(): string { return $this->argumentsString; } + + public function isCommandAllowedToFail(): bool + { + return $this->commandAllowedToFail; + } } diff --git a/tests/Maker/FunctionalTest.php b/tests/Maker/FunctionalTest.php index 1fa99f467..8d79e76c2 100644 --- a/tests/Maker/FunctionalTest.php +++ b/tests/Maker/FunctionalTest.php @@ -191,7 +191,7 @@ public function getCommandTests() ->updateSchemaAfterCommand() ]; - yield 'entity_many_to_one_simple' => [MakerTestDetails::createTest( + yield 'entity_many_to_one_simple_with_inverse' => [MakerTestDetails::createTest( $this->getMakerInstance(MakeEntity::class), [ // entity class name @@ -476,6 +476,52 @@ public function getCommandTests() ->configureDatabase(false) ]; + yield 'entity_xml_mapping_error_existing' => [MakerTestDetails::createTest( + $this->getMakerInstance(MakeEntity::class), + [ + 'User', + ]) + ->setFixtureFilesPath(__DIR__ . '/../fixtures/MakeEntityXmlMappingError') + ->addReplacement( + 'config/packages/doctrine.yaml', + 'type: annotation', + 'type: xml' + ) + ->addReplacement( + 'config/packages/doctrine.yaml', + "dir: '%kernel.project_dir%/src/Entity'", + "dir: '%kernel.project_dir%/config/doctrine'" + ) + ->configureDatabase(false) + ->setCommandAllowedToFail(true) + ->assert(function(string $output, string $directory) { + $this->assertContains('Only annotation mapping is supported', $output); + }) + ]; + + yield 'entity_xml_mapping_error_new_class' => [MakerTestDetails::createTest( + $this->getMakerInstance(MakeEntity::class), + [ + 'UserAvatarPhoto', + ]) + ->setFixtureFilesPath(__DIR__ . '/../fixtures/MakeEntityXmlMappingError') + ->addReplacement( + 'config/packages/doctrine.yaml', + 'type: annotation', + 'type: xml' + ) + ->addReplacement( + 'config/packages/doctrine.yaml', + "dir: '%kernel.project_dir%/src/Entity'", + "dir: '%kernel.project_dir%/config/doctrine'" + ) + ->configureDatabase(false) + ->setCommandAllowedToFail(true) + ->assert(function(string $output, string $directory) { + $this->assertContains('Only annotation mapping is supported', $output); + }) + ]; + yield 'entity_updating_overwrite' => [MakerTestDetails::createTest( $this->getMakerInstance(MakeEntity::class), [ diff --git a/tests/fixtures/MakeEntityXmlMappingError/config/doctrine/User.orm.xml b/tests/fixtures/MakeEntityXmlMappingError/config/doctrine/User.orm.xml new file mode 100644 index 000000000..322344224 --- /dev/null +++ b/tests/fixtures/MakeEntityXmlMappingError/config/doctrine/User.orm.xml @@ -0,0 +1,12 @@ + + + + + + + + + \ No newline at end of file diff --git a/tests/fixtures/MakeEntityXmlMappingError/src/Entity/User.php b/tests/fixtures/MakeEntityXmlMappingError/src/Entity/User.php new file mode 100644 index 000000000..8c2e33ba5 --- /dev/null +++ b/tests/fixtures/MakeEntityXmlMappingError/src/Entity/User.php @@ -0,0 +1,16 @@ +id; + } + + // add your own fields +}