Skip to content

Commit

Permalink
Doctrine embedded entity 'make:entity --regenerate' bugfix
Browse files Browse the repository at this point in the history
  • Loading branch information
sadikoff authored and weaverryan committed May 2, 2018
1 parent c7c8f72 commit 0e36480
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 1 deletion.
28 changes: 27 additions & 1 deletion src/Doctrine/EntityRegenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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];
Expand All @@ -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);
}

Expand Down
40 changes: 40 additions & 0 deletions src/Util/ClassSourceManipulator.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
12 changes: 12 additions & 0 deletions tests/Maker/FunctionalTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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),
[
Expand Down
28 changes: 28 additions & 0 deletions tests/fixtures/MakeEntityRegenerateEmbedable/src/Entity/Food.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
* @ORM\Entity()
*/
class Food
{
/**
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;

/**
* @ORM\Column(name="title", type="string", length=255)
*/
private $title;

/**
* @ORM\Embedded(class="App\Entity\Recipe")
*/
private $recipe;
}
21 changes: 21 additions & 0 deletions tests/fixtures/MakeEntityRegenerateEmbedable/src/Entity/Recipe.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
* @ORM\Embeddable()
*/
class Recipe
{
/**
* @ORM\Column(type="string", length=255)
*/
private $ingredients;

/**
* @ORM\Column(type="string", length=255)
*/
private $steps;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

namespace App\Tests;

use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Doctrine\ORM\EntityManager;
use App\Entity\Food;
use App\Entity\Recipe;

class GeneratedEntityTest extends KernelTestCase
{
public function testGeneratedEntity()
{
self::bootKernel();
/** @var EntityManager $em */
$em = self::$kernel->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());
}
}

0 comments on commit 0e36480

Please sign in to comment.