From 969b0975259d1632d77bb5a703cd0a596af5a11a Mon Sep 17 00:00:00 2001 From: Jesse Rushlow Date: Mon, 5 Feb 2024 10:04:46 -0500 Subject: [PATCH 01/24] [ci] don't use database:create for sqlite in tests --- src/Test/MakerTestRunner.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Test/MakerTestRunner.php b/src/Test/MakerTestRunner.php index 72583409c..14f9cfb98 100644 --- a/src/Test/MakerTestRunner.php +++ b/src/Test/MakerTestRunner.php @@ -171,6 +171,17 @@ public function configureDatabase(bool $createSchema = true): void return $config; }); + if (str_starts_with(getenv('TEST_DATABASE_DSN'), 'sqlite')) { + // make sure the database doesn't exist... + $this->runConsole('doctrine:database:drop', [], '--env=test --force'); + + // create the schema + $this->runConsole('doctrine:schema:create', [], '--env=test'); + + return; + } + + // @TODO _ @legacy - deal with this duplication - test on postgres/mysql // this looks silly, but it's the only way to drop the database *for sure*, // as doctrine:database:drop will error if there is no database // also, skip for SQLITE, as it does not support --if-not-exists From e7ccbb17e3d63bf7326c39939591e39c66a7a17b Mon Sep 17 00:00:00 2001 From: Jesse Rushlow Date: Mon, 5 Feb 2024 12:25:16 -0500 Subject: [PATCH 02/24] WIP - stop using arrays in CSM... handle FieldMapping::class --- src/Doctrine/EntityRegenerator.php | 6 +- src/Test/MakerTestProcess.php | 2 + src/Util/CSM/ObjectMapping.php | 36 ++++++++++++ src/Util/ClassSourceManipulator.php | 42 ++++++++++---- tests/Util/ClassSourceManipulatorTest.php | 70 +++++++++++++---------- 5 files changed, 114 insertions(+), 42 deletions(-) create mode 100644 src/Util/CSM/ObjectMapping.php diff --git a/src/Doctrine/EntityRegenerator.php b/src/Doctrine/EntityRegenerator.php index 8dee3eabe..ac865ec05 100644 --- a/src/Doctrine/EntityRegenerator.php +++ b/src/Doctrine/EntityRegenerator.php @@ -75,6 +75,8 @@ public function regenerateEntities(string $classOrNamespace): void continue; } + // dump([$fieldName => $mapping]); + $className = $mapping['class']; $embeddedClasses[$fieldName] = $this->getPathOfClass($className); @@ -102,7 +104,9 @@ public function regenerateEntities(string $classOrNamespace): void continue; } - $manipulator->addEntityField($fieldName, $mapping); + // dump([$classMetadata]); + + $manipulator->addEntityField($fieldName, (array) $mapping); } $getIsNullable = function (array $mapping) { diff --git a/src/Test/MakerTestProcess.php b/src/Test/MakerTestProcess.php index 9257c7206..e11a07fc3 100644 --- a/src/Test/MakerTestProcess.php +++ b/src/Test/MakerTestProcess.php @@ -45,6 +45,8 @@ public function setInput($input): self public function run($allowToFail = false, array $envVars = []): self { + // @TODO _ @legacy -> This needs to be a env for debugging... + $this->process->setTimeout(null); $this->process->run(null, $envVars); if (!$allowToFail && !$this->process->isSuccessful()) { diff --git a/src/Util/CSM/ObjectMapping.php b/src/Util/CSM/ObjectMapping.php new file mode 100644 index 000000000..4462d058d --- /dev/null +++ b/src/Util/CSM/ObjectMapping.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\MakerBundle\Util\CSM; + +class ObjectMapping +{ + public function __construct( + public string $type, + public ?int $length = null, + public ?string $id = null, + public ?bool $nullable = null, + public array $options = [], + public ?int $precision = null, + public ?int $scale = null, + public bool $needsTypeHint = true, + ) { + } + + public static function createFromObject(\Doctrine\ORM\Mapping\FieldMapping $mapping): self + { + return new self( + type: $mapping->type, + id: $mapping->id, + nullable: $mapping->nullable, + ); + } +} diff --git a/src/Util/ClassSourceManipulator.php b/src/Util/ClassSourceManipulator.php index 833b80dcb..185c525ff 100644 --- a/src/Util/ClassSourceManipulator.php +++ b/src/Util/ClassSourceManipulator.php @@ -38,6 +38,7 @@ use Symfony\Bundle\MakerBundle\Doctrine\RelationOneToMany; use Symfony\Bundle\MakerBundle\Doctrine\RelationOneToOne; use Symfony\Bundle\MakerBundle\Str; +use Symfony\Bundle\MakerBundle\Util\CSM\ObjectMapping; /** * @internal @@ -96,27 +97,48 @@ public function getSourceCode(): string return $this->sourceCode; } - public function addEntityField(string $propertyName, array $columnOptions, array $comments = []): void + public function addEntityField(string $propertyName, ObjectMapping $columnOptions, array $comments = []): void { - $typeHint = DoctrineHelper::getPropertyTypeForColumn($columnOptions['type']); - if ($typeHint && DoctrineHelper::canColumnTypeBeInferredByPropertyType($columnOptions['type'], $typeHint)) { - unset($columnOptions['type']); +// if ($columnOptions instanceof \Doctrine\ORM\Mapping\FieldMapping::class) { +// $columnOptions['type'] = $columnOptions->type; +// $columnOptions['nullable'] = $columnOptions->nullable; +// $columnOptions['id'] = $columnOptions->id; +// } + + if (is_array($columnOptions)) { + dump($columnOptions); + $columnOptions = \Doctrine\ORM\Mapping\FieldMapping::fromMappingArray($columnOptions); + } + + dump($columnOptions); + $typeHint = DoctrineHelper::getPropertyTypeForColumn($columnOptions->type); + if ($typeHint && DoctrineHelper::canColumnTypeBeInferredByPropertyType($columnOptions->type, $typeHint)) { +// unset($columnOptions['type']); + $columnOptions->needsTypeHint = false; } - if (isset($columnOptions['type'])) { - $typeConstant = DoctrineHelper::getTypeConstant($columnOptions['type']); +// if (isset($columnOptions['type'])) { + if ($columnOptions->needsTypeHint) { + $typeConstant = DoctrineHelper::getTypeConstant($columnOptions->type); if ($typeConstant) { $this->addUseStatementIfNecessary(Types::class); - $columnOptions['type'] = $typeConstant; + $columnOptions->type = $typeConstant; } } // 2) USE property type on property below, nullable // 3) If default value, then NOT nullable - $nullable = $columnOptions['nullable'] ?? false; - $isId = (bool) ($columnOptions['id'] ?? false); - $attributes[] = $this->buildAttributeNode(Column::class, $columnOptions, 'ORM'); + $nullable = $columnOptions->nullable ?? false; + $isId = (bool) ($columnOptions->id ?? false); + + $someArray = []; + $someArray['type'] = $columnOptions->type; +// $someArray['fieldName'] = $columnOptions->fieldName; + + // @TODO foreach over the properties and check if they're null. If not -> add to $someArray + + $attributes[] = $this->buildAttributeNode(Column::class, $someArray, 'ORM'); $defaultValue = null; if ('array' === $typeHint && !$nullable) { diff --git a/tests/Util/ClassSourceManipulatorTest.php b/tests/Util/ClassSourceManipulatorTest.php index 33c44fb9c..c3622fcfc 100644 --- a/tests/Util/ClassSourceManipulatorTest.php +++ b/tests/Util/ClassSourceManipulatorTest.php @@ -20,6 +20,7 @@ use Symfony\Bundle\MakerBundle\Doctrine\RelationOneToMany; use Symfony\Bundle\MakerBundle\Doctrine\RelationOneToOne; use Symfony\Bundle\MakerBundle\Util\ClassSourceManipulator; +use Symfony\Bundle\MakerBundle\Util\CSM\ObjectMapping; use Symfony\Component\Security\Core\User\UserInterface; class ClassSourceManipulatorTest extends TestCase @@ -213,7 +214,7 @@ public function getAttributeClassTests(): \Generator /** * @dataProvider getAddEntityFieldTests */ - public function testAddEntityField(string $sourceFilename, string $propertyName, array $fieldOptions, $expectedSourceFilename): void + public function testAddEntityField(string $sourceFilename, string $propertyName, ObjectMapping $fieldOptions, $expectedSourceFilename): void { $sourcePath = __DIR__.'/fixtures/source'; $expectedPath = __DIR__.'/fixtures/add_entity_field'; @@ -226,7 +227,7 @@ public function testAddEntityField(string $sourceFilename, string $propertyName, ); } - private function runAddEntityFieldTests(string $source, string $propertyName, array $fieldOptions, string $expected): void + private function runAddEntityFieldTests(string $source, string $propertyName, ObjectMapping $fieldOptions, string $expected): void { $manipulator = new ClassSourceManipulator($source, false); $manipulator->addEntityField($propertyName, $fieldOptions); @@ -239,71 +240,78 @@ public function getAddEntityFieldTests(): \Generator yield 'entity_normal_add' => [ 'User_simple.php', 'fooProp', - [ - 'type' => 'string', - 'length' => 255, - 'nullable' => false, - 'options' => ['comment' => 'new field'], - ], + new ObjectMapping(type: 'string', length: 255, nullable: false, options: ['comment' => 'new field']), +// [ +// 'type' => 'string', +// 'length' => 255, +// 'nullable' => false, +// 'options' => ['comment' => 'new field'], +// ], 'User_simple.php', ]; yield 'entity_add_datetime' => [ 'User_simple.php', 'createdAt', - [ - 'type' => 'datetime', - 'nullable' => true, - ], + new ObjectMapping(type: 'datetime', nullable: true), +// [ +// 'type' => 'datetime', +// 'nullable' => true, +// ], 'User_simple_datetime.php', ]; yield 'entity_field_property_already_exists' => [ 'User_some_props.php', 'firstName', - [ - 'type' => 'string', - 'length' => 255, - 'nullable' => false, - ], + new ObjectMapping(type: 'string', length: 255, nullable: false), +// [ +// 'type' => 'string', +// 'length' => 255, +// 'nullable' => false, +// ], 'User_simple_prop_already_exists.php', ]; yield 'entity_field_property_zero' => [ 'User_simple.php', 'decimal', - [ - 'type' => 'decimal', - 'precision' => 6, - 'scale' => 0, - ], + new ObjectMapping(type: 'decimal', precision: 6, scale: 0), +// [ +// 'type' => 'decimal', +// 'precision' => 6, +// 'scale' => 0, +// ], 'User_simple_prop_zero.php', ]; yield 'entity_add_object' => [ 'User_simple.php', 'someObject', - [ - 'type' => 'object', - ], + new ObjectMapping(type: 'object'), +// [ +// 'type' => 'object', +// ], 'User_simple_object.php', ]; yield 'entity_add_uuid' => [ 'User_simple.php', 'uuid', - [ - 'type' => 'uuid', - ], + new ObjectMapping(type: 'uuid'), +// [ +// 'type' => 'uuid', +// ], 'User_simple_uuid.php', ]; yield 'entity_add_ulid' => [ 'User_simple.php', 'ulid', - [ - 'type' => 'ulid', - ], + new ObjectMapping(type: 'ulid'), +// [ +// 'type' => 'ulid', +// ], 'User_simple_ulid.php', ]; } From 4fdf4e759c4f82ac204ed436d5a2f12213c58b1d Mon Sep 17 00:00:00 2001 From: Jesse Rushlow Date: Mon, 5 Feb 2024 13:09:37 -0500 Subject: [PATCH 03/24] which tests blow up --- src/Doctrine/EntityRegenerator.php | 10 +++- src/Maker/MakeEntity.php | 4 +- src/Security/UserClassBuilder.php | 41 +++++++++------- src/Util/CSM/ClassPropertyModel.php | 58 +++++++++++++++++++++++ src/Util/CSM/ObjectMapping.php | 36 -------------- src/Util/ClassSourceManipulator.php | 42 ++++++++-------- tests/Util/ClassSourceManipulatorTest.php | 38 +++++++-------- 7 files changed, 134 insertions(+), 95 deletions(-) create mode 100644 src/Util/CSM/ClassPropertyModel.php delete mode 100644 src/Util/CSM/ObjectMapping.php diff --git a/src/Doctrine/EntityRegenerator.php b/src/Doctrine/EntityRegenerator.php index ac865ec05..55bd9e980 100644 --- a/src/Doctrine/EntityRegenerator.php +++ b/src/Doctrine/EntityRegenerator.php @@ -18,6 +18,7 @@ use Symfony\Bundle\MakerBundle\FileManager; use Symfony\Bundle\MakerBundle\Generator; use Symfony\Bundle\MakerBundle\Util\ClassSourceManipulator; +use Symfony\Bundle\MakerBundle\Util\CSM\ClassPropertyModel; /** * @internal @@ -95,7 +96,11 @@ public function regenerateEntities(string $classOrNamespace): void if (str_contains($fieldName, '.')) { [$fieldName, $embeddedFiledName] = explode('.', $fieldName); - $operations[$embeddedClasses[$fieldName]]->addEntityField($embeddedFiledName, $mapping); +// $operations[$embeddedClasses[$fieldName]]->addEntityField($embeddedFiledName, $mapping); + $operations[$embeddedClasses[$fieldName]]->addEntityField(new ClassPropertyModel( + propertyName: $embeddedFiledName, + type: $mapping['type'] ?? $mapping->type + )); continue; } @@ -106,7 +111,8 @@ public function regenerateEntities(string $classOrNamespace): void // dump([$classMetadata]); - $manipulator->addEntityField($fieldName, (array) $mapping); +// $manipulator->addEntityField($fieldName, (array) $mapping); + $manipulator->addEntityField(new ClassPropertyModel(propertyName: $fieldName, type: $mapping['type'] ?? $mapping->type)); } $getIsNullable = function (array $mapping) { diff --git a/src/Maker/MakeEntity.php b/src/Maker/MakeEntity.php index 07896c675..7e58277a7 100644 --- a/src/Maker/MakeEntity.php +++ b/src/Maker/MakeEntity.php @@ -28,6 +28,7 @@ use Symfony\Bundle\MakerBundle\Util\ClassDetails; use Symfony\Bundle\MakerBundle\Util\ClassSourceManipulator; use Symfony\Bundle\MakerBundle\Util\CliOutputHelper; +use Symfony\Bundle\MakerBundle\Util\CSM\ClassPropertyModel; use Symfony\Bundle\MakerBundle\Util\PhpCompatUtil; use Symfony\Bundle\MakerBundle\Validator; use Symfony\Component\Console\Command\Command; @@ -230,7 +231,8 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen if (\is_array($newField)) { $annotationOptions = $newField; unset($annotationOptions['fieldName']); - $manipulator->addEntityField($newField['fieldName'], $annotationOptions); +// $manipulator->addEntityField($newField['fieldName'], $annotationOptions); + $manipulator->addEntityField(new ClassPropertyModel(propertyName: $newField['fieldName'], type: $annotationOptions['type'])); $currentFields[] = $newField['fieldName']; } elseif ($newField instanceof EntityRelation) { diff --git a/src/Security/UserClassBuilder.php b/src/Security/UserClassBuilder.php index 376879f11..8f4e43475 100644 --- a/src/Security/UserClassBuilder.php +++ b/src/Security/UserClassBuilder.php @@ -13,6 +13,7 @@ use PhpParser\Node; use Symfony\Bundle\MakerBundle\Util\ClassSourceManipulator; +use Symfony\Bundle\MakerBundle\Util\CSM\ClassPropertyModel; use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; use Symfony\Component\Security\Core\User\UserInterface; @@ -52,13 +53,19 @@ private function addGetUsername(ClassSourceManipulator $manipulator, UserClassCo if ($userClassConfig->isEntity()) { // add entity property $manipulator->addEntityField( - $userClassConfig->getIdentityPropertyName(), - [ - 'type' => 'string', - // https://github.com/FriendsOfSymfony/FOSUserBundle/issues/1919 - 'length' => 180, - 'unique' => true, - ] + new ClassPropertyModel( + propertyName: $userClassConfig->getIdentityPropertyName(), + type: 'string', + length: 180, + unique: true, + ) + +// [ +// 'type' => 'string', +// // https://github.com/FriendsOfSymfony/FOSUserBundle/issues/1919 +// 'length' => 180, +// 'unique' => true, +// ] ); } else { // add normal property @@ -99,10 +106,11 @@ private function addGetRoles(ClassSourceManipulator $manipulator, UserClassConfi if ($userClassConfig->isEntity()) { // add entity property $manipulator->addEntityField( - 'roles', - [ - 'type' => 'json', - ] + new ClassPropertyModel(propertyName: 'roles', type: 'json') +// 'roles', +// [ +// 'type' => 'json', +// ] ); } else { // add normal property @@ -202,11 +210,12 @@ private function addGetPassword(ClassSourceManipulator $manipulator, UserClassCo if ($userClassConfig->isEntity()) { // add entity property $manipulator->addEntityField( - 'password', - [ - 'type' => 'string', - ], - [$propertyDocs] + new ClassPropertyModel(propertyName: 'password', type: 'string', comments: [$propertyDocs]) +// 'password', +// [ +// 'type' => 'string', +// ], +// [$propertyDocs] ); } else { // add normal property diff --git a/src/Util/CSM/ClassPropertyModel.php b/src/Util/CSM/ClassPropertyModel.php new file mode 100644 index 000000000..f23a5929a --- /dev/null +++ b/src/Util/CSM/ClassPropertyModel.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\MakerBundle\Util\CSM; + +class ClassPropertyModel +{ + public function __construct( + public string $propertyName, + public string $type, + public array $comments = [], + public ?int $length = null, + public ?string $id = null, + public ?bool $nullable = null, + public array $options = [], + public ?int $precision = null, + public ?int $scale = null, + public bool $needsTypeHint = true, + public bool $unique = false, + ) { + } + + public function getAttributes(): array + { + $attributes = []; + + $attributes['type'] = $this->type; + +// if (!empty($this->comments)) { +// $attributes['options'] = $this->comments; +// } + + foreach (['length', 'id', 'nullable', 'precision', 'scale'] as $property) { + if (null !== $this->$property) { + $attributes[$property] = $this->$property; + } + } + + return $attributes; + } + +// public static function createFromObject(\Doctrine\ORM\Mapping\FieldMapping $mapping): self +// { +//// return new self( +//// type: $mapping->type, +//// id: $mapping->id, +//// nullable: $mapping->nullable, +//// ); +// } +} diff --git a/src/Util/CSM/ObjectMapping.php b/src/Util/CSM/ObjectMapping.php deleted file mode 100644 index 4462d058d..000000000 --- a/src/Util/CSM/ObjectMapping.php +++ /dev/null @@ -1,36 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\MakerBundle\Util\CSM; - -class ObjectMapping -{ - public function __construct( - public string $type, - public ?int $length = null, - public ?string $id = null, - public ?bool $nullable = null, - public array $options = [], - public ?int $precision = null, - public ?int $scale = null, - public bool $needsTypeHint = true, - ) { - } - - public static function createFromObject(\Doctrine\ORM\Mapping\FieldMapping $mapping): self - { - return new self( - type: $mapping->type, - id: $mapping->id, - nullable: $mapping->nullable, - ); - } -} diff --git a/src/Util/ClassSourceManipulator.php b/src/Util/ClassSourceManipulator.php index 185c525ff..00449306a 100644 --- a/src/Util/ClassSourceManipulator.php +++ b/src/Util/ClassSourceManipulator.php @@ -38,7 +38,7 @@ use Symfony\Bundle\MakerBundle\Doctrine\RelationOneToMany; use Symfony\Bundle\MakerBundle\Doctrine\RelationOneToOne; use Symfony\Bundle\MakerBundle\Str; -use Symfony\Bundle\MakerBundle\Util\CSM\ObjectMapping; +use Symfony\Bundle\MakerBundle\Util\CSM\ClassPropertyModel; /** * @internal @@ -97,7 +97,7 @@ public function getSourceCode(): string return $this->sourceCode; } - public function addEntityField(string $propertyName, ObjectMapping $columnOptions, array $comments = []): void + public function addEntityField(ClassPropertyModel $mapping): void { // if ($columnOptions instanceof \Doctrine\ORM\Mapping\FieldMapping::class) { // $columnOptions['type'] = $columnOptions->type; @@ -105,40 +105,40 @@ public function addEntityField(string $propertyName, ObjectMapping $columnOption // $columnOptions['id'] = $columnOptions->id; // } - if (is_array($columnOptions)) { - dump($columnOptions); - $columnOptions = \Doctrine\ORM\Mapping\FieldMapping::fromMappingArray($columnOptions); - } +// if (is_array($columnOptions)) { +// dump($columnOptions); +// $columnOptions = \Doctrine\ORM\Mapping\FieldMapping::fromMappingArray($columnOptions); +// } - dump($columnOptions); - $typeHint = DoctrineHelper::getPropertyTypeForColumn($columnOptions->type); - if ($typeHint && DoctrineHelper::canColumnTypeBeInferredByPropertyType($columnOptions->type, $typeHint)) { +// dump($columnOptions); + $typeHint = DoctrineHelper::getPropertyTypeForColumn($mapping->type); + if ($typeHint && DoctrineHelper::canColumnTypeBeInferredByPropertyType($mapping->type, $typeHint)) { // unset($columnOptions['type']); - $columnOptions->needsTypeHint = false; + $mapping->needsTypeHint = false; } // if (isset($columnOptions['type'])) { - if ($columnOptions->needsTypeHint) { - $typeConstant = DoctrineHelper::getTypeConstant($columnOptions->type); + if ($mapping->needsTypeHint) { + $typeConstant = DoctrineHelper::getTypeConstant($mapping->type); if ($typeConstant) { $this->addUseStatementIfNecessary(Types::class); - $columnOptions->type = $typeConstant; + $mapping->type = $typeConstant; } } // 2) USE property type on property below, nullable // 3) If default value, then NOT nullable - $nullable = $columnOptions->nullable ?? false; - $isId = (bool) ($columnOptions->id ?? false); + $nullable = $mapping->nullable ?? false; + $isId = (bool) ($mapping->id ?? false); $someArray = []; - $someArray['type'] = $columnOptions->type; + $someArray['type'] = $mapping->type; // $someArray['fieldName'] = $columnOptions->fieldName; // @TODO foreach over the properties and check if they're null. If not -> add to $someArray - $attributes[] = $this->buildAttributeNode(Column::class, $someArray, 'ORM'); + $attributes[] = $this->buildAttributeNode(Column::class, $mapping->getAttributes(), 'ORM'); $defaultValue = null; if ('array' === $typeHint && !$nullable) { @@ -154,15 +154,15 @@ public function addEntityField(string $propertyName, ObjectMapping $columnOption } $this->addProperty( - name: $propertyName, + name: $mapping->propertyName, defaultValue: $defaultValue, attributes: $attributes, - comments: $comments, + comments: $mapping->comments, propertyType: $propertyType ); $this->addGetter( - $propertyName, + $mapping->propertyName, $typeHint, // getter methods always have nullable return values // because even though these are required in the db, they may not be set yet @@ -172,7 +172,7 @@ public function addEntityField(string $propertyName, ObjectMapping $columnOption // don't generate setters for id fields if (!$isId) { - $this->addSetter($propertyName, $typeHint, $nullable); + $this->addSetter($mapping->propertyName, $typeHint, $nullable); } } diff --git a/tests/Util/ClassSourceManipulatorTest.php b/tests/Util/ClassSourceManipulatorTest.php index c3622fcfc..014d9b74d 100644 --- a/tests/Util/ClassSourceManipulatorTest.php +++ b/tests/Util/ClassSourceManipulatorTest.php @@ -20,7 +20,7 @@ use Symfony\Bundle\MakerBundle\Doctrine\RelationOneToMany; use Symfony\Bundle\MakerBundle\Doctrine\RelationOneToOne; use Symfony\Bundle\MakerBundle\Util\ClassSourceManipulator; -use Symfony\Bundle\MakerBundle\Util\CSM\ObjectMapping; +use Symfony\Bundle\MakerBundle\Util\CSM\ClassPropertyModel; use Symfony\Component\Security\Core\User\UserInterface; class ClassSourceManipulatorTest extends TestCase @@ -214,23 +214,23 @@ public function getAttributeClassTests(): \Generator /** * @dataProvider getAddEntityFieldTests */ - public function testAddEntityField(string $sourceFilename, string $propertyName, ObjectMapping $fieldOptions, $expectedSourceFilename): void + public function testAddEntityField(string $sourceFilename, ClassPropertyModel $fieldOptions, $expectedSourceFilename): void { $sourcePath = __DIR__.'/fixtures/source'; $expectedPath = __DIR__.'/fixtures/add_entity_field'; $this->runAddEntityFieldTests( file_get_contents(sprintf('%s/%s', $sourcePath, $sourceFilename)), - $propertyName, +// $propertyName, $fieldOptions, file_get_contents(sprintf('%s/%s', $expectedPath, $expectedSourceFilename)) ); } - private function runAddEntityFieldTests(string $source, string $propertyName, ObjectMapping $fieldOptions, string $expected): void + private function runAddEntityFieldTests(string $source, ClassPropertyModel $fieldOptions, string $expected): void { $manipulator = new ClassSourceManipulator($source, false); - $manipulator->addEntityField($propertyName, $fieldOptions); + $manipulator->addEntityField($fieldOptions); $this->assertSame($expected, $manipulator->getSourceCode()); } @@ -239,8 +239,8 @@ public function getAddEntityFieldTests(): \Generator { yield 'entity_normal_add' => [ 'User_simple.php', - 'fooProp', - new ObjectMapping(type: 'string', length: 255, nullable: false, options: ['comment' => 'new field']), +// 'fooProp', + new ClassPropertyModel(propertyName: 'fooProp', type: 'string', length: 255, nullable: false, options: ['comment' => 'new field']), // [ // 'type' => 'string', // 'length' => 255, @@ -252,8 +252,8 @@ public function getAddEntityFieldTests(): \Generator yield 'entity_add_datetime' => [ 'User_simple.php', - 'createdAt', - new ObjectMapping(type: 'datetime', nullable: true), +// 'createdAt', + new ClassPropertyModel(propertyName: 'createdAt', type: 'datetime', nullable: true), // [ // 'type' => 'datetime', // 'nullable' => true, @@ -263,8 +263,8 @@ public function getAddEntityFieldTests(): \Generator yield 'entity_field_property_already_exists' => [ 'User_some_props.php', - 'firstName', - new ObjectMapping(type: 'string', length: 255, nullable: false), +// 'firstName', + new ClassPropertyModel(propertyName: 'firstName', type: 'string', length: 255, nullable: false), // [ // 'type' => 'string', // 'length' => 255, @@ -275,8 +275,8 @@ public function getAddEntityFieldTests(): \Generator yield 'entity_field_property_zero' => [ 'User_simple.php', - 'decimal', - new ObjectMapping(type: 'decimal', precision: 6, scale: 0), +// 'decimal', + new ClassPropertyModel(propertyName: 'decimal', type: 'decimal', precision: 6, scale: 0), // [ // 'type' => 'decimal', // 'precision' => 6, @@ -287,8 +287,8 @@ public function getAddEntityFieldTests(): \Generator yield 'entity_add_object' => [ 'User_simple.php', - 'someObject', - new ObjectMapping(type: 'object'), +// 'someObject', + new ClassPropertyModel(propertyName: 'someObject', type: 'object'), // [ // 'type' => 'object', // ], @@ -297,8 +297,8 @@ public function getAddEntityFieldTests(): \Generator yield 'entity_add_uuid' => [ 'User_simple.php', - 'uuid', - new ObjectMapping(type: 'uuid'), +// 'uuid', + new ClassPropertyModel(propertyName: 'uuid', type: 'uuid'), // [ // 'type' => 'uuid', // ], @@ -307,8 +307,8 @@ public function getAddEntityFieldTests(): \Generator yield 'entity_add_ulid' => [ 'User_simple.php', - 'ulid', - new ObjectMapping(type: 'ulid'), +// 'ulid', + new ClassPropertyModel(propertyName: 'ulid', type: 'ulid'), // [ // 'type' => 'ulid', // ], From 1cdb1f7029398ae5bb41e9de00c9060cde84bc61 Mon Sep 17 00:00:00 2001 From: Jesse Rushlow Date: Mon, 5 Feb 2024 13:59:16 -0500 Subject: [PATCH 04/24] use prop model in make:entity --- src/Doctrine/EntityRegenerator.php | 1 + src/Maker/MakeEntity.php | 34 +++++++++++++++++++---------- src/Util/ClassSourceManipulator.php | 1 + 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/Doctrine/EntityRegenerator.php b/src/Doctrine/EntityRegenerator.php index 55bd9e980..c6cc9fe5f 100644 --- a/src/Doctrine/EntityRegenerator.php +++ b/src/Doctrine/EntityRegenerator.php @@ -97,6 +97,7 @@ public function regenerateEntities(string $classOrNamespace): void [$fieldName, $embeddedFiledName] = explode('.', $fieldName); // $operations[$embeddedClasses[$fieldName]]->addEntityField($embeddedFiledName, $mapping); + dump(['regen' => $embeddedFiledName, $mapping]); $operations[$embeddedClasses[$fieldName]]->addEntityField(new ClassPropertyModel( propertyName: $embeddedFiledName, type: $mapping['type'] ?? $mapping->type diff --git a/src/Maker/MakeEntity.php b/src/Maker/MakeEntity.php index 7e58277a7..867ee0a65 100644 --- a/src/Maker/MakeEntity.php +++ b/src/Maker/MakeEntity.php @@ -228,13 +228,16 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen $fileManagerOperations = []; $fileManagerOperations[$entityPath] = $manipulator; - if (\is_array($newField)) { - $annotationOptions = $newField; - unset($annotationOptions['fieldName']); + if ($newField instanceof ClassPropertyModel) { +// if (\is_array($newField)) { +// $annotationOptions = $newField; +// unset($annotationOptions['fieldName']); +// dump(['newField' => $newField]); // $manipulator->addEntityField($newField['fieldName'], $annotationOptions); - $manipulator->addEntityField(new ClassPropertyModel(propertyName: $newField['fieldName'], type: $annotationOptions['type'])); + $manipulator->addEntityField($newField); - $currentFields[] = $newField['fieldName']; + $currentFields[] = $newField->propertyName; +// $currentFields[] = $newField['fieldName']; } elseif ($newField instanceof EntityRelation) { // both overridden below for OneToMany $newFieldName = $newField->getOwningProperty(); @@ -331,7 +334,7 @@ public function configureDependencies(DependencyBuilder $dependencies, InputInte ORMDependencyBuilder::buildDependencies($dependencies); } - private function askForNextField(ConsoleStyle $io, array $fields, string $entityClass, bool $isFirstField): EntityRelation|array|null + private function askForNextField(ConsoleStyle $io, array $fields, string $entityClass, bool $isFirstField): EntityRelation|ClassPropertyModel|null { $io->writeln(''); @@ -409,23 +412,30 @@ private function askForNextField(ConsoleStyle $io, array $fields, string $entity } // this is a normal field - $data = ['fieldName' => $fieldName, 'type' => $type]; +// $data = ['fieldName' => $fieldName, 'type' => $type]; + $classProperty = new ClassPropertyModel(propertyName: $fieldName, type: $type); + if ('string' === $type) { // default to 255, avoid the question - $data['length'] = $io->ask('Field length', 255, [Validator::class, 'validateLength']); + $classProperty->length = $io->ask('Field length', 255, [Validator::class, 'validateLength']); +// $data['length'] = $io->ask('Field length', 255, [Validator::class, 'validateLength']); } elseif ('decimal' === $type) { // 10 is the default value given in \Doctrine\DBAL\Schema\Column::$_precision - $data['precision'] = $io->ask('Precision (total number of digits stored: 100.00 would be 5)', 10, [Validator::class, 'validatePrecision']); +// $data['precision'] = $io->ask('Precision (total number of digits stored: 100.00 would be 5)', 10, [Validator::class, 'validatePrecision']); + $classProperty->precision = $io->ask('Precision (total number of digits stored: 100.00 would be 5)', 10, [Validator::class, 'validatePrecision']); // 0 is the default value given in \Doctrine\DBAL\Schema\Column::$_scale - $data['scale'] = $io->ask('Scale (number of decimals to store: 100.00 would be 2)', 0, [Validator::class, 'validateScale']); +// $data['scale'] = $io->ask('Scale (number of decimals to store: 100.00 would be 2)', 0, [Validator::class, 'validateScale']); + $classProperty->scale = $io->ask('Scale (number of decimals to store: 100.00 would be 2)', 0, [Validator::class, 'validateScale']); } if ($io->confirm('Can this field be null in the database (nullable)', false)) { - $data['nullable'] = true; + $classProperty->nullable = true; +// $data['nullable'] = true; } - return $data; + return $classProperty; +// return $data; } private function printAvailableTypes(ConsoleStyle $io): void diff --git a/src/Util/ClassSourceManipulator.php b/src/Util/ClassSourceManipulator.php index 00449306a..4a39b26c1 100644 --- a/src/Util/ClassSourceManipulator.php +++ b/src/Util/ClassSourceManipulator.php @@ -138,6 +138,7 @@ public function addEntityField(ClassPropertyModel $mapping): void // @TODO foreach over the properties and check if they're null. If not -> add to $someArray + dump($mapping->getAttributes(), $mapping); $attributes[] = $this->buildAttributeNode(Column::class, $mapping->getAttributes(), 'ORM'); $defaultValue = null; From c4cbff840849bfa1fa0145fef333d994c1763bfb Mon Sep 17 00:00:00 2001 From: Jesse Rushlow Date: Mon, 5 Feb 2024 14:02:31 -0500 Subject: [PATCH 05/24] dont create the schema in make:migration tests --- src/Test/MakerTestRunner.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Test/MakerTestRunner.php b/src/Test/MakerTestRunner.php index 14f9cfb98..6bd12c21a 100644 --- a/src/Test/MakerTestRunner.php +++ b/src/Test/MakerTestRunner.php @@ -175,8 +175,10 @@ public function configureDatabase(bool $createSchema = true): void // make sure the database doesn't exist... $this->runConsole('doctrine:database:drop', [], '--env=test --force'); - // create the schema - $this->runConsole('doctrine:schema:create', [], '--env=test'); + if ($createSchema) { + // create the schema + $this->runConsole('doctrine:schema:create', [], '--env=test'); + } return; } From 90a31235c911912b0fe1d2ea6c68be78e98fffd6 Mon Sep 17 00:00:00 2001 From: Jesse Rushlow Date: Tue, 6 Feb 2024 02:54:29 -0500 Subject: [PATCH 06/24] fix make form tests --- .../tests/it_generates_form_with_many_to_many_relation.php | 4 ++-- .../tests/it_generates_form_with_many_to_one_relation.php | 7 +++---- .../tests/it_generates_form_with_one_to_one_relation.php | 7 +++---- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/tests/fixtures/make-form/tests/it_generates_form_with_many_to_many_relation.php b/tests/fixtures/make-form/tests/it_generates_form_with_many_to_many_relation.php index 6652306b8..5be663aa6 100644 --- a/tests/fixtures/make-form/tests/it_generates_form_with_many_to_many_relation.php +++ b/tests/fixtures/make-form/tests/it_generates_form_with_many_to_many_relation.php @@ -6,10 +6,10 @@ use App\Entity\Book; use App\Form\BookType; use Doctrine\Common\Collections\ArrayCollection; -use Doctrine\ORM\AbstractQuery; use Doctrine\ORM\EntityManager; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Query; use Doctrine\ORM\QueryBuilder; use Doctrine\Persistence\ManagerRegistry; use Symfony\Bridge\Doctrine\Form\DoctrineOrmExtension; @@ -80,7 +80,7 @@ protected function getExtensions(): array [Library::class, new ClassMetadata(Library::class)], ]); - $execute = $this->createMock(AbstractQuery::class); + $execute = $this->createMock(Query::class); $execute->method('execute') ->willReturn([ (new Library())->setName('foo'), diff --git a/tests/fixtures/make-form/tests/it_generates_form_with_many_to_one_relation.php b/tests/fixtures/make-form/tests/it_generates_form_with_many_to_one_relation.php index 49e5626c0..f73845cd5 100644 --- a/tests/fixtures/make-form/tests/it_generates_form_with_many_to_one_relation.php +++ b/tests/fixtures/make-form/tests/it_generates_form_with_many_to_one_relation.php @@ -5,10 +5,10 @@ use App\Entity\Author; use App\Entity\Book; use App\Form\BookType; -use Doctrine\ORM\AbstractQuery; use Doctrine\ORM\EntityManager; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Query; use Doctrine\ORM\QueryBuilder; use Doctrine\Persistence\ManagerRegistry; use Symfony\Bridge\Doctrine\Form\DoctrineOrmExtension; @@ -53,17 +53,16 @@ protected function getExtensions(): array ]) ; - $execute = $this->createMock(AbstractQuery::class); + $execute = $this->createMock(Query::class); $execute->method('execute') ->willReturn([ - (new Author())->setName('foo') + (new Author())->setName('foo'), ]); $query = $this->createMock(QueryBuilder::class); $query->method('getQuery') ->willReturn($execute); - $entityRepository = $this->createMock(EntityRepository::class); $entityRepository->method('createQueryBuilder') ->willReturn($query) diff --git a/tests/fixtures/make-form/tests/it_generates_form_with_one_to_one_relation.php b/tests/fixtures/make-form/tests/it_generates_form_with_one_to_one_relation.php index ef5b6de47..deb501726 100644 --- a/tests/fixtures/make-form/tests/it_generates_form_with_one_to_one_relation.php +++ b/tests/fixtures/make-form/tests/it_generates_form_with_one_to_one_relation.php @@ -5,10 +5,10 @@ use App\Entity\Librarian; use App\Entity\Library; use App\Form\LibraryType; -use Doctrine\ORM\AbstractQuery; use Doctrine\ORM\EntityManager; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Query; use Doctrine\ORM\QueryBuilder; use Doctrine\Persistence\ManagerRegistry; use Symfony\Bridge\Doctrine\Form\DoctrineOrmExtension; @@ -53,17 +53,16 @@ protected function getExtensions(): array ]) ; - $execute = $this->createMock(AbstractQuery::class); + $execute = $this->createMock(Query::class); $execute->method('execute') ->willReturn([ - (new Librarian())->setName('foo') + (new Librarian())->setName('foo'), ]); $query = $this->createMock(QueryBuilder::class); $query->method('getQuery') ->willReturn($execute); - $entityRepository = $this->createMock(EntityRepository::class); $entityRepository->method('createQueryBuilder') ->willReturn($query) From 2192cc5018da4a4939fa20028e30405316a01cf6 Mon Sep 17 00:00:00 2001 From: Jesse Rushlow Date: Tue, 6 Feb 2024 03:46:15 -0500 Subject: [PATCH 07/24] dont add type attribute if type is inferred --- src/Util/CSM/ClassPropertyModel.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Util/CSM/ClassPropertyModel.php b/src/Util/CSM/ClassPropertyModel.php index f23a5929a..e82ce0217 100644 --- a/src/Util/CSM/ClassPropertyModel.php +++ b/src/Util/CSM/ClassPropertyModel.php @@ -32,11 +32,13 @@ public function getAttributes(): array { $attributes = []; - $attributes['type'] = $this->type; + if ($this->needsTypeHint) { + $attributes['type'] = $this->type; + } -// if (!empty($this->comments)) { -// $attributes['options'] = $this->comments; -// } + if (!empty($this->options)) { + $attributes['options'] = $this->options; + } foreach (['length', 'id', 'nullable', 'precision', 'scale'] as $property) { if (null !== $this->$property) { From 363001e744a981616976e75d5bb0f934fd72f6d8 Mon Sep 17 00:00:00 2001 From: Jesse Rushlow Date: Tue, 6 Feb 2024 03:47:42 -0500 Subject: [PATCH 08/24] WIP - allow doctrine association mapping classes in regen --- src/Doctrine/EntityRegenerator.php | 10 ++-------- src/Util/ClassSourceManipulator.php | 27 +++------------------------ 2 files changed, 5 insertions(+), 32 deletions(-) diff --git a/src/Doctrine/EntityRegenerator.php b/src/Doctrine/EntityRegenerator.php index c6cc9fe5f..eceacfb01 100644 --- a/src/Doctrine/EntityRegenerator.php +++ b/src/Doctrine/EntityRegenerator.php @@ -76,8 +76,6 @@ public function regenerateEntities(string $classOrNamespace): void continue; } - // dump([$fieldName => $mapping]); - $className = $mapping['class']; $embeddedClasses[$fieldName] = $this->getPathOfClass($className); @@ -96,8 +94,6 @@ public function regenerateEntities(string $classOrNamespace): void if (str_contains($fieldName, '.')) { [$fieldName, $embeddedFiledName] = explode('.', $fieldName); -// $operations[$embeddedClasses[$fieldName]]->addEntityField($embeddedFiledName, $mapping); - dump(['regen' => $embeddedFiledName, $mapping]); $operations[$embeddedClasses[$fieldName]]->addEntityField(new ClassPropertyModel( propertyName: $embeddedFiledName, type: $mapping['type'] ?? $mapping->type @@ -110,13 +106,11 @@ public function regenerateEntities(string $classOrNamespace): void continue; } - // dump([$classMetadata]); - -// $manipulator->addEntityField($fieldName, (array) $mapping); $manipulator->addEntityField(new ClassPropertyModel(propertyName: $fieldName, type: $mapping['type'] ?? $mapping->type)); } - $getIsNullable = function (array $mapping) { + // @TODO _ @legacy - Type needed for mapping + $getIsNullable = static function ($mapping) { if (!isset($mapping['joinColumns'][0]['nullable'])) { // the default for relationships IS nullable return true; diff --git a/src/Util/ClassSourceManipulator.php b/src/Util/ClassSourceManipulator.php index 4a39b26c1..1ae8ed3c2 100644 --- a/src/Util/ClassSourceManipulator.php +++ b/src/Util/ClassSourceManipulator.php @@ -99,25 +99,11 @@ public function getSourceCode(): string public function addEntityField(ClassPropertyModel $mapping): void { -// if ($columnOptions instanceof \Doctrine\ORM\Mapping\FieldMapping::class) { -// $columnOptions['type'] = $columnOptions->type; -// $columnOptions['nullable'] = $columnOptions->nullable; -// $columnOptions['id'] = $columnOptions->id; -// } - -// if (is_array($columnOptions)) { -// dump($columnOptions); -// $columnOptions = \Doctrine\ORM\Mapping\FieldMapping::fromMappingArray($columnOptions); -// } - -// dump($columnOptions); $typeHint = DoctrineHelper::getPropertyTypeForColumn($mapping->type); if ($typeHint && DoctrineHelper::canColumnTypeBeInferredByPropertyType($mapping->type, $typeHint)) { -// unset($columnOptions['type']); $mapping->needsTypeHint = false; } -// if (isset($columnOptions['type'])) { if ($mapping->needsTypeHint) { $typeConstant = DoctrineHelper::getTypeConstant($mapping->type); if ($typeConstant) { @@ -132,13 +118,6 @@ public function addEntityField(ClassPropertyModel $mapping): void $nullable = $mapping->nullable ?? false; $isId = (bool) ($mapping->id ?? false); - $someArray = []; - $someArray['type'] = $mapping->type; -// $someArray['fieldName'] = $columnOptions->fieldName; - - // @TODO foreach over the properties and check if they're null. If not -> add to $someArray - - dump($mapping->getAttributes(), $mapping); $attributes[] = $this->buildAttributeNode(Column::class, $mapping->getAttributes(), 'ORM'); $defaultValue = null; @@ -333,7 +312,7 @@ public function addConstructor(array $params, string $methodBody): void /** * @param Node[] $params */ - public function addMethodBuilder(Builder\Method $methodBuilder, array $params = [], string $methodBody = null): void + public function addMethodBuilder(Builder\Method $methodBuilder, array $params = [], ?string $methodBody = null): void { $this->addMethodParams($methodBuilder, $params); @@ -382,7 +361,7 @@ public function createMethodLevelBlankLine() /** * @param array $attributes */ - public function addProperty(string $name, $defaultValue = self::DEFAULT_VALUE_NONE, array $attributes = [], array $comments = [], string $propertyType = null): void + public function addProperty(string $name, $defaultValue = self::DEFAULT_VALUE_NONE, array $attributes = [], array $comments = [], ?string $propertyType = null): void { if ($this->propertyExists($name)) { // we never overwrite properties @@ -866,7 +845,7 @@ public function addUseStatementIfNecessary(string $class): string * @param array $options The named arguments for the attribute ($key = argument name, $value = argument value) * @param ?string $attributePrefix If a prefix is provided, the node is built using the prefix. E.g. #[ORM\Column()] */ - public function buildAttributeNode(string $attributeClass, array $options, string $attributePrefix = null): Node\Attribute + public function buildAttributeNode(string $attributeClass, array $options, ?string $attributePrefix = null): Node\Attribute { $options = $this->sortOptionsByClassConstructorParameters($options, $attributeClass); From fe7f01dd9eae93fa86269ca5fe86e4b025866547 Mon Sep 17 00:00:00 2001 From: Jesse Rushlow Date: Tue, 6 Feb 2024 04:06:17 -0500 Subject: [PATCH 09/24] missing unique attribute --- src/Util/CSM/ClassPropertyModel.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Util/CSM/ClassPropertyModel.php b/src/Util/CSM/ClassPropertyModel.php index e82ce0217..903863466 100644 --- a/src/Util/CSM/ClassPropertyModel.php +++ b/src/Util/CSM/ClassPropertyModel.php @@ -40,6 +40,10 @@ public function getAttributes(): array $attributes['options'] = $this->options; } + if ($this->unique) { + $attributes['unique'] = true; + } + foreach (['length', 'id', 'nullable', 'precision', 'scale'] as $property) { if (null !== $this->$property) { $attributes[$property] = $this->$property; From 96b0290a1d32fca8240442a86be46a8f35fd6bcf Mon Sep 17 00:00:00 2001 From: Jesse Rushlow Date: Tue, 6 Feb 2024 04:55:04 -0500 Subject: [PATCH 10/24] fix missing id attribute --- src/Doctrine/EntityRegenerator.php | 5 ++++- src/Util/CSM/ClassPropertyModel.php | 23 ++++++++++++++++++++++- src/Util/ClassSourceManipulator.php | 3 +-- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/Doctrine/EntityRegenerator.php b/src/Doctrine/EntityRegenerator.php index eceacfb01..639a20f14 100644 --- a/src/Doctrine/EntityRegenerator.php +++ b/src/Doctrine/EntityRegenerator.php @@ -106,7 +106,10 @@ public function regenerateEntities(string $classOrNamespace): void continue; } - $manipulator->addEntityField(new ClassPropertyModel(propertyName: $fieldName, type: $mapping['type'] ?? $mapping->type)); +// $x = ClassPropertyModel::createFromArray($mapping); +// dump([$fieldName, $mapping, $x]); +// $manipulator->addEntityField(new ClassPropertyModel(propertyName: $fieldName, type: $mapping['type'] ?? $mapping->type)); + $manipulator->addEntityField(ClassPropertyModel::createFromArray($mapping)); } // @TODO _ @legacy - Type needed for mapping diff --git a/src/Util/CSM/ClassPropertyModel.php b/src/Util/CSM/ClassPropertyModel.php index 903863466..5e2d2c51a 100644 --- a/src/Util/CSM/ClassPropertyModel.php +++ b/src/Util/CSM/ClassPropertyModel.php @@ -18,7 +18,7 @@ public function __construct( public string $type, public array $comments = [], public ?int $length = null, - public ?string $id = null, + public ?bool $id = null, public ?bool $nullable = null, public array $options = [], public ?int $precision = null, @@ -53,6 +53,27 @@ public function getAttributes(): array return $attributes; } + public static function createFromArray(\Doctrine\ORM\Mapping\FieldMapping|array $data): self + { + // @TODO - Better exception + if (empty($data['fieldName']) || empty($data['type'])) { + throw new \RuntimeException('Needs name and type'); + } + + return new self( + propertyName: $data['fieldName'], + type: $data['type'], + comments: $data['comments'] ?? [], + length: $data['length'] ?? null, + id: $data['id'] ?? false, + nullable: $data['nullable'] ?? false, + options: $data['options'] ?? [], + precision: $data['precision'] ?? null, + scale: $data['scale'] ?? null, + unique: $data['unique'] ?? false, + ); + } + // public static function createFromObject(\Doctrine\ORM\Mapping\FieldMapping $mapping): self // { //// return new self( diff --git a/src/Util/ClassSourceManipulator.php b/src/Util/ClassSourceManipulator.php index 1ae8ed3c2..595031146 100644 --- a/src/Util/ClassSourceManipulator.php +++ b/src/Util/ClassSourceManipulator.php @@ -116,7 +116,6 @@ public function addEntityField(ClassPropertyModel $mapping): void // 3) If default value, then NOT nullable $nullable = $mapping->nullable ?? false; - $isId = (bool) ($mapping->id ?? false); $attributes[] = $this->buildAttributeNode(Column::class, $mapping->getAttributes(), 'ORM'); @@ -151,7 +150,7 @@ public function addEntityField(ClassPropertyModel $mapping): void ); // don't generate setters for id fields - if (!$isId) { + if (!($mapping->id ?? false)) { $this->addSetter($mapping->propertyName, $typeHint, $nullable); } } From 948d0db983be60b2a2bfbbca9f37ef51f7d6934a Mon Sep 17 00:00:00 2001 From: Jesse Rushlow Date: Tue, 6 Feb 2024 05:56:59 -0500 Subject: [PATCH 11/24] DisconnectedClassMetadataFactory is removed, set the reflection service manually --- src/Doctrine/DoctrineHelper.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Doctrine/DoctrineHelper.php b/src/Doctrine/DoctrineHelper.php index 07f76fdfb..53ae3cfef 100644 --- a/src/Doctrine/DoctrineHelper.php +++ b/src/Doctrine/DoctrineHelper.php @@ -17,13 +17,13 @@ use Doctrine\ORM\Mapping\Driver\AttributeDriver; use Doctrine\ORM\Mapping\MappingException as ORMMappingException; use Doctrine\ORM\Mapping\NamingStrategy; -use Doctrine\ORM\Tools\DisconnectedClassMetadataFactory; use Doctrine\Persistence\ManagerRegistry; use Doctrine\Persistence\Mapping\AbstractClassMetadataFactory; use Doctrine\Persistence\Mapping\ClassMetadata; use Doctrine\Persistence\Mapping\Driver\MappingDriver; use Doctrine\Persistence\Mapping\Driver\MappingDriverChain; use Doctrine\Persistence\Mapping\MappingException as PersistenceMappingException; +use Doctrine\Persistence\Mapping\StaticReflectionService; use Symfony\Bundle\MakerBundle\Util\ClassNameDetails; use Symfony\Component\Uid\Ulid; use Symfony\Component\Uid\Uuid; @@ -174,8 +174,8 @@ public function getMetadata(string $classOrNamespace = null, bool $disconnected $loaded = $this->isInstanceOf($cmf, AbstractClassMetadataFactory::class) ? $cmf->getLoadedMetadata() : []; } - $cmf = new DisconnectedClassMetadataFactory(); - $cmf->setEntityManager($em); + // Set the reflection service that was used in the now removed DisconnectedClassMetadataFactory::class + $cmf->setReflectionService(new StaticReflectionService()); foreach ($loaded as $m) { $cmf->setMetadataFor($m->getName(), $m); From bdeb0faafb00332801e9f8b597f56c866c2dd96a Mon Sep 17 00:00:00 2001 From: Jesse Rushlow Date: Tue, 6 Feb 2024 06:16:43 -0500 Subject: [PATCH 12/24] tmp - disable mercure tests --- .github/workflows/ci-linux.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci-linux.yaml b/.github/workflows/ci-linux.yaml index b771d6ee8..083c68956 100644 --- a/.github/workflows/ci-linux.yaml +++ b/.github/workflows/ci-linux.yaml @@ -11,6 +11,7 @@ on: env: PHPUNIT_FLAGS: "-v" SYMFONY_PHPUNIT_DIR: "$HOME/symfony-bridge/.phpunit" + MAKER_SKIP_MERCURE_TEST: 1 jobs: coding-standards: From e3279675c1cdbc61c3e11e2d4878bad6b218323f Mon Sep 17 00:00:00 2001 From: Jesse Rushlow Date: Tue, 6 Feb 2024 08:21:44 -0500 Subject: [PATCH 13/24] cleanup comments --- src/Doctrine/EntityRegenerator.php | 3 -- src/Maker/MakeEntity.php | 12 ------- src/Security/UserClassBuilder.php | 17 +--------- src/Util/CSM/ClassPropertyModel.php | 13 ++----- tests/Util/ClassSourceManipulatorTest.php | 41 ++--------------------- 5 files changed, 6 insertions(+), 80 deletions(-) diff --git a/src/Doctrine/EntityRegenerator.php b/src/Doctrine/EntityRegenerator.php index 639a20f14..a0e68510b 100644 --- a/src/Doctrine/EntityRegenerator.php +++ b/src/Doctrine/EntityRegenerator.php @@ -106,9 +106,6 @@ public function regenerateEntities(string $classOrNamespace): void continue; } -// $x = ClassPropertyModel::createFromArray($mapping); -// dump([$fieldName, $mapping, $x]); -// $manipulator->addEntityField(new ClassPropertyModel(propertyName: $fieldName, type: $mapping['type'] ?? $mapping->type)); $manipulator->addEntityField(ClassPropertyModel::createFromArray($mapping)); } diff --git a/src/Maker/MakeEntity.php b/src/Maker/MakeEntity.php index 867ee0a65..9ffc9dcfe 100644 --- a/src/Maker/MakeEntity.php +++ b/src/Maker/MakeEntity.php @@ -229,15 +229,9 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen $fileManagerOperations[$entityPath] = $manipulator; if ($newField instanceof ClassPropertyModel) { -// if (\is_array($newField)) { -// $annotationOptions = $newField; -// unset($annotationOptions['fieldName']); -// dump(['newField' => $newField]); -// $manipulator->addEntityField($newField['fieldName'], $annotationOptions); $manipulator->addEntityField($newField); $currentFields[] = $newField->propertyName; -// $currentFields[] = $newField['fieldName']; } elseif ($newField instanceof EntityRelation) { // both overridden below for OneToMany $newFieldName = $newField->getOwningProperty(); @@ -412,30 +406,24 @@ private function askForNextField(ConsoleStyle $io, array $fields, string $entity } // this is a normal field -// $data = ['fieldName' => $fieldName, 'type' => $type]; $classProperty = new ClassPropertyModel(propertyName: $fieldName, type: $type); if ('string' === $type) { // default to 255, avoid the question $classProperty->length = $io->ask('Field length', 255, [Validator::class, 'validateLength']); -// $data['length'] = $io->ask('Field length', 255, [Validator::class, 'validateLength']); } elseif ('decimal' === $type) { // 10 is the default value given in \Doctrine\DBAL\Schema\Column::$_precision -// $data['precision'] = $io->ask('Precision (total number of digits stored: 100.00 would be 5)', 10, [Validator::class, 'validatePrecision']); $classProperty->precision = $io->ask('Precision (total number of digits stored: 100.00 would be 5)', 10, [Validator::class, 'validatePrecision']); // 0 is the default value given in \Doctrine\DBAL\Schema\Column::$_scale -// $data['scale'] = $io->ask('Scale (number of decimals to store: 100.00 would be 2)', 0, [Validator::class, 'validateScale']); $classProperty->scale = $io->ask('Scale (number of decimals to store: 100.00 would be 2)', 0, [Validator::class, 'validateScale']); } if ($io->confirm('Can this field be null in the database (nullable)', false)) { $classProperty->nullable = true; -// $data['nullable'] = true; } return $classProperty; -// return $data; } private function printAvailableTypes(ConsoleStyle $io): void diff --git a/src/Security/UserClassBuilder.php b/src/Security/UserClassBuilder.php index 8f4e43475..9190ac3ba 100644 --- a/src/Security/UserClassBuilder.php +++ b/src/Security/UserClassBuilder.php @@ -59,13 +59,6 @@ private function addGetUsername(ClassSourceManipulator $manipulator, UserClassCo length: 180, unique: true, ) - -// [ -// 'type' => 'string', -// // https://github.com/FriendsOfSymfony/FOSUserBundle/issues/1919 -// 'length' => 180, -// 'unique' => true, -// ] ); } else { // add normal property @@ -107,10 +100,6 @@ private function addGetRoles(ClassSourceManipulator $manipulator, UserClassConfi // add entity property $manipulator->addEntityField( new ClassPropertyModel(propertyName: 'roles', type: 'json') -// 'roles', -// [ -// 'type' => 'json', -// ] ); } else { // add normal property @@ -210,12 +199,8 @@ private function addGetPassword(ClassSourceManipulator $manipulator, UserClassCo if ($userClassConfig->isEntity()) { // add entity property $manipulator->addEntityField( + // @TODO - Test for comments added to property new ClassPropertyModel(propertyName: 'password', type: 'string', comments: [$propertyDocs]) -// 'password', -// [ -// 'type' => 'string', -// ], -// [$propertyDocs] ); } else { // add normal property diff --git a/src/Util/CSM/ClassPropertyModel.php b/src/Util/CSM/ClassPropertyModel.php index 5e2d2c51a..7771d1456 100644 --- a/src/Util/CSM/ClassPropertyModel.php +++ b/src/Util/CSM/ClassPropertyModel.php @@ -11,6 +11,8 @@ namespace Symfony\Bundle\MakerBundle\Util\CSM; +use Doctrine\ORM\Mapping\FieldMapping; + class ClassPropertyModel { public function __construct( @@ -53,7 +55,7 @@ public function getAttributes(): array return $attributes; } - public static function createFromArray(\Doctrine\ORM\Mapping\FieldMapping|array $data): self + public static function createFromArray(FieldMapping|array $data): self { // @TODO - Better exception if (empty($data['fieldName']) || empty($data['type'])) { @@ -73,13 +75,4 @@ public static function createFromArray(\Doctrine\ORM\Mapping\FieldMapping|array unique: $data['unique'] ?? false, ); } - -// public static function createFromObject(\Doctrine\ORM\Mapping\FieldMapping $mapping): self -// { -//// return new self( -//// type: $mapping->type, -//// id: $mapping->id, -//// nullable: $mapping->nullable, -//// ); -// } } diff --git a/tests/Util/ClassSourceManipulatorTest.php b/tests/Util/ClassSourceManipulatorTest.php index 014d9b74d..8b4ad8901 100644 --- a/tests/Util/ClassSourceManipulatorTest.php +++ b/tests/Util/ClassSourceManipulatorTest.php @@ -214,15 +214,14 @@ public function getAttributeClassTests(): \Generator /** * @dataProvider getAddEntityFieldTests */ - public function testAddEntityField(string $sourceFilename, ClassPropertyModel $fieldOptions, $expectedSourceFilename): void + public function testAddEntityField(string $sourceFilename, ClassPropertyModel $propertyModel, $expectedSourceFilename): void { $sourcePath = __DIR__.'/fixtures/source'; $expectedPath = __DIR__.'/fixtures/add_entity_field'; $this->runAddEntityFieldTests( file_get_contents(sprintf('%s/%s', $sourcePath, $sourceFilename)), -// $propertyName, - $fieldOptions, + $propertyModel, file_get_contents(sprintf('%s/%s', $expectedPath, $expectedSourceFilename)) ); } @@ -239,79 +238,43 @@ public function getAddEntityFieldTests(): \Generator { yield 'entity_normal_add' => [ 'User_simple.php', -// 'fooProp', new ClassPropertyModel(propertyName: 'fooProp', type: 'string', length: 255, nullable: false, options: ['comment' => 'new field']), -// [ -// 'type' => 'string', -// 'length' => 255, -// 'nullable' => false, -// 'options' => ['comment' => 'new field'], -// ], 'User_simple.php', ]; yield 'entity_add_datetime' => [ 'User_simple.php', -// 'createdAt', new ClassPropertyModel(propertyName: 'createdAt', type: 'datetime', nullable: true), -// [ -// 'type' => 'datetime', -// 'nullable' => true, -// ], 'User_simple_datetime.php', ]; yield 'entity_field_property_already_exists' => [ 'User_some_props.php', -// 'firstName', new ClassPropertyModel(propertyName: 'firstName', type: 'string', length: 255, nullable: false), -// [ -// 'type' => 'string', -// 'length' => 255, -// 'nullable' => false, -// ], 'User_simple_prop_already_exists.php', ]; yield 'entity_field_property_zero' => [ 'User_simple.php', -// 'decimal', new ClassPropertyModel(propertyName: 'decimal', type: 'decimal', precision: 6, scale: 0), -// [ -// 'type' => 'decimal', -// 'precision' => 6, -// 'scale' => 0, -// ], 'User_simple_prop_zero.php', ]; yield 'entity_add_object' => [ 'User_simple.php', -// 'someObject', new ClassPropertyModel(propertyName: 'someObject', type: 'object'), -// [ -// 'type' => 'object', -// ], 'User_simple_object.php', ]; yield 'entity_add_uuid' => [ 'User_simple.php', -// 'uuid', new ClassPropertyModel(propertyName: 'uuid', type: 'uuid'), -// [ -// 'type' => 'uuid', -// ], 'User_simple_uuid.php', ]; yield 'entity_add_ulid' => [ 'User_simple.php', -// 'ulid', new ClassPropertyModel(propertyName: 'ulid', type: 'ulid'), -// [ -// 'type' => 'ulid', -// ], 'User_simple_ulid.php', ]; } From 2e9563ad74a43bce50c82397e7d3ef1282a91bda Mon Sep 17 00:00:00 2001 From: Jesse Rushlow Date: Tue, 6 Feb 2024 08:52:50 -0500 Subject: [PATCH 14/24] use creator method --- src/Doctrine/EntityRegenerator.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Doctrine/EntityRegenerator.php b/src/Doctrine/EntityRegenerator.php index a0e68510b..0ec29948d 100644 --- a/src/Doctrine/EntityRegenerator.php +++ b/src/Doctrine/EntityRegenerator.php @@ -94,10 +94,10 @@ public function regenerateEntities(string $classOrNamespace): void if (str_contains($fieldName, '.')) { [$fieldName, $embeddedFiledName] = explode('.', $fieldName); - $operations[$embeddedClasses[$fieldName]]->addEntityField(new ClassPropertyModel( - propertyName: $embeddedFiledName, - type: $mapping['type'] ?? $mapping->type - )); + $property = ClassPropertyModel::createFromArray($mapping); + $property->propertyName = $embeddedFiledName; + + $operations[$embeddedClasses[$fieldName]]->addEntityField($property); continue; } From e6db929376e0a5ad8abccb040619fd08eed8f1c6 Mon Sep 17 00:00:00 2001 From: Jesse Rushlow Date: Tue, 6 Feb 2024 08:58:39 -0500 Subject: [PATCH 15/24] what is still using an array --- src/Doctrine/EntityRegenerator.php | 4 ++-- src/Util/CSM/ClassPropertyModel.php | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Doctrine/EntityRegenerator.php b/src/Doctrine/EntityRegenerator.php index 0ec29948d..e3512f246 100644 --- a/src/Doctrine/EntityRegenerator.php +++ b/src/Doctrine/EntityRegenerator.php @@ -94,7 +94,7 @@ public function regenerateEntities(string $classOrNamespace): void if (str_contains($fieldName, '.')) { [$fieldName, $embeddedFiledName] = explode('.', $fieldName); - $property = ClassPropertyModel::createFromArray($mapping); + $property = ClassPropertyModel::createFromObject($mapping); $property->propertyName = $embeddedFiledName; $operations[$embeddedClasses[$fieldName]]->addEntityField($property); @@ -106,7 +106,7 @@ public function regenerateEntities(string $classOrNamespace): void continue; } - $manipulator->addEntityField(ClassPropertyModel::createFromArray($mapping)); + $manipulator->addEntityField(ClassPropertyModel::createFromObject($mapping)); } // @TODO _ @legacy - Type needed for mapping diff --git a/src/Util/CSM/ClassPropertyModel.php b/src/Util/CSM/ClassPropertyModel.php index 7771d1456..420699661 100644 --- a/src/Util/CSM/ClassPropertyModel.php +++ b/src/Util/CSM/ClassPropertyModel.php @@ -12,6 +12,7 @@ namespace Symfony\Bundle\MakerBundle\Util\CSM; use Doctrine\ORM\Mapping\FieldMapping; +use Symfony\Bundle\MakerBundle\Exception\RuntimeCommandException; class ClassPropertyModel { @@ -55,11 +56,15 @@ public function getAttributes(): array return $attributes; } - public static function createFromArray(FieldMapping|array $data): self + public static function createFromObject(FieldMapping|array $data): self { + if (is_array($data)) { + @trigger_deprecation('symfony/maker-bundle', 'v99999', 'Found some data as an array....'); + } + // @TODO - Better exception if (empty($data['fieldName']) || empty($data['type'])) { - throw new \RuntimeException('Needs name and type'); + throw new RuntimeCommandException('Cannot create property model - "fieldName" & "type" are required.'); } return new self( From 030e68976b5d5bf2d4441a75651290b10733756b Mon Sep 17 00:00:00 2001 From: Jesse Rushlow Date: Tue, 6 Feb 2024 09:16:05 -0500 Subject: [PATCH 16/24] dump depends --- composer.json | 6 +++--- src/Util/CSM/ClassPropertyModel.php | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/composer.json b/composer.json index 2c7db2232..00f629c37 100644 --- a/composer.json +++ b/composer.json @@ -29,7 +29,7 @@ "require-dev": { "composer/semver": "^3.0", "doctrine/doctrine-bundle": "^2.5.0", - "doctrine/orm": "^2.10.0", + "doctrine/orm": "^2.15|^3", "symfony/http-client": "^6.4|^7.0", "symfony/phpunit-bridge": "^6.4.1|^7.0", "symfony/security-core": "^6.4|^7.0", @@ -41,8 +41,8 @@ "sort-packages": true }, "conflict": { - "doctrine/orm": "<2.10", - "doctrine/doctrine-bundle": "<2.4" + "doctrine/orm": "<2.15", + "doctrine/doctrine-bundle": "<2.10" }, "autoload": { "psr-4": { "Symfony\\Bundle\\MakerBundle\\": "src/" } diff --git a/src/Util/CSM/ClassPropertyModel.php b/src/Util/CSM/ClassPropertyModel.php index 420699661..e818ba462 100644 --- a/src/Util/CSM/ClassPropertyModel.php +++ b/src/Util/CSM/ClassPropertyModel.php @@ -58,9 +58,10 @@ public function getAttributes(): array public static function createFromObject(FieldMapping|array $data): self { - if (is_array($data)) { - @trigger_deprecation('symfony/maker-bundle', 'v99999', 'Found some data as an array....'); - } + // @TODO _ @legacy - REMOVE DEPRECATION +// if (is_array($data)) { +// @trigger_deprecation('symfony/maker-bundle', 'v99999', 'Found some data as an array....'); +// } // @TODO - Better exception if (empty($data['fieldName']) || empty($data['type'])) { From c8644116bb0050d34f386c3916f624f6db1b4dc8 Mon Sep 17 00:00:00 2001 From: Jesse Rushlow Date: Tue, 6 Feb 2024 09:29:31 -0500 Subject: [PATCH 17/24] use FieldMapping properties --- src/Util/CSM/ClassPropertyModel.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/Util/CSM/ClassPropertyModel.php b/src/Util/CSM/ClassPropertyModel.php index e818ba462..6a12f62d9 100644 --- a/src/Util/CSM/ClassPropertyModel.php +++ b/src/Util/CSM/ClassPropertyModel.php @@ -68,6 +68,20 @@ public static function createFromObject(FieldMapping|array $data): self throw new RuntimeCommandException('Cannot create property model - "fieldName" & "type" are required.'); } + if ($data instanceof FieldMapping) { + return new self( + propertyName: $data->fieldName, + type: $data->type, + length: $data->length, + id: $data->id ?? false, + nullable: $data->nullable ?? false, + options: $data->options ?? [], + precision: $data->precision, + scale: $data->scale, + unique: $data->unique ?? false, + ); + } + return new self( propertyName: $data['fieldName'], type: $data['type'], From 46f5f78aebbe482fb4e5b0d06b0aed06b2d45651 Mon Sep 17 00:00:00 2001 From: Jesse Rushlow Date: Tue, 6 Feb 2024 10:21:14 -0500 Subject: [PATCH 18/24] handle relation mapping objects --- src/Doctrine/EntityRegenerator.php | 69 +++++++++++++++++------------ src/Doctrine/RelationManyToMany.php | 23 ++++++++++ src/Doctrine/RelationManyToOne.php | 13 ++++++ src/Doctrine/RelationOneToMany.php | 11 +++++ src/Doctrine/RelationOneToOne.php | 27 +++++++++++ 5 files changed, 114 insertions(+), 29 deletions(-) diff --git a/src/Doctrine/EntityRegenerator.php b/src/Doctrine/EntityRegenerator.php index e3512f246..d832862e1 100644 --- a/src/Doctrine/EntityRegenerator.php +++ b/src/Doctrine/EntityRegenerator.php @@ -126,50 +126,61 @@ public function regenerateEntities(string $classOrNamespace): void switch ($mapping['type']) { case ClassMetadata::MANY_TO_ONE: - $relation = (new RelationManyToOne( - propertyName: $mapping['fieldName'], - targetClassName: $mapping['targetEntity'], - targetPropertyName: $mapping['inversedBy'], - mapInverseRelation: null !== $mapping['inversedBy'], - isOwning: true, - isNullable: $getIsNullable($mapping), - )); + // $relation = (new RelationManyToOne( + // propertyName: $mapping['fieldName'], + // targetClassName: $mapping['targetEntity'], + // targetPropertyName: $mapping['inversedBy'], + // mapInverseRelation: null !== $mapping['inversedBy'], + // isOwning: true, + // isNullable: $getIsNullable($mapping), + // )); + + // @TODO - handle isNullable + $relation = RelationManyToOne::createFromObject($mapping); $manipulator->addManyToOneRelation($relation); break; case ClassMetadata::ONE_TO_MANY: - $relation = (new RelationOneToMany( - propertyName: $mapping['fieldName'], - targetClassName: $mapping['targetEntity'], - targetPropertyName: $mapping['mappedBy'], - orphanRemoval: $mapping['orphanRemoval'], - )); + // $relation = (new RelationOneToMany( + // propertyName: $mapping['fieldName'], + // targetClassName: $mapping['targetEntity'], + // targetPropertyName: $mapping['mappedBy'], + // orphanRemoval: $mapping['orphanRemoval'], + // )); + + $relation = RelationOneToMany::createFromObject($mapping); $manipulator->addOneToManyRelation($relation); break; case ClassMetadata::MANY_TO_MANY: - $relation = (new RelationManyToMany( - propertyName: $mapping['fieldName'], - targetClassName: $mapping['targetEntity'], - targetPropertyName: $mapping['mappedBy'], - mapInverseRelation: $mapping['isOwningSide'] ? (null !== $mapping['inversedBy']) : true, - isOwning: $mapping['isOwningSide'], - )); + // @TODO _ @legacy mappedBy Doesnt exist on object + // $relation = (new RelationManyToMany( + // propertyName: $mapping['fieldName'], + // targetClassName: $mapping['targetEntity'], + // targetPropertyName: $mapping['mappedBy'], + // mapInverseRelation: $mapping['isOwningSide'] ? (null !== $mapping['inversedBy']) : true, + // isOwning: $mapping['isOwningSide'], + // )); + + $relation = RelationManyToMany::createFromObject($mapping); $manipulator->addManyToManyRelation($relation); break; case ClassMetadata::ONE_TO_ONE: - $relation = (new RelationOneToOne( - propertyName: $mapping['fieldName'], - targetClassName: $mapping['targetEntity'], - targetPropertyName: $mapping['isOwningSide'] ? $mapping['inversedBy'] : $mapping['mappedBy'], - mapInverseRelation: $mapping['isOwningSide'] ? (null !== $mapping['inversedBy']) : true, - isOwning: $mapping['isOwningSide'], - isNullable: $getIsNullable($mapping), - )); + // @TODO _ @legacy - mappedBy and isNullable + // $relation = (new RelationOneToOne( + // propertyName: $mapping['fieldName'], + // targetClassName: $mapping['targetEntity'], + // targetPropertyName: $mapping['isOwningSide'] ? $mapping['inversedBy'] : $mapping['mappedBy'], + // mapInverseRelation: $mapping['isOwningSide'] ? (null !== $mapping['inversedBy']) : true, + // isOwning: $mapping['isOwningSide'], + // isNullable: $getIsNullable($mapping), + // )); + + $relation = RelationOneToOne::createFromObject($mapping); $manipulator->addOneToOneRelation($relation); diff --git a/src/Doctrine/RelationManyToMany.php b/src/Doctrine/RelationManyToMany.php index eaf0599d1..9a69645b1 100644 --- a/src/Doctrine/RelationManyToMany.php +++ b/src/Doctrine/RelationManyToMany.php @@ -11,6 +11,8 @@ namespace Symfony\Bundle\MakerBundle\Doctrine; +use Doctrine\ORM\Mapping\ManyToManyInverseSideMapping; +use Doctrine\ORM\Mapping\ManyToManyOwningSideMapping; use Symfony\Bundle\MakerBundle\Str; /** @@ -27,4 +29,25 @@ public function getTargetRemoverMethodName(): string { return 'remove'.Str::asCamelCase(Str::pluralCamelCaseToSingular($this->getTargetPropertyName())); } + + public static function createFromObject(ManyToManyInverseSideMapping|ManyToManyOwningSideMapping $mapping): self + { + if ($mapping instanceof ManyToManyOwningSideMapping) { + return new self( + propertyName: $mapping->fieldName, + targetClassName: $mapping->targetEntity, + targetPropertyName: $mapping->inversedBy, // @TODO _ @legacy Is this correct? + mapInverseRelation: (null !== $mapping->inversedBy), + isOwning: $mapping->isOwningSide(), + ); + } + + return new self( + propertyName: $mapping->fieldName, + targetClassName: $mapping->targetEntity, + targetPropertyName: $mapping->mappedBy, // @TODO _ @legacy mappedBy Doesnt exist on object + mapInverseRelation: true, + isOwning: $mapping->isOwningSide(), + ); + } } diff --git a/src/Doctrine/RelationManyToOne.php b/src/Doctrine/RelationManyToOne.php index 97c26c619..16ec8cc8f 100644 --- a/src/Doctrine/RelationManyToOne.php +++ b/src/Doctrine/RelationManyToOne.php @@ -11,9 +11,22 @@ namespace Symfony\Bundle\MakerBundle\Doctrine; +use Doctrine\ORM\Mapping\ManyToOneAssociationMapping; + /** * @internal */ final class RelationManyToOne extends BaseRelation { + public static function createFromObject(ManyToOneAssociationMapping $data): self + { + return new self( + propertyName: $data->fieldName, + targetClassName: $data->targetEntity, + targetPropertyName: $data->inversedBy, + mapInverseRelation: null !== $data->inversedBy, + isOwning: true, + // isNullable: + ); + } } diff --git a/src/Doctrine/RelationOneToMany.php b/src/Doctrine/RelationOneToMany.php index 1fa43dd30..1923258ff 100644 --- a/src/Doctrine/RelationOneToMany.php +++ b/src/Doctrine/RelationOneToMany.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\MakerBundle\Doctrine; +use Doctrine\ORM\Mapping\OneToManyAssociationMapping; use Symfony\Bundle\MakerBundle\Str; /** @@ -32,4 +33,14 @@ public function isMapInverseRelation(): bool { throw new \Exception('OneToMany IS the inverse side!'); } + + public static function createFromObject(OneToManyAssociationMapping $data): self + { + return new self( + propertyName: $data->fieldName, + targetClassName: $data->targetEntity, + targetPropertyName: $data->mappedBy, + orphanRemoval: $data->orphanRemoval, + ); + } } diff --git a/src/Doctrine/RelationOneToOne.php b/src/Doctrine/RelationOneToOne.php index 8b970a9bd..5e852f8a2 100644 --- a/src/Doctrine/RelationOneToOne.php +++ b/src/Doctrine/RelationOneToOne.php @@ -11,6 +11,8 @@ namespace Symfony\Bundle\MakerBundle\Doctrine; +use Doctrine\ORM\Mapping\OneToOneInverseSideMapping; +use Doctrine\ORM\Mapping\OneToOneOwningSideMapping; use Symfony\Bundle\MakerBundle\Str; /** @@ -27,4 +29,29 @@ public function getTargetSetterMethodName(): string { return 'set'.Str::asCamelCase($this->getTargetPropertyName()); } + + public static function createFromObject(OneToOneInverseSideMapping|OneToOneOwningSideMapping $mapping): self + { + // @TODO - Handle arrays + + if ($mapping instanceof OneToOneOwningSideMapping) { + return new self( + propertyName: $mapping->fieldName, + targetClassName: $mapping->targetEntity, + targetPropertyName: $mapping->inversedBy, + mapInverseRelation: (null !== $mapping->inversedBy), + isOwning: true, + // isNullable: // @TODO Handle Nullable + ); + } + + return new self( + propertyName: $mapping->fieldName, + targetClassName: $mapping->targetEntity, + targetPropertyName: $mapping->mappedBy, + mapInverseRelation: true, + isOwning: false, + // isNullable: // @TODO Handle Nullable + ); + } } From 76b52eb688176a29c46da84e3e71ab60caaf3f62 Mon Sep 17 00:00:00 2001 From: Jesse Rushlow Date: Tue, 6 Feb 2024 10:21:47 -0500 Subject: [PATCH 19/24] naming conventions --- src/Doctrine/EntityRegenerator.php | 6 +++--- src/Maker/MakeEntity.php | 8 ++++---- src/Security/UserClassBuilder.php | 8 ++++---- .../Model/ClassProperty.php} | 9 +++++++-- src/Util/ClassSourceManipulator.php | 4 ++-- tests/Util/ClassSourceManipulatorTest.php | 20 +++++++++---------- 6 files changed, 30 insertions(+), 25 deletions(-) rename src/Util/{CSM/ClassPropertyModel.php => ClassSource/Model/ClassProperty.php} (94%) diff --git a/src/Doctrine/EntityRegenerator.php b/src/Doctrine/EntityRegenerator.php index d832862e1..4855872f9 100644 --- a/src/Doctrine/EntityRegenerator.php +++ b/src/Doctrine/EntityRegenerator.php @@ -17,8 +17,8 @@ use Symfony\Bundle\MakerBundle\Exception\RuntimeCommandException; use Symfony\Bundle\MakerBundle\FileManager; use Symfony\Bundle\MakerBundle\Generator; +use Symfony\Bundle\MakerBundle\Util\ClassSource\Model\ClassProperty; use Symfony\Bundle\MakerBundle\Util\ClassSourceManipulator; -use Symfony\Bundle\MakerBundle\Util\CSM\ClassPropertyModel; /** * @internal @@ -94,7 +94,7 @@ public function regenerateEntities(string $classOrNamespace): void if (str_contains($fieldName, '.')) { [$fieldName, $embeddedFiledName] = explode('.', $fieldName); - $property = ClassPropertyModel::createFromObject($mapping); + $property = ClassProperty::createFromObject($mapping); $property->propertyName = $embeddedFiledName; $operations[$embeddedClasses[$fieldName]]->addEntityField($property); @@ -106,7 +106,7 @@ public function regenerateEntities(string $classOrNamespace): void continue; } - $manipulator->addEntityField(ClassPropertyModel::createFromObject($mapping)); + $manipulator->addEntityField(ClassProperty::createFromObject($mapping)); } // @TODO _ @legacy - Type needed for mapping diff --git a/src/Maker/MakeEntity.php b/src/Maker/MakeEntity.php index 9ffc9dcfe..cc3818185 100644 --- a/src/Maker/MakeEntity.php +++ b/src/Maker/MakeEntity.php @@ -28,7 +28,7 @@ use Symfony\Bundle\MakerBundle\Util\ClassDetails; use Symfony\Bundle\MakerBundle\Util\ClassSourceManipulator; use Symfony\Bundle\MakerBundle\Util\CliOutputHelper; -use Symfony\Bundle\MakerBundle\Util\CSM\ClassPropertyModel; +use Symfony\Bundle\MakerBundle\Util\ClassSource\Model\ClassProperty; use Symfony\Bundle\MakerBundle\Util\PhpCompatUtil; use Symfony\Bundle\MakerBundle\Validator; use Symfony\Component\Console\Command\Command; @@ -228,7 +228,7 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen $fileManagerOperations = []; $fileManagerOperations[$entityPath] = $manipulator; - if ($newField instanceof ClassPropertyModel) { + if ($newField instanceof ClassProperty) { $manipulator->addEntityField($newField); $currentFields[] = $newField->propertyName; @@ -328,7 +328,7 @@ public function configureDependencies(DependencyBuilder $dependencies, InputInte ORMDependencyBuilder::buildDependencies($dependencies); } - private function askForNextField(ConsoleStyle $io, array $fields, string $entityClass, bool $isFirstField): EntityRelation|ClassPropertyModel|null + private function askForNextField(ConsoleStyle $io, array $fields, string $entityClass, bool $isFirstField): EntityRelation|ClassProperty|null { $io->writeln(''); @@ -406,7 +406,7 @@ private function askForNextField(ConsoleStyle $io, array $fields, string $entity } // this is a normal field - $classProperty = new ClassPropertyModel(propertyName: $fieldName, type: $type); + $classProperty = new ClassProperty(propertyName: $fieldName, type: $type); if ('string' === $type) { // default to 255, avoid the question diff --git a/src/Security/UserClassBuilder.php b/src/Security/UserClassBuilder.php index 9190ac3ba..00bf7e577 100644 --- a/src/Security/UserClassBuilder.php +++ b/src/Security/UserClassBuilder.php @@ -13,7 +13,7 @@ use PhpParser\Node; use Symfony\Bundle\MakerBundle\Util\ClassSourceManipulator; -use Symfony\Bundle\MakerBundle\Util\CSM\ClassPropertyModel; +use Symfony\Bundle\MakerBundle\Util\ClassSource\Model\ClassProperty; use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; use Symfony\Component\Security\Core\User\UserInterface; @@ -53,7 +53,7 @@ private function addGetUsername(ClassSourceManipulator $manipulator, UserClassCo if ($userClassConfig->isEntity()) { // add entity property $manipulator->addEntityField( - new ClassPropertyModel( + new ClassProperty( propertyName: $userClassConfig->getIdentityPropertyName(), type: 'string', length: 180, @@ -99,7 +99,7 @@ private function addGetRoles(ClassSourceManipulator $manipulator, UserClassConfi if ($userClassConfig->isEntity()) { // add entity property $manipulator->addEntityField( - new ClassPropertyModel(propertyName: 'roles', type: 'json') + new ClassProperty(propertyName: 'roles', type: 'json') ); } else { // add normal property @@ -200,7 +200,7 @@ private function addGetPassword(ClassSourceManipulator $manipulator, UserClassCo // add entity property $manipulator->addEntityField( // @TODO - Test for comments added to property - new ClassPropertyModel(propertyName: 'password', type: 'string', comments: [$propertyDocs]) + new ClassProperty(propertyName: 'password', type: 'string', comments: [$propertyDocs]) ); } else { // add normal property diff --git a/src/Util/CSM/ClassPropertyModel.php b/src/Util/ClassSource/Model/ClassProperty.php similarity index 94% rename from src/Util/CSM/ClassPropertyModel.php rename to src/Util/ClassSource/Model/ClassProperty.php index 6a12f62d9..4fc0d662b 100644 --- a/src/Util/CSM/ClassPropertyModel.php +++ b/src/Util/ClassSource/Model/ClassProperty.php @@ -9,12 +9,17 @@ * file that was distributed with this source code. */ -namespace Symfony\Bundle\MakerBundle\Util\CSM; +namespace Symfony\Bundle\MakerBundle\Util\ClassSource\Model; use Doctrine\ORM\Mapping\FieldMapping; use Symfony\Bundle\MakerBundle\Exception\RuntimeCommandException; -class ClassPropertyModel +/** + * @author Jesse Rushlow + * + * @internal + */ +final class ClassProperty { public function __construct( public string $propertyName, diff --git a/src/Util/ClassSourceManipulator.php b/src/Util/ClassSourceManipulator.php index 595031146..9d429624d 100644 --- a/src/Util/ClassSourceManipulator.php +++ b/src/Util/ClassSourceManipulator.php @@ -38,7 +38,7 @@ use Symfony\Bundle\MakerBundle\Doctrine\RelationOneToMany; use Symfony\Bundle\MakerBundle\Doctrine\RelationOneToOne; use Symfony\Bundle\MakerBundle\Str; -use Symfony\Bundle\MakerBundle\Util\CSM\ClassPropertyModel; +use Symfony\Bundle\MakerBundle\Util\ClassSource\Model\ClassProperty; /** * @internal @@ -97,7 +97,7 @@ public function getSourceCode(): string return $this->sourceCode; } - public function addEntityField(ClassPropertyModel $mapping): void + public function addEntityField(ClassProperty $mapping): void { $typeHint = DoctrineHelper::getPropertyTypeForColumn($mapping->type); if ($typeHint && DoctrineHelper::canColumnTypeBeInferredByPropertyType($mapping->type, $typeHint)) { diff --git a/tests/Util/ClassSourceManipulatorTest.php b/tests/Util/ClassSourceManipulatorTest.php index 8b4ad8901..1cca8b096 100644 --- a/tests/Util/ClassSourceManipulatorTest.php +++ b/tests/Util/ClassSourceManipulatorTest.php @@ -20,7 +20,7 @@ use Symfony\Bundle\MakerBundle\Doctrine\RelationOneToMany; use Symfony\Bundle\MakerBundle\Doctrine\RelationOneToOne; use Symfony\Bundle\MakerBundle\Util\ClassSourceManipulator; -use Symfony\Bundle\MakerBundle\Util\CSM\ClassPropertyModel; +use Symfony\Bundle\MakerBundle\Util\ClassSource\Model\ClassProperty; use Symfony\Component\Security\Core\User\UserInterface; class ClassSourceManipulatorTest extends TestCase @@ -214,7 +214,7 @@ public function getAttributeClassTests(): \Generator /** * @dataProvider getAddEntityFieldTests */ - public function testAddEntityField(string $sourceFilename, ClassPropertyModel $propertyModel, $expectedSourceFilename): void + public function testAddEntityField(string $sourceFilename, ClassProperty $propertyModel, $expectedSourceFilename): void { $sourcePath = __DIR__.'/fixtures/source'; $expectedPath = __DIR__.'/fixtures/add_entity_field'; @@ -226,7 +226,7 @@ public function testAddEntityField(string $sourceFilename, ClassPropertyModel $p ); } - private function runAddEntityFieldTests(string $source, ClassPropertyModel $fieldOptions, string $expected): void + private function runAddEntityFieldTests(string $source, ClassProperty $fieldOptions, string $expected): void { $manipulator = new ClassSourceManipulator($source, false); $manipulator->addEntityField($fieldOptions); @@ -238,43 +238,43 @@ public function getAddEntityFieldTests(): \Generator { yield 'entity_normal_add' => [ 'User_simple.php', - new ClassPropertyModel(propertyName: 'fooProp', type: 'string', length: 255, nullable: false, options: ['comment' => 'new field']), + new ClassProperty(propertyName: 'fooProp', type: 'string', length: 255, nullable: false, options: ['comment' => 'new field']), 'User_simple.php', ]; yield 'entity_add_datetime' => [ 'User_simple.php', - new ClassPropertyModel(propertyName: 'createdAt', type: 'datetime', nullable: true), + new ClassProperty(propertyName: 'createdAt', type: 'datetime', nullable: true), 'User_simple_datetime.php', ]; yield 'entity_field_property_already_exists' => [ 'User_some_props.php', - new ClassPropertyModel(propertyName: 'firstName', type: 'string', length: 255, nullable: false), + new ClassProperty(propertyName: 'firstName', type: 'string', length: 255, nullable: false), 'User_simple_prop_already_exists.php', ]; yield 'entity_field_property_zero' => [ 'User_simple.php', - new ClassPropertyModel(propertyName: 'decimal', type: 'decimal', precision: 6, scale: 0), + new ClassProperty(propertyName: 'decimal', type: 'decimal', precision: 6, scale: 0), 'User_simple_prop_zero.php', ]; yield 'entity_add_object' => [ 'User_simple.php', - new ClassPropertyModel(propertyName: 'someObject', type: 'object'), + new ClassProperty(propertyName: 'someObject', type: 'object'), 'User_simple_object.php', ]; yield 'entity_add_uuid' => [ 'User_simple.php', - new ClassPropertyModel(propertyName: 'uuid', type: 'uuid'), + new ClassProperty(propertyName: 'uuid', type: 'uuid'), 'User_simple_uuid.php', ]; yield 'entity_add_ulid' => [ 'User_simple.php', - new ClassPropertyModel(propertyName: 'ulid', type: 'ulid'), + new ClassProperty(propertyName: 'ulid', type: 'ulid'), 'User_simple_ulid.php', ]; } From 8e85cb6197a7d9c77c4640d92773133494c3a835 Mon Sep 17 00:00:00 2001 From: Jesse Rushlow Date: Tue, 6 Feb 2024 10:52:12 -0500 Subject: [PATCH 20/24] cleanup relation mapping --- src/Doctrine/EntityRegenerator.php | 62 ++--------------------------- src/Doctrine/RelationManyToMany.php | 15 ++++++- src/Doctrine/RelationManyToOne.php | 24 ++++++++--- src/Doctrine/RelationOneToMany.php | 20 +++++++--- src/Doctrine/RelationOneToOne.php | 18 +++++++-- 5 files changed, 64 insertions(+), 75 deletions(-) diff --git a/src/Doctrine/EntityRegenerator.php b/src/Doctrine/EntityRegenerator.php index 4855872f9..f60998bf9 100644 --- a/src/Doctrine/EntityRegenerator.php +++ b/src/Doctrine/EntityRegenerator.php @@ -109,16 +109,6 @@ public function regenerateEntities(string $classOrNamespace): void $manipulator->addEntityField(ClassProperty::createFromObject($mapping)); } - // @TODO _ @legacy - Type needed for mapping - $getIsNullable = static function ($mapping) { - if (!isset($mapping['joinColumns'][0]['nullable'])) { - // the default for relationships IS nullable - return true; - } - - return $mapping['joinColumns'][0]['nullable']; - }; - foreach ($classMetadata->associationMappings as $fieldName => $mapping) { if (!\in_array($fieldName, $mappedFields)) { continue; @@ -126,63 +116,19 @@ public function regenerateEntities(string $classOrNamespace): void switch ($mapping['type']) { case ClassMetadata::MANY_TO_ONE: - // $relation = (new RelationManyToOne( - // propertyName: $mapping['fieldName'], - // targetClassName: $mapping['targetEntity'], - // targetPropertyName: $mapping['inversedBy'], - // mapInverseRelation: null !== $mapping['inversedBy'], - // isOwning: true, - // isNullable: $getIsNullable($mapping), - // )); - - // @TODO - handle isNullable - $relation = RelationManyToOne::createFromObject($mapping); - - $manipulator->addManyToOneRelation($relation); + $manipulator->addManyToOneRelation(RelationManyToOne::createFromObject($mapping)); break; case ClassMetadata::ONE_TO_MANY: - // $relation = (new RelationOneToMany( - // propertyName: $mapping['fieldName'], - // targetClassName: $mapping['targetEntity'], - // targetPropertyName: $mapping['mappedBy'], - // orphanRemoval: $mapping['orphanRemoval'], - // )); - - $relation = RelationOneToMany::createFromObject($mapping); - - $manipulator->addOneToManyRelation($relation); + $manipulator->addOneToManyRelation(RelationOneToMany::createFromObject($mapping)); break; case ClassMetadata::MANY_TO_MANY: - // @TODO _ @legacy mappedBy Doesnt exist on object - // $relation = (new RelationManyToMany( - // propertyName: $mapping['fieldName'], - // targetClassName: $mapping['targetEntity'], - // targetPropertyName: $mapping['mappedBy'], - // mapInverseRelation: $mapping['isOwningSide'] ? (null !== $mapping['inversedBy']) : true, - // isOwning: $mapping['isOwningSide'], - // )); - - $relation = RelationManyToMany::createFromObject($mapping); - - $manipulator->addManyToManyRelation($relation); + $manipulator->addManyToManyRelation(RelationManyToMany::createFromObject($mapping)); break; case ClassMetadata::ONE_TO_ONE: - // @TODO _ @legacy - mappedBy and isNullable - // $relation = (new RelationOneToOne( - // propertyName: $mapping['fieldName'], - // targetClassName: $mapping['targetEntity'], - // targetPropertyName: $mapping['isOwningSide'] ? $mapping['inversedBy'] : $mapping['mappedBy'], - // mapInverseRelation: $mapping['isOwningSide'] ? (null !== $mapping['inversedBy']) : true, - // isOwning: $mapping['isOwningSide'], - // isNullable: $getIsNullable($mapping), - // )); - - $relation = RelationOneToOne::createFromObject($mapping); - - $manipulator->addOneToOneRelation($relation); + $manipulator->addOneToOneRelation(RelationOneToOne::createFromObject($mapping)); break; default: diff --git a/src/Doctrine/RelationManyToMany.php b/src/Doctrine/RelationManyToMany.php index 9a69645b1..a57bd2ceb 100644 --- a/src/Doctrine/RelationManyToMany.php +++ b/src/Doctrine/RelationManyToMany.php @@ -30,8 +30,19 @@ public function getTargetRemoverMethodName(): string return 'remove'.Str::asCamelCase(Str::pluralCamelCaseToSingular($this->getTargetPropertyName())); } - public static function createFromObject(ManyToManyInverseSideMapping|ManyToManyOwningSideMapping $mapping): self + public static function createFromObject(ManyToManyInverseSideMapping|ManyToManyOwningSideMapping|array $mapping): self { + /* @legacy Remove conditional when ORM x is no longer supported! */ + if (\is_array($mapping)) { + return new self( + propertyName: $mapping['fieldName'], + targetClassName: $mapping['targetEntity'], + targetPropertyName: $mapping['mappedBy'], + mapInverseRelation: $mapping['isOwningSide'] ? (null !== $mapping['inversedBy']) : true, + isOwning: $mapping['isOwningSide'], + ); + } + if ($mapping instanceof ManyToManyOwningSideMapping) { return new self( propertyName: $mapping->fieldName, @@ -45,7 +56,7 @@ public static function createFromObject(ManyToManyInverseSideMapping|ManyToManyO return new self( propertyName: $mapping->fieldName, targetClassName: $mapping->targetEntity, - targetPropertyName: $mapping->mappedBy, // @TODO _ @legacy mappedBy Doesnt exist on object + targetPropertyName: $mapping->mappedBy, mapInverseRelation: true, isOwning: $mapping->isOwningSide(), ); diff --git a/src/Doctrine/RelationManyToOne.php b/src/Doctrine/RelationManyToOne.php index 16ec8cc8f..ed3d02540 100644 --- a/src/Doctrine/RelationManyToOne.php +++ b/src/Doctrine/RelationManyToOne.php @@ -18,15 +18,27 @@ */ final class RelationManyToOne extends BaseRelation { - public static function createFromObject(ManyToOneAssociationMapping $data): self + public static function createFromObject(ManyToOneAssociationMapping|array $mapping): self { + /* @legacy Remove conditional when ORM x is no longer supported! */ + if (\is_array($mapping)) { + return new self( + propertyName: $mapping['fieldName'], + targetClassName: $mapping['targetEntity'], + targetPropertyName: $mapping['inversedBy'], + mapInverseRelation: null !== $mapping['inversedBy'], + isOwning: true, + isNullable: $mapping['joinColumns'][0]['nullable'] ?? true, + ); + } + return new self( - propertyName: $data->fieldName, - targetClassName: $data->targetEntity, - targetPropertyName: $data->inversedBy, - mapInverseRelation: null !== $data->inversedBy, + propertyName: $mapping->fieldName, + targetClassName: $mapping->targetEntity, + targetPropertyName: $mapping->inversedBy, + mapInverseRelation: null !== $mapping->inversedBy, isOwning: true, - // isNullable: + isNullable: $mapping->joinColumns[0]->nullable ?? true, ); } } diff --git a/src/Doctrine/RelationOneToMany.php b/src/Doctrine/RelationOneToMany.php index 1923258ff..8060cbaff 100644 --- a/src/Doctrine/RelationOneToMany.php +++ b/src/Doctrine/RelationOneToMany.php @@ -34,13 +34,23 @@ public function isMapInverseRelation(): bool throw new \Exception('OneToMany IS the inverse side!'); } - public static function createFromObject(OneToManyAssociationMapping $data): self + public static function createFromObject(OneToManyAssociationMapping|array $mapping): self { + /* @legacy Remove conditional when ORM x is no longer supported! */ + if (\is_array($mapping)) { + return new self( + propertyName: $mapping['fieldName'], + targetClassName: $mapping['targetEntity'], + targetPropertyName: $mapping['mappedBy'], + orphanRemoval: $mapping['orphanRemoval'], + ); + } + return new self( - propertyName: $data->fieldName, - targetClassName: $data->targetEntity, - targetPropertyName: $data->mappedBy, - orphanRemoval: $data->orphanRemoval, + propertyName: $mapping->fieldName, + targetClassName: $mapping->targetEntity, + targetPropertyName: $mapping->mappedBy, + orphanRemoval: $mapping->orphanRemoval, ); } } diff --git a/src/Doctrine/RelationOneToOne.php b/src/Doctrine/RelationOneToOne.php index 5e852f8a2..7f3f10f66 100644 --- a/src/Doctrine/RelationOneToOne.php +++ b/src/Doctrine/RelationOneToOne.php @@ -30,9 +30,19 @@ public function getTargetSetterMethodName(): string return 'set'.Str::asCamelCase($this->getTargetPropertyName()); } - public static function createFromObject(OneToOneInverseSideMapping|OneToOneOwningSideMapping $mapping): self + public static function createFromObject(OneToOneInverseSideMapping|OneToOneOwningSideMapping|array $mapping): self { - // @TODO - Handle arrays + /* @legacy Remove conditional when ORM x is no longer supported! */ + if (\is_array($mapping)) { + return new self( + propertyName: $mapping['fieldName'], + targetClassName: $mapping['targetEntity'], + targetPropertyName: $mapping['isOwningSide'] ? $mapping['inversedBy'] : $mapping['mappedBy'], + mapInverseRelation: $mapping['isOwningSide'] ? (null !== $mapping['inversedBy']) : true, + isOwning: $mapping['isOwningSide'], + isNullable: $mapping['joinColumns'][0]['nullable'] ?? true, + ); + } if ($mapping instanceof OneToOneOwningSideMapping) { return new self( @@ -41,7 +51,7 @@ public static function createFromObject(OneToOneInverseSideMapping|OneToOneOwnin targetPropertyName: $mapping->inversedBy, mapInverseRelation: (null !== $mapping->inversedBy), isOwning: true, - // isNullable: // @TODO Handle Nullable + isNullable: $mapping->joinColumns[0]->nullable ?? true, ); } @@ -51,7 +61,7 @@ public static function createFromObject(OneToOneInverseSideMapping|OneToOneOwnin targetPropertyName: $mapping->mappedBy, mapInverseRelation: true, isOwning: false, - // isNullable: // @TODO Handle Nullable + isNullable: $mapping->joinColumns[0]->nullable ?? true, ); } } From f17167f2ec54a5b5b86825415543273c7c3e0cb9 Mon Sep 17 00:00:00 2001 From: Jesse Rushlow Date: Tue, 6 Feb 2024 11:20:11 -0500 Subject: [PATCH 21/24] handle inversed property attributes --- tests/Util/ClassSourceManipulatorTest.php | 16 ++++- .../add_entity_field/User_simple_object.php | 3 +- .../legacy/User_simple_object.php | 35 +++++++++++ .../add_one_to_many_relation/User_simple.php | 2 +- .../User_simple_orphan_removal.php | 2 +- .../User_with_use_statements.php | 2 +- .../legacy/User_simple.php | 59 ++++++++++++++++++ .../legacy/User_simple_orphan_removal.php | 59 ++++++++++++++++++ .../legacy/User_with_use_statements.php | 62 +++++++++++++++++++ .../User_simple_self.php | 2 +- .../legacy/User_simple_self.php | 34 ++++++++++ 11 files changed, 268 insertions(+), 8 deletions(-) create mode 100644 tests/Util/fixtures/add_entity_field/legacy/User_simple_object.php create mode 100644 tests/Util/fixtures/add_one_to_many_relation/legacy/User_simple.php create mode 100644 tests/Util/fixtures/add_one_to_many_relation/legacy/User_simple_orphan_removal.php create mode 100644 tests/Util/fixtures/add_one_to_many_relation/legacy/User_with_use_statements.php create mode 100644 tests/Util/fixtures/add_one_to_one_relation/legacy/User_simple_self.php diff --git a/tests/Util/ClassSourceManipulatorTest.php b/tests/Util/ClassSourceManipulatorTest.php index 1cca8b096..193587b79 100644 --- a/tests/Util/ClassSourceManipulatorTest.php +++ b/tests/Util/ClassSourceManipulatorTest.php @@ -13,6 +13,7 @@ use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Entity; +use Doctrine\ORM\Mapping\FieldMapping; use PhpParser\Builder\Param; use PHPUnit\Framework\TestCase; use Symfony\Bundle\MakerBundle\Doctrine\RelationManyToMany; @@ -236,6 +237,9 @@ private function runAddEntityFieldTests(string $source, ClassProperty $fieldOpti public function getAddEntityFieldTests(): \Generator { + /** @legacy - Remove when Doctrine/ORM 2.x is no longer supported. */ + $isLegacy = !class_exists(FieldMapping::class); + yield 'entity_normal_add' => [ 'User_simple.php', new ClassProperty(propertyName: 'fooProp', type: 'string', length: 255, nullable: false, options: ['comment' => 'new field']), @@ -263,7 +267,7 @@ public function getAddEntityFieldTests(): \Generator yield 'entity_add_object' => [ 'User_simple.php', new ClassProperty(propertyName: 'someObject', type: 'object'), - 'User_simple_object.php', + $isLegacy ? 'legacy/User_simple_object.php' : 'User_simple_object.php', ]; yield 'entity_add_uuid' => [ @@ -385,6 +389,11 @@ public function testAddOneToManyRelation(string $sourceFilename, string $expecte $sourcePath = __DIR__.'/fixtures/source'; $expectedPath = __DIR__.'/fixtures/add_one_to_many_relation'; + /** @legacy - Remove when Doctrine/ORM 2.x is no longer supported. */ + if (!class_exists(FieldMapping::class)) { + $expectedPath.= '/legacy'; + } + $this->runAddOneToManyRelationTests( file_get_contents(sprintf('%s/%s', $sourcePath, $sourceFilename)), file_get_contents(sprintf('%s/%s', $expectedPath, $expectedSourceFilename)), @@ -522,6 +531,9 @@ private function runAddOneToOneRelation(string $source, string $expected, Relati public function getAddOneToOneRelationTests(): \Generator { + /** @legacy - Remove when Doctrine/ORM 2.x is no longer supported. */ + $isLegacy = !class_exists(FieldMapping::class); + yield 'one_to_one_owning' => [ 'User_simple.php', 'User_simple_owning.php', @@ -537,7 +549,7 @@ public function getAddOneToOneRelationTests(): \Generator // a relationship to yourself - return type is self yield 'one_to_one_owning_self' => [ 'User_simple.php', - 'User_simple_self.php', + $isLegacy ? 'legacy/User_simple_self.php' : 'User_simple_self.php', new RelationOneToOne( propertyName: 'embeddedUser', targetClassName: \App\Entity\User::class, diff --git a/tests/Util/fixtures/add_entity_field/User_simple_object.php b/tests/Util/fixtures/add_entity_field/User_simple_object.php index 0ed05a87b..c4c9524f9 100644 --- a/tests/Util/fixtures/add_entity_field/User_simple_object.php +++ b/tests/Util/fixtures/add_entity_field/User_simple_object.php @@ -2,7 +2,6 @@ namespace App\Entity; -use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Mapping as ORM; #[ORM\Entity] @@ -13,7 +12,7 @@ class User #[ORM\Column()] private ?int $id = null; - #[ORM\Column(type: Types::OBJECT)] + #[ORM\Column(type: 'object')] private ?object $someObject = null; public function getId(): ?int diff --git a/tests/Util/fixtures/add_entity_field/legacy/User_simple_object.php b/tests/Util/fixtures/add_entity_field/legacy/User_simple_object.php new file mode 100644 index 000000000..0ed05a87b --- /dev/null +++ b/tests/Util/fixtures/add_entity_field/legacy/User_simple_object.php @@ -0,0 +1,35 @@ +id; + } + + public function getSomeObject(): ?object + { + return $this->someObject; + } + + public function setSomeObject(object $someObject): static + { + $this->someObject = $someObject; + + return $this; + } +} diff --git a/tests/Util/fixtures/add_one_to_many_relation/User_simple.php b/tests/Util/fixtures/add_one_to_many_relation/User_simple.php index e742e7ef0..915cae2a6 100644 --- a/tests/Util/fixtures/add_one_to_many_relation/User_simple.php +++ b/tests/Util/fixtures/add_one_to_many_relation/User_simple.php @@ -14,7 +14,7 @@ class User #[ORM\Column()] private ?int $id = null; - #[ORM\OneToMany(mappedBy: 'user', targetEntity: UserAvatarPhoto::class)] + #[ORM\OneToMany(targetEntity: UserAvatarPhoto::class, mappedBy: 'user')] private Collection $avatarPhotos; public function __construct() diff --git a/tests/Util/fixtures/add_one_to_many_relation/User_simple_orphan_removal.php b/tests/Util/fixtures/add_one_to_many_relation/User_simple_orphan_removal.php index e76e70d81..effc715bf 100644 --- a/tests/Util/fixtures/add_one_to_many_relation/User_simple_orphan_removal.php +++ b/tests/Util/fixtures/add_one_to_many_relation/User_simple_orphan_removal.php @@ -14,7 +14,7 @@ class User #[ORM\Column()] private ?int $id = null; - #[ORM\OneToMany(mappedBy: 'user', targetEntity: UserAvatarPhoto::class, orphanRemoval: true)] + #[ORM\OneToMany(targetEntity: UserAvatarPhoto::class, mappedBy: 'user', orphanRemoval: true)] private Collection $avatarPhotos; public function __construct() diff --git a/tests/Util/fixtures/add_one_to_many_relation/User_with_use_statements.php b/tests/Util/fixtures/add_one_to_many_relation/User_with_use_statements.php index 698741299..bf907e58c 100644 --- a/tests/Util/fixtures/add_one_to_many_relation/User_with_use_statements.php +++ b/tests/Util/fixtures/add_one_to_many_relation/User_with_use_statements.php @@ -17,7 +17,7 @@ class User #[ORM\Column()] private ?int $id = null; - #[ORM\OneToMany(mappedBy: 'user', targetEntity: UserAvatarPhoto::class)] + #[ORM\OneToMany(targetEntity: UserAvatarPhoto::class, mappedBy: 'user')] private Collection $avatarPhotos; public function __construct() diff --git a/tests/Util/fixtures/add_one_to_many_relation/legacy/User_simple.php b/tests/Util/fixtures/add_one_to_many_relation/legacy/User_simple.php new file mode 100644 index 000000000..e742e7ef0 --- /dev/null +++ b/tests/Util/fixtures/add_one_to_many_relation/legacy/User_simple.php @@ -0,0 +1,59 @@ +avatarPhotos = new ArrayCollection(); + } + + public function getId(): ?int + { + return $this->id; + } + + /** + * @return Collection + */ + public function getAvatarPhotos(): Collection + { + return $this->avatarPhotos; + } + + public function addAvatarPhoto(UserAvatarPhoto $avatarPhoto): static + { + if (!$this->avatarPhotos->contains($avatarPhoto)) { + $this->avatarPhotos->add($avatarPhoto); + $avatarPhoto->setUser($this); + } + + return $this; + } + + public function removeAvatarPhoto(UserAvatarPhoto $avatarPhoto): static + { + if ($this->avatarPhotos->removeElement($avatarPhoto)) { + // set the owning side to null (unless already changed) + if ($avatarPhoto->getUser() === $this) { + $avatarPhoto->setUser(null); + } + } + + return $this; + } +} diff --git a/tests/Util/fixtures/add_one_to_many_relation/legacy/User_simple_orphan_removal.php b/tests/Util/fixtures/add_one_to_many_relation/legacy/User_simple_orphan_removal.php new file mode 100644 index 000000000..e76e70d81 --- /dev/null +++ b/tests/Util/fixtures/add_one_to_many_relation/legacy/User_simple_orphan_removal.php @@ -0,0 +1,59 @@ +avatarPhotos = new ArrayCollection(); + } + + public function getId(): ?int + { + return $this->id; + } + + /** + * @return Collection + */ + public function getAvatarPhotos(): Collection + { + return $this->avatarPhotos; + } + + public function addAvatarPhoto(UserAvatarPhoto $avatarPhoto): static + { + if (!$this->avatarPhotos->contains($avatarPhoto)) { + $this->avatarPhotos->add($avatarPhoto); + $avatarPhoto->setUser($this); + } + + return $this; + } + + public function removeAvatarPhoto(UserAvatarPhoto $avatarPhoto): static + { + if ($this->avatarPhotos->removeElement($avatarPhoto)) { + // set the owning side to null (unless already changed) + if ($avatarPhoto->getUser() === $this) { + $avatarPhoto->setUser(null); + } + } + + return $this; + } +} diff --git a/tests/Util/fixtures/add_one_to_many_relation/legacy/User_with_use_statements.php b/tests/Util/fixtures/add_one_to_many_relation/legacy/User_with_use_statements.php new file mode 100644 index 000000000..698741299 --- /dev/null +++ b/tests/Util/fixtures/add_one_to_many_relation/legacy/User_with_use_statements.php @@ -0,0 +1,62 @@ +avatarPhotos = new ArrayCollection(); + } + + public function getId(): ?int + { + return $this->id; + } + + /** + * @return Collection + */ + public function getAvatarPhotos(): Collection + { + return $this->avatarPhotos; + } + + public function addAvatarPhoto(UserAvatarPhoto $avatarPhoto): static + { + if (!$this->avatarPhotos->contains($avatarPhoto)) { + $this->avatarPhotos->add($avatarPhoto); + $avatarPhoto->setUser($this); + } + + return $this; + } + + public function removeAvatarPhoto(UserAvatarPhoto $avatarPhoto): static + { + if ($this->avatarPhotos->removeElement($avatarPhoto)) { + // set the owning side to null (unless already changed) + if ($avatarPhoto->getUser() === $this) { + $avatarPhoto->setUser(null); + } + } + + return $this; + } +} diff --git a/tests/Util/fixtures/add_one_to_one_relation/User_simple_self.php b/tests/Util/fixtures/add_one_to_one_relation/User_simple_self.php index 22e44cc9d..6ee090a86 100644 --- a/tests/Util/fixtures/add_one_to_one_relation/User_simple_self.php +++ b/tests/Util/fixtures/add_one_to_one_relation/User_simple_self.php @@ -12,7 +12,7 @@ class User #[ORM\Column()] private ?int $id = null; - #[ORM\OneToOne(inversedBy: 'user', targetEntity: self::class, cascade: ['persist', 'remove'])] + #[ORM\OneToOne(targetEntity: self::class, inversedBy: 'user', cascade: ['persist', 'remove'])] private ?self $embeddedUser = null; public function getId(): ?int diff --git a/tests/Util/fixtures/add_one_to_one_relation/legacy/User_simple_self.php b/tests/Util/fixtures/add_one_to_one_relation/legacy/User_simple_self.php new file mode 100644 index 000000000..22e44cc9d --- /dev/null +++ b/tests/Util/fixtures/add_one_to_one_relation/legacy/User_simple_self.php @@ -0,0 +1,34 @@ +id; + } + + public function getEmbeddedUser(): ?self + { + return $this->embeddedUser; + } + + public function setEmbeddedUser(?self $embeddedUser): static + { + $this->embeddedUser = $embeddedUser; + + return $this; + } +} From 78d38213274eb50e8fdb478425b02a6a6ce4ceb1 Mon Sep 17 00:00:00 2001 From: Jesse Rushlow Date: Tue, 6 Feb 2024 11:43:25 -0500 Subject: [PATCH 22/24] handle array access deprecations --- src/Doctrine/EntityRegenerator.php | 4 +++- src/Util/ClassSource/Model/ClassProperty.php | 15 +++++---------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/Doctrine/EntityRegenerator.php b/src/Doctrine/EntityRegenerator.php index f60998bf9..df0473a59 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\EmbeddedClassMapping; use Doctrine\ORM\Mapping\MappingException; use Doctrine\Persistence\Mapping\MappingException as PersistenceMappingException; use Symfony\Bundle\MakerBundle\Exception\RuntimeCommandException; @@ -76,7 +77,8 @@ public function regenerateEntities(string $classOrNamespace): void continue; } - $className = $mapping['class']; + /** @legacy - Remove conditional when ORM 2.x is no longer supported. */ + $className = ($mapping instanceof EmbeddedClassMapping) ? $mapping->class : $mapping['class']; $embeddedClasses[$fieldName] = $this->getPathOfClass($className); diff --git a/src/Util/ClassSource/Model/ClassProperty.php b/src/Util/ClassSource/Model/ClassProperty.php index 4fc0d662b..220c42029 100644 --- a/src/Util/ClassSource/Model/ClassProperty.php +++ b/src/Util/ClassSource/Model/ClassProperty.php @@ -63,16 +63,6 @@ public function getAttributes(): array public static function createFromObject(FieldMapping|array $data): self { - // @TODO _ @legacy - REMOVE DEPRECATION -// if (is_array($data)) { -// @trigger_deprecation('symfony/maker-bundle', 'v99999', 'Found some data as an array....'); -// } - - // @TODO - Better exception - if (empty($data['fieldName']) || empty($data['type'])) { - throw new RuntimeCommandException('Cannot create property model - "fieldName" & "type" are required.'); - } - if ($data instanceof FieldMapping) { return new self( propertyName: $data->fieldName, @@ -87,6 +77,11 @@ public static function createFromObject(FieldMapping|array $data): self ); } + /* @legacy Remove when ORM 2.x is no longer supported. */ + if (empty($data['fieldName']) || empty($data['type'])) { + throw new RuntimeCommandException('Cannot create property model - "fieldName" & "type" are required.'); + } + return new self( propertyName: $data['fieldName'], type: $data['type'], From 3a0faa4507a11122e5b36ab871b0b8e13030de2b Mon Sep 17 00:00:00 2001 From: Jesse Rushlow Date: Tue, 6 Feb 2024 12:05:13 -0500 Subject: [PATCH 23/24] last bits of cleanup --- src/Doctrine/RelationManyToMany.php | 6 +++--- src/Doctrine/RelationManyToOne.php | 2 +- src/Doctrine/RelationOneToMany.php | 2 +- src/Doctrine/RelationOneToOne.php | 4 ++-- src/Security/UserClassBuilder.php | 1 - src/Test/MakerTestRunner.php | 24 ++++++++---------------- 6 files changed, 15 insertions(+), 24 deletions(-) diff --git a/src/Doctrine/RelationManyToMany.php b/src/Doctrine/RelationManyToMany.php index a57bd2ceb..f9aa5ef2a 100644 --- a/src/Doctrine/RelationManyToMany.php +++ b/src/Doctrine/RelationManyToMany.php @@ -32,13 +32,13 @@ public function getTargetRemoverMethodName(): string public static function createFromObject(ManyToManyInverseSideMapping|ManyToManyOwningSideMapping|array $mapping): self { - /* @legacy Remove conditional when ORM x is no longer supported! */ + /* @legacy Remove conditional when ORM 2.x is no longer supported! */ if (\is_array($mapping)) { return new self( propertyName: $mapping['fieldName'], targetClassName: $mapping['targetEntity'], targetPropertyName: $mapping['mappedBy'], - mapInverseRelation: $mapping['isOwningSide'] ? (null !== $mapping['inversedBy']) : true, + mapInverseRelation: !$mapping['isOwningSide'] || null !== $mapping['inversedBy'], isOwning: $mapping['isOwningSide'], ); } @@ -47,7 +47,7 @@ public static function createFromObject(ManyToManyInverseSideMapping|ManyToManyO return new self( propertyName: $mapping->fieldName, targetClassName: $mapping->targetEntity, - targetPropertyName: $mapping->inversedBy, // @TODO _ @legacy Is this correct? + targetPropertyName: $mapping->inversedBy, mapInverseRelation: (null !== $mapping->inversedBy), isOwning: $mapping->isOwningSide(), ); diff --git a/src/Doctrine/RelationManyToOne.php b/src/Doctrine/RelationManyToOne.php index ed3d02540..d7bfa6ba1 100644 --- a/src/Doctrine/RelationManyToOne.php +++ b/src/Doctrine/RelationManyToOne.php @@ -20,7 +20,7 @@ final class RelationManyToOne extends BaseRelation { public static function createFromObject(ManyToOneAssociationMapping|array $mapping): self { - /* @legacy Remove conditional when ORM x is no longer supported! */ + /* @legacy Remove conditional when ORM 2.x is no longer supported! */ if (\is_array($mapping)) { return new self( propertyName: $mapping['fieldName'], diff --git a/src/Doctrine/RelationOneToMany.php b/src/Doctrine/RelationOneToMany.php index 8060cbaff..7970f2f29 100644 --- a/src/Doctrine/RelationOneToMany.php +++ b/src/Doctrine/RelationOneToMany.php @@ -36,7 +36,7 @@ public function isMapInverseRelation(): bool public static function createFromObject(OneToManyAssociationMapping|array $mapping): self { - /* @legacy Remove conditional when ORM x is no longer supported! */ + /* @legacy Remove conditional when ORM 2.x is no longer supported! */ if (\is_array($mapping)) { return new self( propertyName: $mapping['fieldName'], diff --git a/src/Doctrine/RelationOneToOne.php b/src/Doctrine/RelationOneToOne.php index 7f3f10f66..72ecd5cc7 100644 --- a/src/Doctrine/RelationOneToOne.php +++ b/src/Doctrine/RelationOneToOne.php @@ -32,13 +32,13 @@ public function getTargetSetterMethodName(): string public static function createFromObject(OneToOneInverseSideMapping|OneToOneOwningSideMapping|array $mapping): self { - /* @legacy Remove conditional when ORM x is no longer supported! */ + /* @legacy Remove conditional when ORM 2.x is no longer supported! */ if (\is_array($mapping)) { return new self( propertyName: $mapping['fieldName'], targetClassName: $mapping['targetEntity'], targetPropertyName: $mapping['isOwningSide'] ? $mapping['inversedBy'] : $mapping['mappedBy'], - mapInverseRelation: $mapping['isOwningSide'] ? (null !== $mapping['inversedBy']) : true, + mapInverseRelation: !$mapping['isOwningSide'] || null !== $mapping['inversedBy'], isOwning: $mapping['isOwningSide'], isNullable: $mapping['joinColumns'][0]['nullable'] ?? true, ); diff --git a/src/Security/UserClassBuilder.php b/src/Security/UserClassBuilder.php index 00bf7e577..f77bfd132 100644 --- a/src/Security/UserClassBuilder.php +++ b/src/Security/UserClassBuilder.php @@ -199,7 +199,6 @@ private function addGetPassword(ClassSourceManipulator $manipulator, UserClassCo if ($userClassConfig->isEntity()) { // add entity property $manipulator->addEntityField( - // @TODO - Test for comments added to property new ClassProperty(propertyName: 'password', type: 'string', comments: [$propertyDocs]) ); } else { diff --git a/src/Test/MakerTestRunner.php b/src/Test/MakerTestRunner.php index 6bd12c21a..3ef18139f 100644 --- a/src/Test/MakerTestRunner.php +++ b/src/Test/MakerTestRunner.php @@ -171,28 +171,20 @@ public function configureDatabase(bool $createSchema = true): void return $config; }); - if (str_starts_with(getenv('TEST_DATABASE_DSN'), 'sqlite')) { - // make sure the database doesn't exist... - $this->runConsole('doctrine:database:drop', [], '--env=test --force'); - - if ($createSchema) { - // create the schema - $this->runConsole('doctrine:schema:create', [], '--env=test'); - } - - return; - } - - // @TODO _ @legacy - deal with this duplication - test on postgres/mysql // this looks silly, but it's the only way to drop the database *for sure*, // as doctrine:database:drop will error if there is no database - // also, skip for SQLITE, as it does not support --if-not-exists - if (!str_starts_with(getenv('TEST_DATABASE_DSN'), 'sqlite://')) { + if (!$usingSqlite = str_starts_with(getenv('TEST_DATABASE_DSN'), 'sqlite')) { + // --if-not-exists not supported on SQLite $this->runConsole('doctrine:database:create', [], '--env=test --if-not-exists'); } + $this->runConsole('doctrine:database:drop', [], '--env=test --force'); - $this->runConsole('doctrine:database:create', [], '--env=test'); + if (!$usingSqlite) { + // d:d:create not supported on SQLite + $this->runConsole('doctrine:database:create', [], '--env=test'); + } + if ($createSchema) { $this->runConsole('doctrine:schema:create', [], '--env=test'); } From a3f64c103346cc35214e64d7550368fed02cb00d Mon Sep 17 00:00:00 2001 From: Jesse Rushlow Date: Tue, 6 Feb 2024 12:29:04 -0500 Subject: [PATCH 24/24] allow for step debugging --- phpunit.xml.dist | 2 ++ src/Test/MakerTestProcess.php | 7 +++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 38d9c5c04..d35e0fca4 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -19,6 +19,8 @@ + + diff --git a/src/Test/MakerTestProcess.php b/src/Test/MakerTestProcess.php index e11a07fc3..dc3acb301 100644 --- a/src/Test/MakerTestProcess.php +++ b/src/Test/MakerTestProcess.php @@ -45,8 +45,11 @@ public function setInput($input): self public function run($allowToFail = false, array $envVars = []): self { - // @TODO _ @legacy -> This needs to be a env for debugging... - $this->process->setTimeout(null); + if (false !== ($timeout = getenv('MAKER_PROCESS_TIMEOUT'))) { + // Setting a value of null allows for step debugging + $this->process->setTimeout($timeout); + } + $this->process->run(null, $envVars); if (!$allowToFail && !$this->process->isSuccessful()) {