MakerBundle
He he empezado a desgranar el componente Maker de Symfony. El componente no sólo permite la generación de comandos automatizados, sino también podemos personalizar los nuestros propios. Asà por ejemplo en el repo: https://github.com/symfony/maker-bundle/tree/main/src/Maker
Puedo revisar como está contruido cada clase, y cuales son sus dependencias. Básicamente, cada Maker dispone de un conjunto de clases y plantillas prefabricadas para construir ficheros .yaml, .xml, y ficheros .php , que contienen clases, repositorios, getters and setters, namespaces. Y aquà la cosa se pone interesante…
Todos los métodos extienden de la clase AbstractMaker que implementa la interfaz:
interface MakerInterface
{
/**
* Return the command name for your maker (e.g. make:report).
*/
public static function getCommandName(): string;
/**
* Configure the command: set description, input arguments, options, etc.
*
* By default, all arguments will be asked interactively. If you want
* to avoid that, use the $inputConfig->setArgumentAsNonInteractive() method.
*/
public function configureCommand(Command $command, InputConfiguration $inputConfig);
/**
* Configure any library dependencies that your maker requires.
*/
public function configureDependencies(DependencyBuilder $dependencies);
/**
* If necessary, you can use this method to interactively ask the user for input.
*/
public function interact(InputInterface $input, ConsoleStyle $io, Command $command);
/**
* Called after normal code generation: allows you to do anything.
*/
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator);
}
Muy similar a Console, getCommandName configura el nombre del comando que queremos darle.
configureCommand se utiliza para utilizar como en Console, configurando tipo de argumentos y opciones de entrada.
public function configureCommand(Command $command, InputConfiguration $inputConfig): void
{
$command
->addArgument('name', InputArgument::OPTIONAL, 'The name of the security user class (e.g. <fg=yellow>User</>)')
->addOption('is-entity', null, InputOption::VALUE_NONE, 'Do you want to store user data in the database (via Doctrine)?')
->addOption('identity-property-name', null, InputOption::VALUE_REQUIRED, 'Enter a property name that will be the unique "display" name for the user (e.g. <comment>email, username, uuid</comment>)')
->addOption('with-password', null, InputOption::VALUE_NONE, 'Will this app be responsible for checking the password? Choose <comment>No</comment> if the password is actually checked by some other system (e.g. a single sign-on server)')
->setHelp(file_get_contents(__DIR__.'/../Resources/help/MakeUser.txt'));
$inputConfig->setArgumentAsNonInteractive('name');
}
configureDependencies chequea si el comando dispone de las dependencias o componentes instalados. Si no es asÃ, el comando no se ejecutará.
public function configureDependencies(DependencyBuilder $dependencies, InputInterface $input = null): void
{
// checking for SecurityBundle guarantees security.yaml is present
$dependencies->addClassDependency(
SecurityBundle::class,
'security'
);
// needed to update the YAML files
$dependencies->addClassDependency(
Yaml::class,
'yaml'
);
if (null !== $input && $input->getOption('is-entity')) {
ORMDependencyBuilder::buildDependencies($dependencies);
}
}
interact Permite configurar las preguntas para configurar nuestro comando y registrar las opciones seleccionadas. Son las opciones que el programador escoge para generar el proceso.
if (null === $input->getArgument('name')) {
$name = $io->ask(
$command->getDefinition()->getArgument('name')->getDescription(),
'User'
);
$input->setArgument('name', $name);
}
$userIsEntity = $io->confirm(
'Do you want to store user data in the database (via Doctrine)?',
class_exists(DoctrineBundle::class)
);
if ($userIsEntity) {
$dependencies = new DependencyBuilder();
ORMDependencyBuilder::buildDependencies($dependencies);
$missingPackagesMessage = $dependencies->getMissingPackagesMessage(self::getCommandName(), 'Doctrine must be installed to store user data in the database');
if ($missingPackagesMessage) {
throw new RuntimeCommandException($missingPackagesMessage);
}
}
$input->setOption('is-entity', $userIsEntity);
$identityFieldName = $io->ask('Enter a property name that will be the unique "display" name for the user (e.g. <comment>email, username, uuid</comment>)', 'email', [Validator::class, 'validatePropertyName']);
$input->setOption('identity-property-name', $identityFieldName);
$io->text('Will this app need to hash/check user passwords? Choose <comment>No</comment> if passwords are not needed or will be checked/hashed by some other system (e.g. a single sign-on server).');
$userWillHavePassword = $io->confirm('Does this app need to hash/check user passwords?');
$input->setOption('with-password', $userWillHavePassword);
generate Una vez obtenido todos los requerimientos del programador, este método se utiliza para generar las instrucciones y/o operaciones necesarias que debe realizar el comando.
$userClassConfiguration = new UserClassConfiguration(
$input->getOption('is-entity'),
$input->getOption('identity-property-name'),
$input->getOption('with-password')
);
$userClassNameDetails = $generator->createClassNameDetails(
$input->getArgument('name'),
$userClassConfiguration->isEntity() ? 'Entity\\' : 'Security\\'
);
// A) Generate the User class
if ($userClassConfiguration->isEntity()) {
$classPath = $this->entityClassGenerator->generateEntityClass(
$userClassNameDetails,
false, // api resource
$userClassConfiguration->hasPassword() // security user
);
} else {
$classPath = $generator->generateClass($userClassNameDetails->getFullName(), 'Class.tpl.php');
}
// need to write changes early so we can modify the contents below
$generator->writeChanges();
$entityUsesAttributes = ($isEntity = $userClassConfiguration->isEntity()) && $this->doctrineHelper->doesClassUsesAttributes($userClassNameDetails->getFullName());
if ($isEntity && !$entityUsesAttributes) {
throw new \RuntimeException('MakeUser only supports attribute mapping with doctrine entities.');
}
Podemos pasar dependencias a nuestro comando Maker personalizado de la siguiente manera:
App\Command\GenerateUserCommand:
arguments: ['@maker.entity_class_generator']