2023-07-21 10:35:15 -04:00
< ? php declare ( strict_types = 1 );
2022-10-27 11:53:34 -04:00
/*
* This file is part of the Symfony MakerBundle package .
*
* ( c ) Fabien Potencier < fabien @ symfony . com >
*
* For the full copyright and license information , please view the LICENSE
* file that was distributed with this source code .
*/
namespace App\Maker ;
use Doctrine\Bundle\DoctrineBundle\DoctrineBundle ;
2023-07-21 10:35:15 -04:00
use Doctrine\Inflector\ { Inflector , InflectorFactory };
use Doctrine\ORM\ { EntityManagerInterface , EntityRepository };
2022-10-27 11:53:34 -04:00
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter ;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController ;
use Symfony\Bundle\FrameworkBundle\KernelBrowser ;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase ;
use Symfony\Bundle\MakerBundle\Doctrine\DoctrineHelper ;
use Symfony\Bundle\MakerBundle\Maker\AbstractMaker ;
use Symfony\Bundle\MakerBundle\Renderer\FormTypeRenderer ;
use Symfony\Bundle\MakerBundle\Util\UseStatementGenerator ;
2023-07-21 10:35:15 -04:00
use Symfony\Bundle\MakerBundle\ { ConsoleStyle , DependencyBuilder , Generator , InputConfiguration , Str , Validator };
2022-10-27 11:53:34 -04:00
use Symfony\Bundle\TwigBundle\TwigBundle ;
use Symfony\Component\Console\Command\Command ;
2023-07-21 10:35:15 -04:00
use Symfony\Component\Console\Input\ { InputArgument , InputInterface };
2022-10-27 11:53:34 -04:00
use Symfony\Component\Console\Question\Question ;
use Symfony\Component\Form\AbstractType ;
2023-07-21 10:35:15 -04:00
use Symfony\Component\HttpFoundation\ { Request , Response };
2022-10-27 11:53:34 -04:00
use Symfony\Component\Routing\Annotation\Route ;
use Symfony\Component\Security\Csrf\CsrfTokenManager ;
use Symfony\Component\Validator\Validation ;
/**
* @ author Sadicov Vladimir < sadikoff @ gmail . com >
*/
2023-07-21 10:35:15 -04:00
final class MakeCollectionCrud extends AbstractMaker {
2022-10-27 11:53:34 -04:00
private Inflector $inflector ;
private string $controllerClassName ;
2023-07-21 10:35:15 -04:00
private bool $generateTests = FALSE ;
2022-10-27 11:53:34 -04:00
public function __construct ( private DoctrineHelper $doctrineHelper , private FormTypeRenderer $formTypeRenderer )
{
$this -> inflector = InflectorFactory :: create () -> build ();
2023-07-21 10:35:15 -04:00
define ( 'CRUD_TEMPLATE_PREFIX' , __DIR__ . '/../Resources/crud' );
2022-10-27 11:53:34 -04:00
}
public static function getCommandName () : string
{
return 'make:collection-crud' ;
}
public static function getCommandDescription () : string
{
return 'Creates custom CRUD for this App' ;
}
public function configureCommand ( Command $command , InputConfiguration $inputConfig ) : void
{
$command
-> addArgument ( 'entity-class' , InputArgument :: OPTIONAL , sprintf ( 'The class name of the entity to create CRUD (e.g. <fg=yellow>%s</>)' , Str :: asClassName ( Str :: getRandomTerm ())))
2023-07-21 10:35:15 -04:00
-> setHelp ( file_get_contents ( __DIR__ . '/../Resources/help/MakeCrud.txt' ));
2022-10-27 11:53:34 -04:00
$inputConfig -> setArgumentAsNonInteractive ( 'entity-class' );
}
public function interact ( InputInterface $input , ConsoleStyle $io , Command $command ) : void
{
2023-07-21 10:35:15 -04:00
if ( NULL === $input -> getArgument ( 'entity-class' ))
{
2022-10-27 11:53:34 -04:00
$argument = $command -> getDefinition () -> getArgument ( 'entity-class' );
$entities = $this -> doctrineHelper -> getEntitiesForAutocomplete ();
$question = new Question ( $argument -> getDescription ());
$question -> setAutocompleterValues ( $entities );
$value = $io -> askQuestion ( $question );
$input -> setArgument ( 'entity-class' , $value );
}
$defaultControllerClass = Str :: asClassName ( sprintf ( '%s Controller' , $input -> getArgument ( 'entity-class' )));
$this -> controllerClassName = $io -> ask (
sprintf ( 'Choose a name for your controller class (e.g. <fg=yellow>%s</>)' , $defaultControllerClass ),
$defaultControllerClass
);
2023-07-21 10:35:15 -04:00
$this -> generateTests = $io -> confirm ( 'Do you want to generate tests for the controller?. [Experimental]' , FALSE );
2022-10-27 11:53:34 -04:00
}
public function generate ( InputInterface $input , ConsoleStyle $io , Generator $generator ) : void
{
$entityClassDetails = $generator -> createClassNameDetails (
Validator :: entityExists ( $input -> getArgument ( 'entity-class' ), $this -> doctrineHelper -> getEntitiesForAutocomplete ()),
'Entity\\'
);
$entityDoctrineDetails = $this -> doctrineHelper -> createDoctrineDetails ( $entityClassDetails -> getFullName ());
$repositoryVars = [];
$repositoryClassName = EntityManagerInterface :: class ;
2023-07-21 10:35:15 -04:00
if ( NULL !== $entityDoctrineDetails -> getRepositoryClass ())
{
2022-10-27 11:53:34 -04:00
$repositoryClassDetails = $generator -> createClassNameDetails (
2023-07-21 10:35:15 -04:00
'\\' . $entityDoctrineDetails -> getRepositoryClass (),
2022-10-27 11:53:34 -04:00
'Repository\\' ,
'Repository'
);
$repositoryClassName = $repositoryClassDetails -> getFullName ();
$repositoryVars = [
'repository_full_class_name' => $repositoryClassName ,
'repository_class_name' => $repositoryClassDetails -> getShortName (),
'repository_var' => lcfirst ( $this -> inflector -> singularize ( $repositoryClassDetails -> getShortName ())),
];
}
$controllerClassDetails = $generator -> createClassNameDetails (
$this -> controllerClassName ,
'Controller\\' ,
'Controller'
);
$iter = 0 ;
2023-07-21 10:35:15 -04:00
do
{
2022-10-27 11:53:34 -04:00
$formClassDetails = $generator -> createClassNameDetails (
2023-07-21 10:35:15 -04:00
$entityClassDetails -> getRelativeNameWithoutSuffix () . ( $iter ? : '' ) . 'Type' ,
2022-10-27 11:53:34 -04:00
'Form\\' ,
'Type'
);
2023-07-21 10:35:15 -04:00
$iter ++ ;
}
while ( class_exists ( $formClassDetails -> getFullName ()));
2022-10-27 11:53:34 -04:00
$entityVarPlural = lcfirst ( $this -> inflector -> pluralize ( $entityClassDetails -> getShortName ()));
$entityVarSingular = lcfirst ( $this -> inflector -> singularize ( $entityClassDetails -> getShortName ()));
$entityTwigVarPlural = Str :: asTwigVariable ( $entityVarPlural );
$entityTwigVarSingular = Str :: asTwigVariable ( $entityVarSingular );
$routeName = Str :: asRouteName ( $controllerClassDetails -> getRelativeNameWithoutSuffix ());
$templatesPath = Str :: asFilePath ( $controllerClassDetails -> getRelativeNameWithoutSuffix ());
$useStatements = new UseStatementGenerator ([
$entityClassDetails -> getFullName (),
$formClassDetails -> getFullName (),
$repositoryClassName ,
AbstractController :: class ,
Request :: class ,
Response :: class ,
Route :: class ,
]);
$generator -> generateController (
$controllerClassDetails -> getFullName (),
CRUD_TEMPLATE_PREFIX . '/controller/Controller.tpl.php' ,
2023-07-21 10:35:15 -04:00
array_merge (
[
'use_statements' => $useStatements ,
'entity_class_name' => $entityClassDetails -> getShortName (),
'form_class_name' => $formClassDetails -> getShortName (),
'route_path' => Str :: asRoutePath ( $controllerClassDetails -> getRelativeNameWithoutSuffix ()),
'route_name' => $routeName ,
'templates_path' => $templatesPath ,
'entity_var_plural' => $entityVarPlural ,
'entity_twig_var_plural' => $entityTwigVarPlural ,
'entity_var_singular' => $entityVarSingular ,
'entity_twig_var_singular' => $entityTwigVarSingular ,
'entity_identifier' => $entityDoctrineDetails -> getIdentifier (),
'use_render_form' => method_exists ( AbstractController :: class , 'renderForm' ),
],
2022-10-27 11:53:34 -04:00
$repositoryVars
)
);
$this -> formTypeRenderer -> render (
$formClassDetails ,
$entityDoctrineDetails -> getFormFields (),
$entityClassDetails
);
$templates = [
'edit' => [
'entity_class_name' => $entityClassDetails -> getShortName (),
'entity_twig_var_singular' => $entityTwigVarSingular ,
'entity_identifier' => $entityDoctrineDetails -> getIdentifier (),
'route_name' => $routeName ,
'templates_path' => $templatesPath ,
],
'index' => [
'entity_class_name' => $entityClassDetails -> getShortName (),
'entity_twig_var_plural' => $entityTwigVarPlural ,
'entity_twig_var_singular' => $entityTwigVarSingular ,
'entity_identifier' => $entityDoctrineDetails -> getIdentifier (),
'entity_fields' => $entityDoctrineDetails -> getDisplayFields (),
'route_name' => $routeName ,
],
'new' => [
'entity_class_name' => $entityClassDetails -> getShortName (),
'route_name' => $routeName ,
'templates_path' => $templatesPath ,
],
'show' => [
'entity_class_name' => $entityClassDetails -> getShortName (),
'entity_twig_var_singular' => $entityTwigVarSingular ,
'entity_identifier' => $entityDoctrineDetails -> getIdentifier (),
'entity_fields' => $entityDoctrineDetails -> getDisplayFields (),
'route_name' => $routeName ,
'templates_path' => $templatesPath ,
],
];
2023-07-21 10:35:15 -04:00
foreach ( $templates as $template => $variables )
{
2022-10-27 11:53:34 -04:00
$generator -> generateTemplate (
2023-07-21 10:35:15 -04:00
$templatesPath . '/' . $template . '.html.twig' ,
CRUD_TEMPLATE_PREFIX . '/templates/' . $template . '.tpl.php' ,
2022-10-27 11:53:34 -04:00
$variables
);
}
2023-07-21 10:35:15 -04:00
if ( $this -> generateTests )
{
2022-10-27 11:53:34 -04:00
$testClassDetails = $generator -> createClassNameDetails (
$entityClassDetails -> getRelativeNameWithoutSuffix (),
'Test\\Controller\\' ,
'ControllerTest'
);
$useStatements = new UseStatementGenerator ([
$entityClassDetails -> getFullName (),
WebTestCase :: class ,
KernelBrowser :: class ,
$repositoryClassName ,
]);
$usesEntityManager = EntityManagerInterface :: class === $repositoryClassName ;
2023-07-21 10:35:15 -04:00
if ( $usesEntityManager )
{
2022-10-27 11:53:34 -04:00
$useStatements -> addUseStatement ( EntityRepository :: class );
}
$generator -> generateFile (
2023-07-21 10:35:15 -04:00
'tests/Controller/' . $testClassDetails -> getShortName () . '.php' ,
2022-10-27 11:53:34 -04:00
$usesEntityManager ? 'crud/test/Test.EntityManager.tpl.php' : 'crud/test/Test.tpl.php' ,
[
'use_statements' => $useStatements ,
'entity_full_class_name' => $entityClassDetails -> getFullName (),
'entity_class_name' => $entityClassDetails -> getShortName (),
'entity_var_singular' => $entityVarSingular ,
'route_path' => Str :: asRoutePath ( $controllerClassDetails -> getRelativeNameWithoutSuffix ()),
'route_name' => $routeName ,
'class_name' => Str :: getShortClassName ( $testClassDetails -> getFullName ()),
'namespace' => Str :: getNamespace ( $testClassDetails -> getFullName ()),
'form_fields' => $entityDoctrineDetails -> getFormFields (),
'repository_class_name' => $usesEntityManager ? EntityManagerInterface :: class : $repositoryVars [ 'repository_class_name' ],
'form_field_prefix' => strtolower ( Str :: asSnakeCase ( $entityTwigVarSingular )),
]
);
2023-07-21 10:35:15 -04:00
if ( ! class_exists ( WebTestCase :: class ))
{
2022-10-27 11:53:34 -04:00
$io -> caution ( 'You\'ll need to install the `symfony/test-pack` to execute the tests for your new controller.' );
}
}
$generator -> writeChanges ();
$this -> writeSuccessMessage ( $io );
$io -> text ( sprintf ( 'Next: Check your new CRUD by going to <fg=yellow>%s/</>' , Str :: asRoutePath ( $controllerClassDetails -> getRelativeNameWithoutSuffix ())));
}
public function configureDependencies ( DependencyBuilder $dependencies ) : void
{
$dependencies -> addClassDependency (
Route :: class ,
'router'
);
$dependencies -> addClassDependency (
AbstractType :: class ,
'form'
);
$dependencies -> addClassDependency (
Validation :: class ,
'validator'
);
$dependencies -> addClassDependency (
TwigBundle :: class ,
'twig-bundle'
);
$dependencies -> addClassDependency (
DoctrineBundle :: class ,
'orm'
);
$dependencies -> addClassDependency (
CsrfTokenManager :: class ,
'security-csrf'
);
$dependencies -> addClassDependency (
ParamConverter :: class ,
'annotations'
);
}
}