|
|
<?php
namespace Prometeo\CommandsBundle\Commands;
use Doctrine\DBAL\Types\Type;use Doctrine\ORM\Mapping\Driver\AnnotationDriver;use Symfony\Bundle\MakerBundle\ConsoleStyle;use Symfony\Bundle\MakerBundle\DependencyBuilder;use Symfony\Bundle\MakerBundle\Doctrine\DoctrineHelper;use Symfony\Bundle\MakerBundle\Doctrine\EntityClassGenerator;use Symfony\Bundle\MakerBundle\Doctrine\EntityRegenerator;use Symfony\Bundle\MakerBundle\Doctrine\EntityRelation;use Symfony\Bundle\MakerBundle\Doctrine\ORMDependencyBuilder;use Symfony\Bundle\MakerBundle\Exception\RuntimeCommandException;use Symfony\Bundle\MakerBundle\FileManager;use Symfony\Bundle\MakerBundle\Generator;use Symfony\Bundle\MakerBundle\InputAwareMakerInterface;use Symfony\Bundle\MakerBundle\InputConfiguration;use Symfony\Bundle\MakerBundle\Str;use Symfony\Bundle\MakerBundle\Util\ClassDetails;use Symfony\Bundle\MakerBundle\Util\ClassSourceManipulator;use Symfony\Bundle\MakerBundle\Validator;use Symfony\Component\Console\Command\Command;use Symfony\Component\Console\Input\InputArgument;use Symfony\Component\Console\Input\InputInterface;use Symfony\Component\Console\Input\InputOption;use Symfony\Component\Console\Question\ConfirmationQuestion;use Symfony\Component\Console\Question\Question;
/** * @author Javier Eguiluz <javier.eguiluz@gmail.com> * @author Ryan Weaver <weaverryan@gmail.com> * @author Kévin Dunglas <dunglas@gmail.com> */final class MakeEntities extends AbstractMaker implements InputAwareMakerInterface{ private $fileManager; private $doctrineHelper; private $generator;
public function __construct(FileManager $fileManager, DoctrineHelper $doctrineHelper, string $projectDirectory, Generator $generator = null) { $this->fileManager = $fileManager; $this->doctrineHelper = $doctrineHelper; // $projectDirectory is unused, argument kept for BC
if (null === $generator) { @trigger_error(sprintf('Passing a "%s" instance as 4th argument is mandatory since version 1.5.', Generator::class), E_USER_DEPRECATED); $this->generator = new Generator($fileManager, 'App\\'); } else { $this->generator = $generator; } }
public static function getCommandName(): string { return 'prometeo:make:entities'; }
public function configureCommand(Command $command, InputConfiguration $inputConf) { $command ->setDescription('Creates or updates a Doctrine entity class, and optionally an API Platform resource') ->addArgument('name', InputArgument::OPTIONAL, sprintf('Class name of the entity to create or update (e.g. <fg=yellow>%s</>)', Str::asClassName(Str::getRandomTerm()))) ->addOption('api-resource', 'a', InputOption::VALUE_NONE, 'Mark this class as an API Platform resource (expose a CRUD API for it)') ->addOption('regenerate', null, InputOption::VALUE_NONE, 'Instead of adding new fields, simply generate the methods (e.g. getter/setter) for existing fields') ->addOption('overwrite', null, InputOption::VALUE_NONE, 'Overwrite any existing getter/setter methods') ->setHelp(file_get_contents(__DIR__.'/../Resources/help/MakeEntity.txt')) ;
$inputConf->setArgumentAsNonInteractive('name'); }
public function interact(InputInterface $input, ConsoleStyle $io, Command $command) { if ($input->getArgument('name')) { return; }
if ($input->getOption('regenerate')) { $io->block([ 'This command will generate any missing methods (e.g. getters & setters) for a class or all classes in a namespace.', 'To overwrite any existing methods, re-run this command with the --overwrite flag', ], null, 'fg=yellow'); $classOrNamespace = $io->ask('Enter a class or namespace to regenerate', $this->getEntityNamespace(), [Validator::class, 'notBlank']);
$input->setArgument('name', $classOrNamespace);
return; }
$argument = $command->getDefinition()->getArgument('name'); $question = $this->createEntityClassQuestion($argument->getDescription()); $value = $io->askQuestion($question);
$input->setArgument('name', $value);
if ( !$input->getOption('api-resource') && class_exists(ApiResource::class) && !class_exists($this->generator->createClassNameDetails($value, 'Entity\\')->getFullName()) ) { $description = $command->getDefinition()->getOption('api-resource')->getDescription(); $question = new ConfirmationQuestion($description, false); $value = $io->askQuestion($question);
$input->setOption('api-resource', $value); } }
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator) { $overwrite = $input->getOption('overwrite');
// the regenerate option has entirely custom behavior
if ($input->getOption('regenerate')) { $this->regenerateEntities($input->getArgument('name'), $overwrite, $generator); $this->writeSuccessMessage($io);
return; }
$entityClassDetails = $generator->createClassNameDetails( $input->getArgument('name'), 'Entity\\' );
$classExists = class_exists($entityClassDetails->getFullName()); if (!$classExists) { $entityClassGenerator = new EntityClassGenerator($generator); $entityPath = $entityClassGenerator->generateEntityClass( $entityClassDetails, $input->getOption('api-resource') );
$generator->writeChanges(); }
if (!$this->doesEntityUseAnnotationMapping($entityClassDetails->getFullName())) { throw new RuntimeCommandException(sprintf('Only annotation mapping is supported by make:entity, but the <info>%s</info> class uses a different format. If you would like this command to generate the properties & getter/setter methods, add your mapping configuration, and then re-run this command with the <info>--regenerate</info> flag.', $entityClassDetails->getFullName())); }
if ($classExists) { $entityPath = $this->getPathOfClass($entityClassDetails->getFullName()); $io->text([ 'Your entity already exists! So let\'s add some new fields!', ]); } else { $io->text([ '', 'Entity generated! Now let\'s add some fields!', 'You can always add more fields later manually or by re-running this command.', ]); }
$currentFields = $this->getPropertyNames($entityClassDetails->getFullName()); $manipulator = $this->createClassManipulator($entityPath, $io, $overwrite);
$isFirstField = true; while (true) { $newField = $this->askForNextField($io, $currentFields, $entityClassDetails->getFullName(), $isFirstField); $isFirstField = false;
if (null === $newField) { break; }
$fileManagerOperations = []; $fileManagerOperations[$entityPath] = $manipulator;
if (\is_array($newField)) { $annotationOptions = $newField; unset($annotationOptions['fieldName']); $manipulator->addEntityField($newField['fieldName'], $annotationOptions);
$currentFields[] = $newField['fieldName']; } elseif ($newField instanceof EntityRelation) { // both overridden below for OneToMany
$newFieldName = $newField->getOwningProperty(); if ($newField->isSelfReferencing()) { $otherManipulatorFilename = $entityPath; $otherManipulator = $manipulator; } else { $otherManipulatorFilename = $this->getPathOfClass($newField->getInverseClass()); $otherManipulator = $this->createClassManipulator($otherManipulatorFilename, $io, $overwrite); } switch ($newField->getType()) { case EntityRelation::MANY_TO_ONE: if ($newField->getOwningClass() === $entityClassDetails->getFullName()) { // THIS class will receive the ManyToOne
$manipulator->addManyToOneRelation($newField->getOwningRelation());
if ($newField->getMapInverseRelation()) { $otherManipulator->addOneToManyRelation($newField->getInverseRelation()); } } else { // the new field being added to THIS entity is the inverse
$newFieldName = $newField->getInverseProperty(); $otherManipulatorFilename = $this->getPathOfClass($newField->getOwningClass()); $otherManipulator = $this->createClassManipulator($otherManipulatorFilename, $io, $overwrite);
// The *other* class will receive the ManyToOne
$otherManipulator->addManyToOneRelation($newField->getOwningRelation()); if (!$newField->getMapInverseRelation()) { throw new \Exception('Somehow a OneToMany relationship is being created, but the inverse side will not be mapped?'); } $manipulator->addOneToManyRelation($newField->getInverseRelation()); }
break; case EntityRelation::MANY_TO_MANY: $manipulator->addManyToManyRelation($newField->getOwningRelation()); if ($newField->getMapInverseRelation()) { $otherManipulator->addManyToManyRelation($newField->getInverseRelation()); }
break; case EntityRelation::ONE_TO_ONE: $manipulator->addOneToOneRelation($newField->getOwningRelation()); if ($newField->getMapInverseRelation()) { $otherManipulator->addOneToOneRelation($newField->getInverseRelation()); }
break; default: throw new \Exception('Invalid relation type'); }
// save the inverse side if it's being mapped
if ($newField->getMapInverseRelation()) { $fileManagerOperations[$otherManipulatorFilename] = $otherManipulator; } $currentFields[] = $newFieldName; } else { throw new \Exception('Invalid value'); }
foreach ($fileManagerOperations as $path => $manipulatorOrMessage) { if (\is_string($manipulatorOrMessage)) { $io->comment($manipulatorOrMessage); } else { $this->fileManager->dumpFile($path, $manipulatorOrMessage->getSourceCode()); } } }
$this->writeSuccessMessage($io); $io->text([ 'Next: When you\'re ready, create a migration with <comment>make:migration</comment>', '', ]); }
public function configureDependencies(DependencyBuilder $dependencies, InputInterface $input = null) { $dependencies->requirePHP71();
if (null !== $input && $input->getOption('api-resource')) { $dependencies->addClassDependency( ApiResource::class, 'api' ); }
ORMDependencyBuilder::buildDependencies($dependencies); }
private function askForNextField(ConsoleStyle $io, array $fields, string $entityClass, bool $isFirstField) { $io->writeln('');
if ($isFirstField) { $questionText = 'New property name (press <return> to stop adding fields)'; } else { $questionText = 'Add another property? Enter the property name (or press <return> to stop adding fields)'; }
$fieldName = $io->ask($questionText, null, function ($name) use ($fields) { // allow it to be empty
if (!$name) { return $name; }
if (\in_array($name, $fields)) { throw new \InvalidArgumentException(sprintf('The "%s" property already exists.', $name)); }
return Validator::validateDoctrineFieldName($name, $this->doctrineHelper->getRegistry()); });
if (!$fieldName) { return null; }
$defaultType = 'string'; // try to guess the type by the field name prefix/suffix
// convert to snake case for simplicity
$snakeCasedField = Str::asSnakeCase($fieldName);
if ('_at' === $suffix = substr($snakeCasedField, -3)) { $defaultType = 'datetime'; } elseif ('_id' === $suffix) { $defaultType = 'integer'; } elseif (0 === strpos($snakeCasedField, 'is_')) { $defaultType = 'boolean'; } elseif (0 === strpos($snakeCasedField, 'has_')) { $defaultType = 'boolean'; }
$type = null; $types = Type::getTypesMap(); // remove deprecated json_array
unset($types[Type::JSON_ARRAY]);
$allValidTypes = array_merge( array_keys($types), EntityRelation::getValidRelationTypes(), ['relation'] ); while (null === $type) { $question = new Question('Field type (enter <comment>?</comment> to see all types)', $defaultType); $question->setAutocompleterValues($allValidTypes); $type = $io->askQuestion($question);
if ('?' === $type) { $this->printAvailableTypes($io); $io->writeln('');
$type = null; } elseif (!\in_array($type, $allValidTypes)) { $this->printAvailableTypes($io); $io->error(sprintf('Invalid type "%s".', $type)); $io->writeln('');
$type = null; } }
if ('relation' === $type || \in_array($type, EntityRelation::getValidRelationTypes())) { return $this->askRelationDetails($io, $entityClass, $type, $fieldName); }
// this is a normal field
$data = ['fieldName' => $fieldName, 'type' => $type]; if ('string' === $type) { // default to 255, avoid the question
$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']);
// 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']); }
if ($io->confirm('Can this field be null in the database (nullable)', false)) { $data['nullable'] = true; }
return $data; }
private function printAvailableTypes(ConsoleStyle $io) { $allTypes = Type::getTypesMap();
if ('Hyper' === getenv('TERM_PROGRAM')) { $wizard = 'wizard 🧙'; } else { $wizard = '\\' === \DIRECTORY_SEPARATOR ? 'wizard' : 'wizard 🧙'; }
$typesTable = [ 'main' => [ 'string' => [], 'text' => [], 'boolean' => [], 'integer' => ['smallint', 'bigint'], 'float' => [], ], 'relation' => [ 'relation' => 'a '.$wizard.' will help you build the relation', EntityRelation::MANY_TO_ONE => [], EntityRelation::ONE_TO_MANY => [], EntityRelation::MANY_TO_MANY => [], EntityRelation::ONE_TO_ONE => [], ], 'array_object' => [ 'array' => ['simple_array'], 'json' => [], 'object' => [], 'binary' => [], 'blob' => [], ], 'date_time' => [ 'datetime' => ['datetime_immutable'], 'datetimetz' => ['datetimetz_immutable'], 'date' => ['date_immutable'], 'time' => ['time_immutable'], 'dateinterval' => [], ], ];
$printSection = function (array $sectionTypes) use ($io, &$allTypes) { foreach ($sectionTypes as $mainType => $subTypes) { unset($allTypes[$mainType]); $line = sprintf(' * <comment>%s</comment>', $mainType);
if (\is_string($subTypes) && $subTypes) { $line .= sprintf(' (%s)', $subTypes); } elseif (\is_array($subTypes) && !empty($subTypes)) { $line .= sprintf(' (or %s)', implode(', ', array_map(function ($subType) { return sprintf('<comment>%s</comment>', $subType); }, $subTypes)));
foreach ($subTypes as $subType) { unset($allTypes[$subType]); } }
$io->writeln($line); }
$io->writeln(''); };
$io->writeln('<info>Main types</info>'); $printSection($typesTable['main']);
$io->writeln('<info>Relationships / Associations</info>'); $printSection($typesTable['relation']);
$io->writeln('<info>Array/Object Types</info>'); $printSection($typesTable['array_object']);
$io->writeln('<info>Date/Time Types</info>'); $printSection($typesTable['date_time']);
$io->writeln('<info>Other Types</info>'); // empty the values
$allTypes = array_map(function () { return []; }, $allTypes); $printSection($allTypes); }
private function createEntityClassQuestion(string $questionText): Question { $question = new Question($questionText); $question->setValidator([Validator::class, 'notBlank']); $question->setAutocompleterValues($this->doctrineHelper->getEntitiesForAutocomplete());
return $question; }
private function askRelationDetails(ConsoleStyle $io, string $generatedEntityClass, string $type, string $newFieldName) { // ask the targetEntity
$targetEntityClass = null; while (null === $targetEntityClass) { $question = $this->createEntityClassQuestion('What class should this entity be related to?');
$answeredEntityClass = $io->askQuestion($question);
// find the correct class name - but give priority over looking
// in the Entity namespace versus just checking the full class
// name to avoid issues with classes like "Directory" that exist
// in PHP's core.
if (class_exists($this->getEntityNamespace().'\\'.$answeredEntityClass)) { $targetEntityClass = $this->getEntityNamespace().'\\'.$answeredEntityClass; } elseif (class_exists($answeredEntityClass)) { $targetEntityClass = $answeredEntityClass; } else { $io->error(sprintf('Unknown class "%s"', $answeredEntityClass)); continue; } }
// help the user select the type
if ('relation' === $type) { $type = $this->askRelationType($io, $generatedEntityClass, $targetEntityClass); }
$askFieldName = function (string $targetClass, string $defaultValue) use ($io) { return $io->ask( sprintf('New field name inside %s', Str::getShortClassName($targetClass)), $defaultValue, function ($name) use ($targetClass) { // it's still *possible* to create duplicate properties - by
// trying to generate the same property 2 times during the
// same make:entity run. property_exists() only knows about
// properties that *originally* existed on this class.
if (property_exists($targetClass, $name)) { throw new \InvalidArgumentException(sprintf('The "%s" class already has a "%s" property.', $targetClass, $name)); }
return Validator::validateDoctrineFieldName($name, $this->doctrineHelper->getRegistry()); } ); };
$askIsNullable = function (string $propertyName, string $targetClass) use ($io) { return $io->confirm(sprintf( 'Is the <comment>%s</comment>.<comment>%s</comment> property allowed to be null (nullable)?', Str::getShortClassName($targetClass), $propertyName )); };
$askOrphanRemoval = function (string $owningClass, string $inverseClass) use ($io) { $io->text([ 'Do you want to activate <comment>orphanRemoval</comment> on your relationship?', sprintf( 'A <comment>%s</comment> is "orphaned" when it is removed from its related <comment>%s</comment>.', Str::getShortClassName($owningClass), Str::getShortClassName($inverseClass) ), sprintf( 'e.g. <comment>$%s->remove%s($%s)</comment>', Str::asLowerCamelCase(Str::getShortClassName($inverseClass)), Str::asCamelCase(Str::getShortClassName($owningClass)), Str::asLowerCamelCase(Str::getShortClassName($owningClass)) ), '', sprintf( 'NOTE: If a <comment>%s</comment> may *change* from one <comment>%s</comment> to another, answer "no".', Str::getShortClassName($owningClass), Str::getShortClassName($inverseClass) ), ]);
return $io->confirm(sprintf('Do you want to automatically delete orphaned <comment>%s</comment> objects (orphanRemoval)?', $owningClass), false); };
$askInverseSide = function (EntityRelation $relation) use ($io) { if ($this->isClassInVendor($relation->getInverseClass())) { $relation->setMapInverseRelation(false);
return; }
// recommend an inverse side, except for OneToOne, where it's inefficient
$recommendMappingInverse = EntityRelation::ONE_TO_ONE !== $relation->getType();
$getterMethodName = 'get'.Str::asCamelCase(Str::getShortClassName($relation->getOwningClass())); if (EntityRelation::ONE_TO_ONE !== $relation->getType()) { // pluralize!
$getterMethodName = Str::singularCamelCaseToPluralCamelCase($getterMethodName); } $mapInverse = $io->confirm( sprintf( 'Do you want to add a new property to <comment>%s</comment> so that you can access/update <comment>%s</comment> objects from it - e.g. <comment>$%s->%s()</comment>?', Str::getShortClassName($relation->getInverseClass()), Str::getShortClassName($relation->getOwningClass()), Str::asLowerCamelCase(Str::getShortClassName($relation->getInverseClass())), $getterMethodName ), $recommendMappingInverse ); $relation->setMapInverseRelation($mapInverse); };
switch ($type) { case EntityRelation::MANY_TO_ONE: $relation = new EntityRelation( EntityRelation::MANY_TO_ONE, $generatedEntityClass, $targetEntityClass ); $relation->setOwningProperty($newFieldName);
$relation->setIsNullable($askIsNullable( $relation->getOwningProperty(), $relation->getOwningClass() ));
$askInverseSide($relation); if ($relation->getMapInverseRelation()) { $io->comment(sprintf( 'A new property will also be added to the <comment>%s</comment> class so that you can access the related <comment>%s</comment> objects from it.', Str::getShortClassName($relation->getInverseClass()), Str::getShortClassName($relation->getOwningClass()) )); $relation->setInverseProperty($askFieldName( $relation->getInverseClass(), Str::singularCamelCaseToPluralCamelCase(Str::getShortClassName($relation->getOwningClass())) ));
// orphan removal only applies if the inverse relation is set
if (!$relation->isNullable()) { $relation->setOrphanRemoval($askOrphanRemoval( $relation->getOwningClass(), $relation->getInverseClass() )); } }
break; case EntityRelation::ONE_TO_MANY: // we *actually* create a ManyToOne, but populate it differently
$relation = new EntityRelation( EntityRelation::MANY_TO_ONE, $targetEntityClass, $generatedEntityClass ); $relation->setInverseProperty($newFieldName);
$io->comment(sprintf( 'A new property will also be added to the <comment>%s</comment> class so that you can access and set the related <comment>%s</comment> object from it.', Str::getShortClassName($relation->getOwningClass()), Str::getShortClassName($relation->getInverseClass()) )); $relation->setOwningProperty($askFieldName( $relation->getOwningClass(), Str::asLowerCamelCase(Str::getShortClassName($relation->getInverseClass())) ));
$relation->setIsNullable($askIsNullable( $relation->getOwningProperty(), $relation->getOwningClass() ));
if (!$relation->isNullable()) { $relation->setOrphanRemoval($askOrphanRemoval( $relation->getOwningClass(), $relation->getInverseClass() )); }
break; case EntityRelation::MANY_TO_MANY: $relation = new EntityRelation( EntityRelation::MANY_TO_MANY, $generatedEntityClass, $targetEntityClass ); $relation->setOwningProperty($newFieldName);
$askInverseSide($relation); if ($relation->getMapInverseRelation()) { $io->comment(sprintf( 'A new property will also be added to the <comment>%s</comment> class so that you can access the related <comment>%s</comment> objects from it.', Str::getShortClassName($relation->getInverseClass()), Str::getShortClassName($relation->getOwningClass()) )); $relation->setInverseProperty($askFieldName( $relation->getInverseClass(), Str::singularCamelCaseToPluralCamelCase(Str::getShortClassName($relation->getOwningClass())) )); }
break; case EntityRelation::ONE_TO_ONE: $relation = new EntityRelation( EntityRelation::ONE_TO_ONE, $generatedEntityClass, $targetEntityClass ); $relation->setOwningProperty($newFieldName);
$relation->setIsNullable($askIsNullable( $relation->getOwningProperty(), $relation->getOwningClass() ));
$askInverseSide($relation); if ($relation->getMapInverseRelation()) { $io->comment(sprintf( 'A new property will also be added to the <comment>%s</comment> class so that you can access the related <comment>%s</comment> object from it.', Str::getShortClassName($relation->getInverseClass()), Str::getShortClassName($relation->getOwningClass()) )); $relation->setInverseProperty($askFieldName( $relation->getInverseClass(), Str::asLowerCamelCase(Str::getShortClassName($relation->getOwningClass())) )); }
break; default: throw new \InvalidArgumentException('Invalid type: '.$type); }
return $relation; }
private function askRelationType(ConsoleStyle $io, string $entityClass, string $targetEntityClass) { $io->writeln('What type of relationship is this?');
$originalEntityShort = Str::getShortClassName($entityClass); $targetEntityShort = Str::getShortClassName($targetEntityClass); $rows = []; $rows[] = [ EntityRelation::MANY_TO_ONE, sprintf("Each <comment>%s</comment> relates to (has) <info>one</info> <comment>%s</comment>.\nEach <comment>%s</comment> can relate to (can have) <info>many</info> <comment>%s</comment> objects", $originalEntityShort, $targetEntityShort, $targetEntityShort, $originalEntityShort), ]; $rows[] = ['', '']; $rows[] = [ EntityRelation::ONE_TO_MANY, sprintf("Each <comment>%s</comment> can relate to (can have) <info>many</info> <comment>%s</comment> objects.\nEach <comment>%s</comment> relates to (has) <info>one</info> <comment>%s</comment>", $originalEntityShort, $targetEntityShort, $targetEntityShort, $originalEntityShort), ]; $rows[] = ['', '']; $rows[] = [ EntityRelation::MANY_TO_MANY, sprintf("Each <comment>%s</comment> can relate to (can have) <info>many</info> <comment>%s</comment> objects.\nEach <comment>%s</comment> can also relate to (can also have) <info>many</info> <comment>%s</comment> objects", $originalEntityShort, $targetEntityShort, $targetEntityShort, $originalEntityShort), ]; $rows[] = ['', '']; $rows[] = [ EntityRelation::ONE_TO_ONE, sprintf("Each <comment>%s</comment> relates to (has) exactly <info>one</info> <comment>%s</comment>.\nEach <comment>%s</comment> also relates to (has) exactly <info>one</info> <comment>%s</comment>.", $originalEntityShort, $targetEntityShort, $targetEntityShort, $originalEntityShort), ];
$io->table([ 'Type', 'Description', ], $rows);
$question = new Question(sprintf( 'Relation type? [%s]', implode(', ', EntityRelation::getValidRelationTypes()) )); $question->setAutocompleterValues(EntityRelation::getValidRelationTypes()); $question->setValidator(function ($type) { if (!\in_array($type, EntityRelation::getValidRelationTypes())) { throw new \InvalidArgumentException(sprintf('Invalid type: use one of: %s', implode(', ', EntityRelation::getValidRelationTypes()))); }
return $type; });
return $io->askQuestion($question); }
private function createClassManipulator(string $path, ConsoleStyle $io, bool $overwrite): ClassSourceManipulator { $manipulator = new ClassSourceManipulator($this->fileManager->getFileContents($path), $overwrite); $manipulator->setIo($io);
return $manipulator; }
private function getPathOfClass(string $class): string { $classDetails = new ClassDetails($class);
return $classDetails->getPath(); }
private function isClassInVendor(string $class): bool { $path = $this->getPathOfClass($class);
return $this->fileManager->isPathInVendor($path); }
private function regenerateEntities(string $classOrNamespace, bool $overwrite, Generator $generator) { $regenerator = new EntityRegenerator($this->doctrineHelper, $this->fileManager, $generator, $overwrite); $regenerator->regenerateEntities($classOrNamespace); }
private function getPropertyNames(string $class): array { if (!class_exists($class)) { return []; }
$reflClass = new \ReflectionClass($class);
return array_map(function (\ReflectionProperty $prop) { return $prop->getName(); }, $reflClass->getProperties()); }
private function doesEntityUseAnnotationMapping(string $className): bool { if (!class_exists($className)) { $otherClassMetadatas = $this->doctrineHelper->getMetadata(Str::getNamespace($className).'\\', true);
// if we have no metadata, we should assume this is the first class being mapped
if (empty($otherClassMetadatas)) { return false; }
$className = reset($otherClassMetadatas)->getName(); }
$driver = $this->doctrineHelper->getMappingDriverForClass($className);
return $driver instanceof AnnotationDriver; }
private function getEntityNamespace(): string { return $this->doctrineHelper->getEntityNamespace(); }}
|