diff --git a/EntityGeneratorBundle/Doctrine/Mapping/Generator/RelationsGenerator.php b/EntityGeneratorBundle/Doctrine/Mapping/Generator/RelationsGenerator.php index a6b94b8..891080e 100644 --- a/EntityGeneratorBundle/Doctrine/Mapping/Generator/RelationsGenerator.php +++ b/EntityGeneratorBundle/Doctrine/Mapping/Generator/RelationsGenerator.php @@ -3,12 +3,15 @@ namespace IvozDevTools\EntityGeneratorBundle\Doctrine\Mapping\Generator; use IvozDevTools\EntityGeneratorBundle\Doctrine\Mapping\MappedEntityRelation; +use IvozDevTools\EntityGeneratorBundle\Doctrine\Mapping\MappingGenerator; use IvozDevTools\EntityGeneratorBundle\Doctrine\Mapping\RequestedProperty; class RelationsGenerator { - public function __construct() - { + public function __construct( + private MappingGenerator $generator, + private string $mappingName + ) { } /** @@ -20,17 +23,21 @@ public function execute(\SimpleXMLElement $xml, array $data): \SimpleXMLElement foreach ($data as $field) { $relation = $field->getRelation(); + $inverseClass = $relation->getInverseClass(); + $inverseClassSegments = explode("\\", $inverseClass); + $className = end($inverseClassSegments); + + $inverseRelation = sprintf( + '%s\\%sInterface', + $inverseClass, + $className + ); + switch ($relation->getType()) { case MappedEntityRelation::MANY_TO_ONE: /** @var \SimpleXMLElement $manyToOne */ $manyToOne = $mappedSuperClass->addChild('many-to-one'); - $inversedEntityName = ucfirst($relation->getOwningProperty()); - $inverseRelation = sprintf( - '%s\\%sInterface', - $relation->getInverseClass(), - $inversedEntityName - ); $manyToOne->addAttribute('field', $field->getFieldName()); $manyToOne->addAttribute('target-entity', $inverseRelation); $manyToOne->addAttribute('fetch', $field->getFetch()); @@ -57,12 +64,6 @@ public function execute(\SimpleXMLElement $xml, array $data): \SimpleXMLElement /** @var \SimpleXMLElement $oneToMany */ $oneToMany = $mappedSuperClass->addChild('one-to-many'); - $inversedEntityName = ucfirst($relation->getOwningProperty()); - $inverseRelation = sprintf( - '%s\\%sInterface', - $relation->getInverseClass(), - $inversedEntityName - ); $oneToMany->addAttribute('field', $field->getFieldName()); $oneToMany->addAttribute('target-entity', $inverseRelation); $oneToMany->addAttribute('fetch', $field->getFetch()); @@ -89,13 +90,6 @@ public function execute(\SimpleXMLElement $xml, array $data): \SimpleXMLElement /** @var \SimpleXMLElement $oneToOne */ $oneToOne = $mappedSuperClass->addChild('one-to-one'); - $inversedEntityName = ucfirst($relation->getOwningProperty()); - $inverseRelation = sprintf( - '%s\\%sInterface', - $relation->getInverseClass(), - $inversedEntityName - ); - $oneToOne->addAttribute('field', $field->getFieldName()); $oneToOne->addAttribute('target-entity', $inverseRelation); $oneToOne->addAttribute('inversed-by', strtolower($relation->getOwningClass())); @@ -129,40 +123,51 @@ private function generateInversedRelation( MappedEntityRelation $relation, string $fetch ) { - throw new \Exception("TODO"); -// $mappingPaths = $this->generator->getMappingsPath(); -// $owningClass = $relation->getOwningClass(); -// $inverseProperty = $relation->getInverseProperty(); -// $owningProperty = $relation->getOwningProperty(); -// -// $output = sprintf( -// '%s/%s.%s.orm.xml', -// $mappingPaths[$relation->getInversedProjectName()], -// ucfirst($owningProperty), -// ucfirst($owningProperty) -// ); -// -// $xml = $this->generator -// ->readXmlFile($output); -// $aliasMappingPaths = $this->generator->getAliasMappingPaths(); -// $targetEntity = sprintf( -// '%s\\%s\\%sInterface', -// $aliasMappingPaths[$this->mappingName], -// $owningClass, -// $owningClass -// ); -// -// $entity = $xml->{'entity'}; -// $oneToMany = $entity->addChild('one-to-many'); -// $oneToMany->addAttribute('field', $inverseProperty); -// $oneToMany->addAttribute('target-entity', $targetEntity); -// $oneToMany->addAttribute('mapped-by', $owningProperty); -// $oneToMany->addAttribute('fetch', $fetch); -// -// $this->generator->generateXml( -// $xml, -// $output -// ); + $mappingPaths = $this->generator->getMappingsPath(); + $owningClass = $relation->getOwningClass(); + $inverseClass = $relation->getInverseClass(); + $inverseProperty = $relation->getInverseProperty(); + $owningProperty = $relation->getOwningProperty(); + + $inverseClassSegments = explode( + "\\", + $inverseClass + ); + + $output = sprintf( + '%s/%s.%s.orm.xml', + $mappingPaths[$relation->getInversedProjectName()], + end($inverseClassSegments), + end($inverseClassSegments), + ); + + $xml = $this + ->generator + ->readXmlFile($output); + + if (!$xml) { + throw new \Exception("File not found " . $output); + } + + $aliasMappingPaths = $this->generator->getAliasMappingPaths(); + $targetEntity = sprintf( + '%s\\%s\\%sInterface', + $aliasMappingPaths[$this->mappingName], + $owningClass, + $owningClass + ); + + $entity = $xml->{'entity'}; + $oneToMany = $entity->addChild('one-to-many'); + $oneToMany->addAttribute('field', $inverseProperty); + $oneToMany->addAttribute('target-entity', $targetEntity); + $oneToMany->addAttribute('mapped-by', $owningProperty); + $oneToMany->addAttribute('fetch', $fetch); + + $this->generator->generateXml( + $xml, + $output + ); } } \ No newline at end of file diff --git a/EntityGeneratorBundle/Doctrine/Mapping/Manipulator/DumpXmlAttributes.php b/EntityGeneratorBundle/Doctrine/Mapping/Manipulator/DumpXmlAttributes.php index c8950ea..caf759a 100644 --- a/EntityGeneratorBundle/Doctrine/Mapping/Manipulator/DumpXmlAttributes.php +++ b/EntityGeneratorBundle/Doctrine/Mapping/Manipulator/DumpXmlAttributes.php @@ -15,22 +15,23 @@ class DumpXmlAttributes { - private $updateFields; - private $updateIndexes; - private $updateRelations; - private $constraintsGenerator; - private $relationsGenerator; - private $fieldsGenerator; + private UpdateFields $updateFields; + private UpdateIndexes $updateIndexes; + private UpdateRelations $updateRelations; + private ConstraintsGenerator$constraintsGenerator; + private RelationsGenerator $relationsGenerator; + private FieldsGenerator $fieldsGenerator; public function __construct( private MappingGenerator $generator, - private MappedPaths $paths + private MappedPaths $paths, + string $mappingName ) { $this->updateFields = new UpdateFields(); $this->updateIndexes = new UpdateIndexes(); $this->updateRelations = new UpdateRelations(); $this->constraintsGenerator = new ConstraintsGenerator(); - $this->relationsGenerator = new RelationsGenerator(); + $this->relationsGenerator = new RelationsGenerator($generator, $mappingName); $this->fieldsGenerator = new FieldsGenerator(); } diff --git a/EntityGeneratorBundle/Doctrine/Mapping/MappingManipulator.php b/EntityGeneratorBundle/Doctrine/Mapping/MappingManipulator.php new file mode 100644 index 0000000..cc637c2 --- /dev/null +++ b/EntityGeneratorBundle/Doctrine/Mapping/MappingManipulator.php @@ -0,0 +1,351 @@ +paths + ->getMappedSuperClassPath(); + + $xml = $this->generator + ->readXmlFile($mappedSuperClassPath); + + $constraints = []; + $fields = []; + $relations = []; + foreach ($data as $value) { + if ($value->getType() === 'index') { + $constraints[] = $value; + continue; + } + + if ($value->getType() === 'relation') { + $relations[] = $value; + continue; + } + + $fields[] = $value; + } + + $xml = $this->generateFields($xml, $fields); + $xml = $this->generateRelations($xml, $relations); + $xml = $this->generateConstraints($xml, $constraints); + + $this->generator->generateXml( + $xml, + $this->paths->getMappedSuperClassPath() + ); + } + + /** + * @param RequestedProperty[] $data + */ + private function generateConstraints(SimpleXMLElement $xml, array $data): SimpleXMLElement + { + /** @var SimpleXMLElement $mappedSuperClass*/ + $mappedSuperClass = $xml->{'mapped-superclass'}; + /** @var SimpleXMLElement $uniqueConstraints*/ + $uniqueConstraints = $mappedSuperClass->{'unique-constraints'}; + /** @var SimpleXMLElement $uniqueConstraint*/ + $uniqueConstraint = $uniqueConstraints->{'unique-constraint'}; + + if (!$uniqueConstraint) { + $uniqueConstraints = $mappedSuperClass->addChild('unique-constraints'); + } + + foreach ($data as $constraint) { + $uniqueConstraint = $uniqueConstraints->addChild('unique-constraint'); + $uniqueConstraint->addAttribute('name', $constraint->getFieldName()); + $uniqueConstraint->addAttribute('columns', $constraint->getUniqueConstraintColumns()); + } + + return $xml; + } + + /** + * @param RequestedProperty[] $data + */ + private function generateFields(SimpleXMLElement $xml, array $data): SimpleXMLElement + { + $mappedSuperClass = $xml->{'mapped-superclass'}; + $options = null; + + foreach ($data as $field) { + + /** @var SimpleXMLElement $item */ + $item = $mappedSuperClass->addChild('field'); + $item->addAttribute('name', $field->getFieldName()); + $item->addAttribute('type', $field->getType()); + $item->addAttribute('column', $field->getFieldName()); + + if ($field->getType() === 'string') { + $item->addAttribute('length', (string) $field->getLength()); + $options = $item->addChild('options'); + $option = $options->addchild('option'); + $option->addAttribute('name', 'fixed'); + + if ($field->getComment()) { + $option = $options->addchild( + 'option', + sprintf( + '[enum:%s]', + $field->getComment() + ) + ); + $option->addAttribute('name', 'comment'); + } + } + + if ($field->getType() === 'integer') { + if ($field->isUnsigned()) { + $options = $item->addChild('options'); + $option = $options->addchild('option', (string) $field->isUnsigned()); + $option->addAttribute('name', 'unsigned'); + $option = $options->addchild('option'); + $option->addAttribute('name', 'fixed'); + } + } + + if ($field->getType() === 'decimal' || $field->getType() === 'float') { + $item->addAttribute('precision', (string) $field->getPrecision()); + $item->addAttribute('scale', (string) $field->getScale()); + } + + $item->addAttribute('nullable', $field->getNullable()); + + if ($field->getDefault()) { + if (!$options) { + $options = $item->addChild('options'); + } + $option = $options->addchild('option', $field->getDefault()); + $option->addAttribute('name', 'default'); + } + } + + return $xml; + } + + /** @param RequestedProperty[] $data */ + private function generateRelations(SimpleXMLElement $xml, array $data): SimpleXMLElement + { + $mappedSuperClass = $xml->{'mapped-superclass'}; + + foreach ($data as $field) { + + $relation = $field->getRelation(); + + switch ($relation->getType()) { + case MappedEntityRelation::MANY_TO_ONE: + /** @var SimpleXMLElement $manyToOne */ + $manyToOne = $mappedSuperClass->addChild('many-to-one'); + + $inversedEntityName = ucfirst($relation->getOwningProperty()); + $inverseRelation = sprintf( + '%s\\%sInterface', + $relation->getInverseClass(), + $inversedEntityName + ); + $manyToOne->addAttribute('field', $field->getFieldName()); + $manyToOne->addAttribute('target-entity', $inverseRelation); + $manyToOne->addAttribute('fetch', $field->getFetch()); + + $joinColumns = $manyToOne->addChild('join-columns'); + $joinColumn = $joinColumns->addChild('join-column'); + $entityId = sprintf('%sId', $relation->getOwningProperty()); + $joinColumn->addAttribute('name', $entityId); + $joinColumn->addAttribute('referenced-column-name', 'id'); + $joinColumn->addAttribute('on-delete', $field->getOnDelete()); + + if ($relation->isNullable()) { + $joinColumn->addAttribute('nullable', (string) $relation->isNullable()); + } + + if ($relation->getMapInverseRelation()) { + $this->generateInversedRelation( + $relation, + $field->getFetch() + ); + } + break; + case MappedEntityRelation::ONE_TO_MANY: + /** @var SimpleXMLElement $oneToMany */ + $oneToMany = $mappedSuperClass->addChild('one-to-many'); + + $inversedEntityName = ucfirst($relation->getOwningProperty()); + $inverseRelation = sprintf( + '%s\\%sInterface', + $relation->getInverseClass(), + $inversedEntityName + ); + $oneToMany->addAttribute('field', $field->getFieldName()); + $oneToMany->addAttribute('target-entity', $inverseRelation); + $oneToMany->addAttribute('fetch', $field->getFetch()); + + $joinColumns = $oneToMany->addChild('join-columns'); + $joinColumn = $joinColumns->addChild('join-column'); + $entityId = sprintf('%sId', $relation->getOwningProperty()); + $joinColumn->addAttribute('name', $entityId); + $joinColumn->addAttribute('referenced-column-name', 'id'); + $joinColumn->addAttribute('on-delete', $field->getOnDelete()); + + if ($relation->isNullable()) { + $joinColumn->addAttribute('nullable', (string) $relation->isNullable()); + } + + if ($relation->getMapInverseRelation()) { + $this->generateInversedRelation( + $relation, + $field->getFetch() + ); + } + break; + case MappedEntityRelation::ONE_TO_ONE: + /** @var SimpleXMLElement $oneToOne */ + $oneToOne = $mappedSuperClass->addChild('one-to-one'); + + $inversedEntityName = ucfirst($relation->getOwningProperty()); + $inverseRelation = sprintf( + '%s\\%sInterface', + $relation->getInverseClass(), + $inversedEntityName + ); + + $oneToOne->addAttribute('field', $field->getFieldName()); + $oneToOne->addAttribute('target-entity', $inverseRelation); + $oneToOne->addAttribute('inversed-by', strtolower($relation->getOwningClass())); + $oneToOne->addAttribute('fetch', $field->getFetch()); + + $joinColumns = $oneToOne->addChild('join-columns'); + $joinColumn = $joinColumns->addChild('join-column'); + $entityId = sprintf('%sId', $relation->getOwningProperty()); + $joinColumn->addAttribute('name', $entityId); + $joinColumn->addAttribute('referenced-column-name', 'id'); + $joinColumn->addAttribute('on-delete', $field->getOnDelete()); + + if ($relation->isNullable()) { + $joinColumn->addAttribute('nullable', (string) $relation->isNullable()); + } + + if ($relation->getMapInverseRelation()) { + $this->generateInversedRelation( + $relation, + $field->getFetch(), + ); + } + break; + } + } + + return $xml; + } + + private function generateInversedRelation( + MappedEntityRelation $relation, + string $fetch + ) { + $mappingPaths = $this->generator->getMappingsPath(); + $owningClass = $relation->getOwningClass(); + $inverseProperty = $relation->getInverseProperty(); + $owningProperty = $relation->getOwningProperty(); + + $output = sprintf( + '%s/%s.%s.orm.xml', + $mappingPaths[$relation->getInversedProjectName()], + ucfirst($owningProperty), + ucfirst($owningProperty) + ); + + $xml = $this->generator + ->readXmlFile($output); + $aliasMappingPaths = $this->generator->getAliasMappingPaths(); + $targetEntity = sprintf( + '%s\\%s\\%sInterface', + $aliasMappingPaths[$this->mappingName], + $owningClass, + $owningClass + ); + + $entity = $xml->{'entity'}; + $oneToMany = $entity->addChild('one-to-many'); + $oneToMany->addAttribute('field', $inverseProperty); + $oneToMany->addAttribute('target-entity', $targetEntity); + $oneToMany->addAttribute('mapped-by', $owningProperty); + $oneToMany->addAttribute('fetch', $fetch); + + $this->generator->generateXml( + $xml, + $output + ); + } + + /** + * @return RequestedProperty[] + */ + public function getFields(): array + { + $mappedSuperClassPath = $this + ->paths + ->getMappedSuperClassPath(); + + $xml = $this->generator + ->readXmlFile($mappedSuperClassPath); + + /** @var SimpleXMLElement $mappedSuperClass*/ + $mappedSuperClass = $xml->{'mapped-superclass'}; + + /** @var RequestedProperty[] $currentFields */ + $currentFields = []; + + foreach ($mappedSuperClass->field as $field) { + $currentFields[] = new RequestedProperty( + $field->attributes()['name'], + $field->attributes()['type'] + ); + } + + foreach ($mappedSuperClass->{'unique-constraints'}->{'unique-constraint'} as $constraint) { + $currentFields[] = new RequestedProperty( + $constraint->attributes()['name'], + 'unique_constraint' + ); + } + + foreach ($mappedSuperClass->{'many-to-one'} as $relation) { + $currentFields[] = new RequestedProperty( + $relation->attributes()['field'], + 'relation' + ); + } + + foreach ($mappedSuperClass->{'one-to-many'} as $relation) { + $currentFields[] = new RequestedProperty( + $relation->attributes()['field'], + 'relation' + ); + } + + foreach ($mappedSuperClass->{'one-to-one'} as $relation) { + $currentFields[] = new RequestedProperty( + $relation->attributes()['field'], + 'relation' + ); + } + + return $currentFields; + } +} diff --git a/EntityGeneratorBundle/Maker/MakeOrmMapping.php b/EntityGeneratorBundle/Maker/MakeOrmMapping.php index c285c26..ebdaaaf 100644 --- a/EntityGeneratorBundle/Maker/MakeOrmMapping.php +++ b/EntityGeneratorBundle/Maker/MakeOrmMapping.php @@ -2,9 +2,13 @@ namespace IvozDevTools\EntityGeneratorBundle\Maker; +use Doctrine\DBAL\Types\Type; use IvozDevTools\EntityGeneratorBundle\Doctrine\Mapping\Manipulator\DumpXmlAttributes; use IvozDevTools\EntityGeneratorBundle\Doctrine\Mapping\Manipulator\GetFields; +use IvozDevTools\EntityGeneratorBundle\Doctrine\Mapping\MappedEntityRelation; +use IvozDevTools\EntityGeneratorBundle\Doctrine\Mapping\MappedPaths; use IvozDevTools\EntityGeneratorBundle\Doctrine\Mapping\MappingGenerator; +use IvozDevTools\EntityGeneratorBundle\Doctrine\Mapping\RequestedProperty; use Symfony\Bundle\MakerBundle\ConsoleStyle; use Symfony\Bundle\MakerBundle\DependencyBuilder; use Symfony\Bundle\MakerBundle\Doctrine\DoctrineHelper; @@ -19,12 +23,6 @@ use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; -use Doctrine\DBAL\Types\Type; -use IvozDevTools\EntityGeneratorBundle\Doctrine\Mapping\MappedEntityRelation; -use IvozDevTools\EntityGeneratorBundle\Doctrine\Mapping\MappedPaths; -use IvozDevTools\EntityGeneratorBundle\Doctrine\Mapping\MappingManipulator; -use IvozDevTools\EntityGeneratorBundle\Doctrine\Mapping\RequestedProperty; - use Symfony\Component\Console\Question\Question; final class MakeOrmMapping extends AbstractMaker implements InputAwareMakerInterface @@ -136,14 +134,19 @@ public function generate( ]); } - $this->updateMappedClass($io, $mappingName, $entityName, $mappedPaths); + $this->updateMappedClass( + $io, + $mappingName, + $entityName, + $mappedPaths + ); } private function updateMappedClass( ConsoleStyle $io, - string $mappingName, - string $entityName, - MappedPaths $paths + string $mappingName, + string $entityName, + MappedPaths $paths ): void { $fieldsManipulator = new GetFields( @@ -162,9 +165,11 @@ private function updateMappedClass( /** @var RequestedProperty[] $newFields */ $newFields[] = $newField; } + $xmlManipulator = new DumpXmlAttributes( generator: $this->generator, paths: $paths, + mappingName: $mappingName ); $xmlManipulator->execute($newFields); @@ -701,9 +706,9 @@ private function printAvailableTypes(ConsoleStyle $io): void ], 'relation' => [ 'relation' => 'a ' . $wizard . ' will help you build the relation', - MappedEntityRelation::MANY_TO_ONE => [], - MappedEntityRelation::ONE_TO_MANY => [], - MappedEntityRelation::ONE_TO_ONE => [], +// MappedEntityRelation::MANY_TO_ONE => [], +// MappedEntityRelation::ONE_TO_MANY => [], +// MappedEntityRelation::ONE_TO_ONE => [], ], 'constraint' => [ 'unique_constraint' => [],