diff --git a/src/Doctrine/EntityRegenerator.php b/src/Doctrine/EntityRegenerator.php index d47184ed7..e98ab2b28 100644 --- a/src/Doctrine/EntityRegenerator.php +++ b/src/Doctrine/EntityRegenerator.php @@ -12,6 +12,7 @@ namespace Symfony\Bundle\MakerBundle\Doctrine; use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Mapping\MappingException; use Symfony\Bundle\MakerBundle\Exception\RuntimeCommandException; use Symfony\Bundle\MakerBundle\FileManager; use Symfony\Bundle\MakerBundle\Generator; @@ -40,7 +41,11 @@ public function __construct(DoctrineHelper $doctrineHelper, FileManager $fileMan public function regenerateEntities(string $classOrNamespace) { - $metadata = $this->doctrineHelper->getMetadata($classOrNamespace, true); + try { + $metadata = $this->doctrineHelper->getMetadata($classOrNamespace); + } catch (MappingException $mappingException) { + $metadata = $this->doctrineHelper->getMetadata($classOrNamespace, true); + } if ($metadata instanceof ClassMetadata) { $metadata = [$metadata]; @@ -67,7 +72,28 @@ public function regenerateEntities(string $classOrNamespace) $manipulator = $this->createClassManipulator($classPath); $operations[$classPath] = $manipulator; + $embeddedClasses = []; + + foreach ($classMetadata->embeddedClasses as $fieldName => $mapping) { + $className = $mapping['class']; + + $embeddedClasses[$fieldName] = $this->getPathOfClass($className); + + $operations[$embeddedClasses[$fieldName]] = $this->createClassManipulator($embeddedClasses[$fieldName]); + + $manipulator->addEmbeddedEntity($fieldName, $className); + } + foreach ($classMetadata->fieldMappings as $fieldName => $mapping) { + // skip embedded fields + if (false !== strpos($fieldName, '.')) { + list($fieldName, $embeddedFiledName) = explode('.', $fieldName); + + $operations[$embeddedClasses[$fieldName]]->addEntityField($embeddedFiledName, $mapping); + + continue; + } + $manipulator->addEntityField($fieldName, $mapping); } diff --git a/src/Util/ClassSourceManipulator.php b/src/Util/ClassSourceManipulator.php index 6f60ac5c6..9372b420d 100644 --- a/src/Util/ClassSourceManipulator.php +++ b/src/Util/ClassSourceManipulator.php @@ -106,6 +106,46 @@ public function addEntityField(string $propertyName, array $columnOptions) } } + public function addEmbeddedEntity(string $propertyName, string $className) + { + $typeHint = self::addUseStatementIfNecessary($className); + + $annotations = [ + $this->buildAnnotationLine( + '@ORM\\Embedded', + [ + 'class' => $className, + ] + ), + ]; + + $this->addProperty($propertyName, $annotations); + + // logic to avoid re-adding the same ArrayCollection line + $addEmbedded = true; + if ($this->getConstructorNode()) { + // We print the constructor to a string, then + // look for "$this->propertyName = " + + $constructorString = $this->printer->prettyPrint([$this->getConstructorNode()]); + if (false !== strpos($constructorString, sprintf('$this->%s = ', $propertyName))) { + $addEmbedded = false; + } + } + + if ($addEmbedded) { + $this->addStatementToConstructor( + new Node\Stmt\Expression(new Node\Expr\Assign( + new Node\Expr\PropertyFetch(new Node\Expr\Variable('this'), $propertyName), + new Node\Expr\New_(new Node\Name($typeHint)) + )) + ); + } + + $this->addGetter($propertyName, $typeHint, false); + $this->addSetter($propertyName, $typeHint, false); + } + public function addManyToOneRelation(RelationManyToOne $manyToOne) { $this->addSingularRelation($manyToOne); diff --git a/tests/Maker/FunctionalTest.php b/tests/Maker/FunctionalTest.php index 4753c0c0f..a9455d992 100644 --- a/tests/Maker/FunctionalTest.php +++ b/tests/Maker/FunctionalTest.php @@ -689,6 +689,18 @@ public function getCommandEntityTests() ->setRequiredPhpVersion(70100) ]; + yield 'entity_regenerate_embeddable' => [MakerTestDetails::createTest( + $this->getMakerInstance(MakeEntity::class), + [ + // namespace: use default App\Entity + '', + ]) + ->setArgumentsString('--regenerate --overwrite') + ->setFixtureFilesPath(__DIR__ . '/../fixtures/MakeEntityRegenerateEmbedable') + ->configureDatabase() + ->setRequiredPhpVersion(70100) + ]; + yield 'entity_regenerate_overwrite' => [MakerTestDetails::createTest( $this->getMakerInstance(MakeEntity::class), [ diff --git a/tests/fixtures/MakeEntityRegenerateEmbedable/src/Entity/Food.php b/tests/fixtures/MakeEntityRegenerateEmbedable/src/Entity/Food.php new file mode 100644 index 000000000..e675c7b5e --- /dev/null +++ b/tests/fixtures/MakeEntityRegenerateEmbedable/src/Entity/Food.php @@ -0,0 +1,28 @@ +getContainer() + ->get('doctrine') + ->getManager(); + + $em->createQuery('DELETE FROM App\\Entity\\Food f')->execute(); + + $food = new Food(); + // check that the constructor was instantiated properly + $this->assertInstanceOf(Recipe::class, $food->getRecipe()); + // fields should now have setters + $food->setTitle('Borscht'); + + $recipe = new Recipe(); + $recipe->setIngredients('ingridients'); + $recipe->setSteps('steps'); + $food->setRecipe($recipe); + + $em->persist($food); + + $em->flush(); + $em->refresh($food); + + /** @var Food[] $actualFood */ + $actualFood = $em->getRepository(Food::class) + ->findAll(); + + $this->assertcount(1, $actualFood); + + /** @var Recipe $actualRecipe */ + $actualRecipe = $actualFood[0]->getRecipe(); + + $this->assertInstanceOf(Recipe::class, $actualRecipe); + $this->assertEquals('ingridients', $actualRecipe->getIngredients()); + $this->assertEquals('steps', $actualRecipe->getSteps()); + } +}