Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[make:*] improve support for ORM 3.x / DBAL 4.x #1439

Merged
merged 24 commits into from
Feb 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci-linux.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ on:
env:
PHPUNIT_FLAGS: "-v"
SYMFONY_PHPUNIT_DIR: "$HOME/symfony-bridge/.phpunit"
MAKER_SKIP_MERCURE_TEST: 1
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ux-turbo uses Doctrine\Commom\Util\ClassUtils::class which no longer exists. As such, 2 MakeEntity tests fail that use Mercure. Keep this here && create an maker bundle issue to fix? Or live with (for now) the red x's on all of our tests?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keep this for now. Then we'll fix it in ux-turbo at some point.


jobs:
coding-standards:
Expand Down
6 changes: 3 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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/" }
Expand Down
2 changes: 2 additions & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
<env name="SYMFONY_PHPUNIT_VERSION" value="9.6" />
<env name="MAKER_SKIP_MERCURE_TEST" value="false"/>
<env name="MAKER_SKIP_PANTHER_TEST" value="false" />
<!-- Overrides process timeout when step debugging -->
<!-- <env name="MAKER_PROCESS_TIMEOUT" value="null" /> -->
Comment on lines +22 to +23
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Allows for debugging Process::fromShellCommandLine() which sets the process $timeout to 60 seconds

</php>

<testsuites>
Expand Down
6 changes: 3 additions & 3 deletions src/Doctrine/DoctrineHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
62 changes: 13 additions & 49 deletions src/Doctrine/EntityRegenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@
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;
use Symfony\Bundle\MakerBundle\FileManager;
use Symfony\Bundle\MakerBundle\Generator;
use Symfony\Bundle\MakerBundle\Util\ClassSource\Model\ClassProperty;
use Symfony\Bundle\MakerBundle\Util\ClassSourceManipulator;

/**
Expand Down Expand Up @@ -75,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);

Expand All @@ -93,7 +96,10 @@ public function regenerateEntities(string $classOrNamespace): void
if (str_contains($fieldName, '.')) {
[$fieldName, $embeddedFiledName] = explode('.', $fieldName);

$operations[$embeddedClasses[$fieldName]]->addEntityField($embeddedFiledName, $mapping);
$property = ClassProperty::createFromObject($mapping);
$property->propertyName = $embeddedFiledName;

$operations[$embeddedClasses[$fieldName]]->addEntityField($property);

continue;
}
Expand All @@ -102,71 +108,29 @@ public function regenerateEntities(string $classOrNamespace): void
continue;
}

$manipulator->addEntityField($fieldName, $mapping);
$manipulator->addEntityField(ClassProperty::createFromObject($mapping));
}

$getIsNullable = function (array $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;
}

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),
));

$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'],
));

$manipulator->addOneToManyRelation($relation);
$manipulator->addOneToManyRelation(RelationOneToMany::createFromObject($mapping));

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'],
));

$manipulator->addManyToManyRelation($relation);
$manipulator->addManyToManyRelation(RelationManyToMany::createFromObject($mapping));

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),
));

$manipulator->addOneToOneRelation($relation);
$manipulator->addOneToOneRelation(RelationOneToOne::createFromObject($mapping));

break;
default:
Expand Down
34 changes: 34 additions & 0 deletions src/Doctrine/RelationManyToMany.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

namespace Symfony\Bundle\MakerBundle\Doctrine;

use Doctrine\ORM\Mapping\ManyToManyInverseSideMapping;
use Doctrine\ORM\Mapping\ManyToManyOwningSideMapping;
use Symfony\Bundle\MakerBundle\Str;

/**
Expand All @@ -27,4 +29,36 @@ public function getTargetRemoverMethodName(): string
{
return 'remove'.Str::asCamelCase(Str::pluralCamelCaseToSingular($this->getTargetPropertyName()));
}

public static function createFromObject(ManyToManyInverseSideMapping|ManyToManyOwningSideMapping|array $mapping): self
{
/* @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'],
isOwning: $mapping['isOwningSide'],
);
}

if ($mapping instanceof ManyToManyOwningSideMapping) {
return new self(
propertyName: $mapping->fieldName,
targetClassName: $mapping->targetEntity,
targetPropertyName: $mapping->inversedBy,
mapInverseRelation: (null !== $mapping->inversedBy),
isOwning: $mapping->isOwningSide(),
);
}

return new self(
propertyName: $mapping->fieldName,
targetClassName: $mapping->targetEntity,
targetPropertyName: $mapping->mappedBy,
mapInverseRelation: true,
isOwning: $mapping->isOwningSide(),
);
}
}
25 changes: 25 additions & 0 deletions src/Doctrine/RelationManyToOne.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,34 @@

namespace Symfony\Bundle\MakerBundle\Doctrine;

use Doctrine\ORM\Mapping\ManyToOneAssociationMapping;

/**
* @internal
*/
final class RelationManyToOne extends BaseRelation
{
public static function createFromObject(ManyToOneAssociationMapping|array $mapping): self
{
/* @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['inversedBy'],
mapInverseRelation: null !== $mapping['inversedBy'],
isOwning: true,
isNullable: $mapping['joinColumns'][0]['nullable'] ?? true,
);
}

return new self(
propertyName: $mapping->fieldName,
targetClassName: $mapping->targetEntity,
targetPropertyName: $mapping->inversedBy,
mapInverseRelation: null !== $mapping->inversedBy,
isOwning: true,
isNullable: $mapping->joinColumns[0]->nullable ?? true,
);
}
}
21 changes: 21 additions & 0 deletions src/Doctrine/RelationOneToMany.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

namespace Symfony\Bundle\MakerBundle\Doctrine;

use Doctrine\ORM\Mapping\OneToManyAssociationMapping;
use Symfony\Bundle\MakerBundle\Str;

/**
Expand All @@ -32,4 +33,24 @@ public function isMapInverseRelation(): bool
{
throw new \Exception('OneToMany IS the inverse side!');
}

public static function createFromObject(OneToManyAssociationMapping|array $mapping): self
{
/* @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'],
orphanRemoval: $mapping['orphanRemoval'],
);
}

return new self(
propertyName: $mapping->fieldName,
targetClassName: $mapping->targetEntity,
targetPropertyName: $mapping->mappedBy,
orphanRemoval: $mapping->orphanRemoval,
);
}
}
37 changes: 37 additions & 0 deletions src/Doctrine/RelationOneToOne.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

namespace Symfony\Bundle\MakerBundle\Doctrine;

use Doctrine\ORM\Mapping\OneToOneInverseSideMapping;
use Doctrine\ORM\Mapping\OneToOneOwningSideMapping;
use Symfony\Bundle\MakerBundle\Str;

/**
Expand All @@ -27,4 +29,39 @@ public function getTargetSetterMethodName(): string
{
return 'set'.Str::asCamelCase($this->getTargetPropertyName());
}

public static function createFromObject(OneToOneInverseSideMapping|OneToOneOwningSideMapping|array $mapping): self
{
/* @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'],
isOwning: $mapping['isOwningSide'],
isNullable: $mapping['joinColumns'][0]['nullable'] ?? true,
);
}

if ($mapping instanceof OneToOneOwningSideMapping) {
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: $mapping->fieldName,
targetClassName: $mapping->targetEntity,
targetPropertyName: $mapping->mappedBy,
mapInverseRelation: true,
isOwning: false,
isNullable: $mapping->joinColumns[0]->nullable ?? true,
);
}
}
Loading
Loading