Add custom maker to generate CRUD consistent with existing sections, so there is less work needed after generating the CRUD for the specific Entity
This commit is contained in:
parent
195101ffde
commit
27f5cd792f
@ -11,6 +11,7 @@
|
||||
"symfony/form": "^6.0.3",
|
||||
"symfony/maker-bundle": "^1.0",
|
||||
"symfony/monolog-bundle": "^3.0",
|
||||
"symfony/security-bundle": "^6.1",
|
||||
"symfony/security-csrf": "^6.1",
|
||||
"symfony/translation": "^6.0.3",
|
||||
"symfony/twig-bundle": "^6.0",
|
||||
|
195
composer.lock
generated
195
composer.lock
generated
@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "9ca36f3fba137c0f63b47e4f71a2ac9f",
|
||||
"content-hash": "191395e56b0c475bdbd184fe246918f8",
|
||||
"packages": [
|
||||
{
|
||||
"name": "composer/package-versions-deprecated",
|
||||
@ -4677,6 +4677,106 @@
|
||||
],
|
||||
"time": "2022-09-09T09:26:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/security-bundle",
|
||||
"version": "v6.1.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/security-bundle.git",
|
||||
"reference": "1410129e36e5d0cf4bde73f4ed5d9e18acff06b3"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/security-bundle/zipball/1410129e36e5d0cf4bde73f4ed5d9e18acff06b3",
|
||||
"reference": "1410129e36e5d0cf4bde73f4ed5d9e18acff06b3",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"composer-runtime-api": ">=2.1",
|
||||
"ext-xml": "*",
|
||||
"php": ">=8.1",
|
||||
"symfony/config": "^5.4|^6.0",
|
||||
"symfony/dependency-injection": "^5.4|^6.0",
|
||||
"symfony/event-dispatcher": "^5.4|^6.0",
|
||||
"symfony/http-foundation": "^5.4|^6.0",
|
||||
"symfony/http-kernel": "^5.4|^6.0",
|
||||
"symfony/password-hasher": "^5.4|^6.0",
|
||||
"symfony/security-core": "^5.4|^6.0",
|
||||
"symfony/security-csrf": "^5.4|^6.0",
|
||||
"symfony/security-http": "^5.4|^6.0"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/browser-kit": "<5.4",
|
||||
"symfony/console": "<5.4",
|
||||
"symfony/framework-bundle": "<5.4",
|
||||
"symfony/ldap": "<5.4",
|
||||
"symfony/twig-bundle": "<5.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/annotations": "^1.10.4",
|
||||
"symfony/asset": "^5.4|^6.0",
|
||||
"symfony/browser-kit": "^5.4|^6.0",
|
||||
"symfony/console": "^5.4|^6.0",
|
||||
"symfony/css-selector": "^5.4|^6.0",
|
||||
"symfony/dom-crawler": "^5.4|^6.0",
|
||||
"symfony/expression-language": "^5.4|^6.0",
|
||||
"symfony/form": "^5.4|^6.0",
|
||||
"symfony/framework-bundle": "^5.4|^6.0",
|
||||
"symfony/ldap": "^5.4|^6.0",
|
||||
"symfony/process": "^5.4|^6.0",
|
||||
"symfony/rate-limiter": "^5.4|^6.0",
|
||||
"symfony/serializer": "^5.4|^6.0",
|
||||
"symfony/translation": "^5.4|^6.0",
|
||||
"symfony/twig-bridge": "^5.4|^6.0",
|
||||
"symfony/twig-bundle": "^5.4|^6.0",
|
||||
"symfony/validator": "^5.4|^6.0",
|
||||
"symfony/yaml": "^5.4|^6.0",
|
||||
"twig/twig": "^2.13|^3.0.4"
|
||||
},
|
||||
"type": "symfony-bundle",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Bundle\\SecurityBundle\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Provides a tight integration of the Security component into the Symfony full-stack framework",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/security-bundle/tree/v6.1.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-07-20T13:46:29+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/security-core",
|
||||
"version": "v6.1.6",
|
||||
@ -4839,6 +4939,89 @@
|
||||
],
|
||||
"time": "2022-05-14T12:53:54+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/security-http",
|
||||
"version": "v6.1.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/security-http.git",
|
||||
"reference": "789492510f7127035da8bb5dbb4fb745970e2348"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/security-http/zipball/789492510f7127035da8bb5dbb4fb745970e2348",
|
||||
"reference": "789492510f7127035da8bb5dbb4fb745970e2348",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.1",
|
||||
"symfony/http-foundation": "^5.4|^6.0",
|
||||
"symfony/http-kernel": "^6.1",
|
||||
"symfony/polyfill-mbstring": "~1.0",
|
||||
"symfony/property-access": "^5.4|^6.0",
|
||||
"symfony/security-core": "^5.4.7|^6.0"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/event-dispatcher": "<5.4.9|>=6,<6.0.9",
|
||||
"symfony/security-bundle": "<5.4",
|
||||
"symfony/security-csrf": "<5.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"psr/log": "^1|^2|^3",
|
||||
"symfony/cache": "^5.4|^6.0",
|
||||
"symfony/rate-limiter": "^5.4|^6.0",
|
||||
"symfony/routing": "^5.4|^6.0",
|
||||
"symfony/security-csrf": "^5.4|^6.0",
|
||||
"symfony/translation": "^5.4|^6.0"
|
||||
},
|
||||
"suggest": {
|
||||
"symfony/routing": "For using the HttpUtils class to create sub-requests, redirect the user, and match URLs",
|
||||
"symfony/security-csrf": "For using tokens to protect authentication/logout attempts"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\Security\\Http\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony Security Component - HTTP Integration",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/security-http/tree/v6.1.6"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-10-01T16:55:12+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/service-contracts",
|
||||
"version": "v3.1.1",
|
||||
@ -6472,12 +6655,12 @@
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Roave/SecurityAdvisories.git",
|
||||
"reference": "599b9d6746e56b67b187afed175dd02fb1a288fa"
|
||||
"reference": "d55c618dc2c5141caa00a416ca21bb6f06cf00e0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/599b9d6746e56b67b187afed175dd02fb1a288fa",
|
||||
"reference": "599b9d6746e56b67b187afed175dd02fb1a288fa",
|
||||
"url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/d55c618dc2c5141caa00a416ca21bb6f06cf00e0",
|
||||
"reference": "d55c618dc2c5141caa00a416ca21bb6f06cf00e0",
|
||||
"shasum": ""
|
||||
},
|
||||
"conflict": {
|
||||
@ -6500,6 +6683,7 @@
|
||||
"asymmetricrypt/asymmetricrypt": ">=0,<9.9.99",
|
||||
"awesome-support/awesome-support": "<=6.0.7",
|
||||
"aws/aws-sdk-php": ">=3,<3.2.1",
|
||||
"badaso/core": "<2.6.1",
|
||||
"bagisto/bagisto": "<0.1.5",
|
||||
"barrelstrength/sprout-base-email": "<1.2.7",
|
||||
"barrelstrength/sprout-forms": "<3.9",
|
||||
@ -6655,6 +6839,7 @@
|
||||
"joomla/filter": "<1.4.4|>=2,<2.0.1",
|
||||
"joomla/input": ">=2,<2.0.2",
|
||||
"joomla/session": "<1.3.1",
|
||||
"joyqi/hyper-down": "<=2.4.27",
|
||||
"jsdecena/laracom": "<2.0.9",
|
||||
"jsmitty12/phpwhois": "<5.1",
|
||||
"kazist/phpwhois": "<=4.2.6",
|
||||
@ -6993,7 +7178,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-10-24T19:11:20+00:00"
|
||||
"time": "2022-10-27T00:18:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/cli-parser",
|
||||
|
@ -1,12 +1,13 @@
|
||||
<?php declare(strict_types=1);
|
||||
<?php
|
||||
|
||||
return [
|
||||
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => TRUE],
|
||||
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => TRUE],
|
||||
Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => TRUE],
|
||||
Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => TRUE],
|
||||
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => TRUE, 'test' => TRUE],
|
||||
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => TRUE],
|
||||
Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => TRUE],
|
||||
Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class => ['all' => TRUE],
|
||||
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
|
||||
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
|
||||
Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true],
|
||||
Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
|
||||
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
|
||||
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
|
||||
Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
|
||||
Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class => ['all' => true],
|
||||
Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
|
||||
];
|
||||
|
39
config/packages/security.yaml
Normal file
39
config/packages/security.yaml
Normal file
@ -0,0 +1,39 @@
|
||||
security:
|
||||
# https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords
|
||||
password_hashers:
|
||||
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
|
||||
# https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
|
||||
providers:
|
||||
users_in_memory: { memory: null }
|
||||
firewalls:
|
||||
dev:
|
||||
pattern: ^/(_(profiler|wdt)|css|images|js)/
|
||||
security: false
|
||||
main:
|
||||
lazy: true
|
||||
provider: users_in_memory
|
||||
|
||||
# activate different ways to authenticate
|
||||
# https://symfony.com/doc/current/security.html#the-firewall
|
||||
|
||||
# https://symfony.com/doc/current/security/impersonating_user.html
|
||||
# switch_user: true
|
||||
|
||||
# Easy way to control access for large sections of your site
|
||||
# Note: Only the *first* access control that matches will be used
|
||||
access_control:
|
||||
# - { path: ^/admin, roles: ROLE_ADMIN }
|
||||
# - { path: ^/profile, roles: ROLE_USER }
|
||||
|
||||
when@test:
|
||||
security:
|
||||
password_hashers:
|
||||
# By default, password hashers are resource intensive and take time. This is
|
||||
# important to generate secure password hashes. In tests however, secure hashes
|
||||
# are not important, waste resources and increase test times. The following
|
||||
# reduces the work factor to the lowest possible values.
|
||||
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface:
|
||||
algorithm: auto
|
||||
cost: 4 # Lowest possible value for bcrypt
|
||||
time_cost: 3 # Lowest possible value for argon
|
||||
memory_cost: 10 # Lowest possible value for argon
|
@ -32,3 +32,8 @@ services:
|
||||
# App\Service\ExampleService:
|
||||
# arguments:
|
||||
# $someArgument: 'some_value'
|
||||
|
||||
App\Maker\MakeCollectionCrud:
|
||||
arguments:
|
||||
'$doctrineHelper': '@maker.doctrine_helper'
|
||||
'$formTypeRenderer': '@maker.renderer.form_type_renderer'
|
||||
|
324
src/Maker/MakeCollectionCrud.php
Normal file
324
src/Maker/MakeCollectionCrud.php
Normal file
@ -0,0 +1,324 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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 App\Traits\FormControllerTrait;
|
||||
use Doctrine\Bundle\DoctrineBundle\DoctrineBundle;
|
||||
use Doctrine\Inflector\Inflector;
|
||||
use Doctrine\Inflector\InflectorFactory;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
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\ConsoleStyle;
|
||||
use Symfony\Bundle\MakerBundle\DependencyBuilder;
|
||||
use Symfony\Bundle\MakerBundle\Doctrine\DoctrineHelper;
|
||||
use Symfony\Bundle\MakerBundle\Generator;
|
||||
use Symfony\Bundle\MakerBundle\InputConfiguration;
|
||||
use Symfony\Bundle\MakerBundle\Maker\AbstractMaker;
|
||||
use Symfony\Bundle\MakerBundle\Renderer\FormTypeRenderer;
|
||||
use Symfony\Bundle\MakerBundle\Str;
|
||||
use Symfony\Bundle\MakerBundle\Util\UseStatementGenerator;
|
||||
use Symfony\Bundle\MakerBundle\Validator;
|
||||
use Symfony\Bundle\TwigBundle\TwigBundle;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Question\Question;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Security\Csrf\CsrfTokenManager;
|
||||
use Symfony\Component\Validator\Validation;
|
||||
|
||||
/**
|
||||
* @author Sadicov Vladimir <sadikoff@gmail.com>
|
||||
*/
|
||||
final class MakeCollectionCrud extends AbstractMaker
|
||||
{
|
||||
private Inflector $inflector;
|
||||
private string $controllerClassName;
|
||||
private bool $generateTests = false;
|
||||
|
||||
public function __construct(private DoctrineHelper $doctrineHelper, private FormTypeRenderer $formTypeRenderer)
|
||||
{
|
||||
$this->inflector = InflectorFactory::create()->build();
|
||||
|
||||
define('CRUD_TEMPLATE_PREFIX', __DIR__.'/../Resources/crud');
|
||||
}
|
||||
|
||||
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())))
|
||||
->setHelp(file_get_contents(__DIR__.'/../Resources/help/MakeCrud.txt'))
|
||||
;
|
||||
|
||||
$inputConfig->setArgumentAsNonInteractive('entity-class');
|
||||
}
|
||||
|
||||
public function interact(InputInterface $input, ConsoleStyle $io, Command $command): void
|
||||
{
|
||||
if (null === $input->getArgument('entity-class')) {
|
||||
$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
|
||||
);
|
||||
|
||||
$this->generateTests = $io->confirm('Do you want to generate tests for the controller?. [Experimental]', false);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (null !== $entityDoctrineDetails->getRepositoryClass()) {
|
||||
$repositoryClassDetails = $generator->createClassNameDetails(
|
||||
'\\'.$entityDoctrineDetails->getRepositoryClass(),
|
||||
'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;
|
||||
do {
|
||||
$formClassDetails = $generator->createClassNameDetails(
|
||||
$entityClassDetails->getRelativeNameWithoutSuffix().($iter ?: '').'Type',
|
||||
'Form\\',
|
||||
'Type'
|
||||
);
|
||||
++$iter;
|
||||
} while (class_exists($formClassDetails->getFullName()));
|
||||
|
||||
$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,
|
||||
FormControllerTrait::class,
|
||||
]);
|
||||
|
||||
$generator->generateController(
|
||||
$controllerClassDetails->getFullName(),
|
||||
CRUD_TEMPLATE_PREFIX . '/controller/Controller.tpl.php',
|
||||
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'),
|
||||
],
|
||||
$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,
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($templates as $template => $variables) {
|
||||
$generator->generateTemplate(
|
||||
$templatesPath.'/'.$template.'.html.twig',
|
||||
CRUD_TEMPLATE_PREFIX . '/templates/'.$template.'.tpl.php',
|
||||
$variables
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->generateTests) {
|
||||
$testClassDetails = $generator->createClassNameDetails(
|
||||
$entityClassDetails->getRelativeNameWithoutSuffix(),
|
||||
'Test\\Controller\\',
|
||||
'ControllerTest'
|
||||
);
|
||||
|
||||
$useStatements = new UseStatementGenerator([
|
||||
$entityClassDetails->getFullName(),
|
||||
WebTestCase::class,
|
||||
KernelBrowser::class,
|
||||
$repositoryClassName,
|
||||
]);
|
||||
|
||||
$usesEntityManager = EntityManagerInterface::class === $repositoryClassName;
|
||||
|
||||
if ($usesEntityManager) {
|
||||
$useStatements->addUseStatement(EntityRepository::class);
|
||||
}
|
||||
|
||||
$generator->generateFile(
|
||||
'tests/Controller/'.$testClassDetails->getShortName().'.php',
|
||||
$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)),
|
||||
]
|
||||
);
|
||||
|
||||
if (!class_exists(WebTestCase::class)) {
|
||||
$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'
|
||||
);
|
||||
}
|
||||
}
|
69
src/Resources/crud/controller/Controller.tpl.php
Normal file
69
src/Resources/crud/controller/Controller.tpl.php
Normal file
@ -0,0 +1,69 @@
|
||||
<?= "<?php declare(strict_types=1);\n" ?>
|
||||
|
||||
namespace <?= $namespace ?>;
|
||||
|
||||
<?= $use_statements; ?>
|
||||
|
||||
#[Route('<?= $route_path ?>')]
|
||||
class <?= $class_name ?> extends AbstractController {
|
||||
use FormControllerTrait;
|
||||
|
||||
protected const ENTITY = <?= $entity_class_name ?>::class;
|
||||
protected const TEMPLATE_PATH = '<?= $entity_twig_var_singular ?>/';
|
||||
protected const ROUTE_PREFIX = '<?= $entity_twig_var_singular ?>_';
|
||||
protected const FORM = <?= $form_class_name ?>::class;
|
||||
|
||||
public function __construct(private readonly EntityManagerInterface $entityManager)
|
||||
{
|
||||
}
|
||||
<?= $generator->generateRouteForControllerMethod('/', sprintf('%s_index', $route_name), ['GET']) ?>
|
||||
<?php if (isset($repository_full_class_name)): ?>
|
||||
public function index(<?= $repository_class_name ?> $<?= $repository_var ?>): Response
|
||||
{
|
||||
return $this->render('<?= $templates_path ?>/index.html.twig', [
|
||||
'<?= $entity_twig_var_plural ?>' => $<?= $repository_var ?>->findAll(),
|
||||
]);
|
||||
}
|
||||
<?php else: ?>
|
||||
public function index(): Response
|
||||
{
|
||||
return $this->itemListView('<?= $entity_twig_var_plural ?>', []);
|
||||
}
|
||||
<?php endif ?>
|
||||
|
||||
<?= $generator->generateRouteForControllerMethod('/new', sprintf('%s_new', $route_name), ['GET', 'POST']) ?>
|
||||
<?php if (isset($repository_full_class_name) && $generator->repositoryHasSaveAndRemoveMethods($repository_full_class_name)) { ?>
|
||||
public function new(Request $request, <?= $repository_class_name ?> $<?= $repository_var ?>): Response
|
||||
<?php } else { ?>
|
||||
public function new(Request $request): Response
|
||||
<?php } ?>
|
||||
{
|
||||
return $this->itemCreate($request, '<?= $entity_twig_var_singular ?>');
|
||||
}
|
||||
|
||||
<?= $generator->generateRouteForControllerMethod(sprintf('/{%s}', $entity_identifier), sprintf('%s_show', $route_name), ['GET']) ?>
|
||||
public function show(<?= $entity_class_name ?> $<?= $entity_var_singular ?>): Response
|
||||
{
|
||||
return $this->itemView($<?= $entity_var_singular ?>, '<?= $entity_twig_var_singular ?>');
|
||||
}
|
||||
|
||||
<?= $generator->generateRouteForControllerMethod(sprintf('/{%s}/edit', $entity_identifier), sprintf('%s_edit', $route_name), ['GET', 'POST']) ?>
|
||||
<?php if (isset($repository_full_class_name) && $generator->repositoryHasSaveAndRemoveMethods($repository_full_class_name)) { ?>
|
||||
public function edit(Request $request, <?= $entity_class_name ?> $<?= $entity_var_singular ?>, <?= $repository_class_name ?> $<?= $repository_var ?>): Response
|
||||
<?php } else { ?>
|
||||
public function edit(Request $request, <?= $entity_class_name ?> $<?= $entity_var_singular ?>): Response
|
||||
<?php } ?>
|
||||
{
|
||||
return $this->itemUpdate($request, $<?= $entity_var_singular ?>, '<?= $entity_twig_var_singular ?>');
|
||||
}
|
||||
|
||||
<?= $generator->generateRouteForControllerMethod(sprintf('/{%s}', $entity_identifier), sprintf('%s_delete', $route_name), ['POST']) ?>
|
||||
<?php if (isset($repository_full_class_name) && $generator->repositoryHasSaveAndRemoveMethods($repository_full_class_name)) { ?>
|
||||
public function delete(Request $request, <?= $entity_class_name ?> $<?= $entity_var_singular ?>, <?= $repository_class_name ?> $<?= $repository_var ?>): Response
|
||||
<?php } else { ?>
|
||||
public function delete(Request $request, <?= $entity_class_name ?> $<?= $entity_var_singular ?>): Response
|
||||
<?php } ?>
|
||||
{
|
||||
return $this->deleteCSRF($request, $<?= $entity_var_singular ?>);
|
||||
}
|
||||
}
|
30
src/Resources/crud/templates/edit.tpl.php
Normal file
30
src/Resources/crud/templates/edit.tpl.php
Normal file
@ -0,0 +1,30 @@
|
||||
{% extends 'form.html.twig' %}
|
||||
|
||||
{% block title %}<?= $entity_class_name ?> - Edit{% endblock %}
|
||||
|
||||
{% block form %}
|
||||
<h2>Edit <?= $entity_class_name ?></h2>
|
||||
|
||||
<div class="small callout">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="{{ path('<?= $route_name ?>_index') }}">Back to the list</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="large primary callout">
|
||||
{{ form_start(edit_form) }}
|
||||
{{ form_widget(edit_form) }}
|
||||
<button
|
||||
type="submit"
|
||||
class="success button expanded"
|
||||
>Update</button>
|
||||
{{ form_end(edit_form) }}
|
||||
|
||||
<form method="post" action="{{ path('<?= $route_name ?>_delete', {'<?= $entity_identifier ?>': <?= $entity_twig_var_singular ?>.<?= $entity_identifier ?>}) }}" onsubmit="return confirm('Are you sure you want to delete this item?');">
|
||||
<input type="hidden" name="_token" value="{{ csrf_token('delete' ~ <?= $entity_twig_var_singular ?>.<?= $entity_identifier ?>) }}">
|
||||
<button type="submit" class="alert button expanded">Delete</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
51
src/Resources/crud/templates/index.tpl.php
Normal file
51
src/Resources/crud/templates/index.tpl.php
Normal file
@ -0,0 +1,51 @@
|
||||
<?= $helper->getHeadPrintCode($entity_class_name); ?>
|
||||
|
||||
{% block body %}
|
||||
<h2><?= $entity_class_name ?></h2>
|
||||
|
||||
<div class="small callout primary">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="{{ path('<?= $route_name ?>_new') }}">Add <?= $entity_class_name ?></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th> </th>
|
||||
<?php foreach ($entity_fields as $field): ?>
|
||||
<th><?= ucfirst($field['fieldName']) ?></th>
|
||||
<?php endforeach; ?>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for <?= $entity_twig_var_singular ?> in <?= $entity_twig_var_plural ?> %}
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="{{ path('<?= $route_name ?>_show', {'<?= $entity_identifier ?>': <?= $entity_twig_var_singular ?>.<?= $entity_identifier ?>}) }}">
|
||||
View 👁
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ path('<?= $route_name ?>_edit', {'<?= $entity_identifier ?>': <?= $entity_twig_var_singular ?>.<?= $entity_identifier ?>}) }}">
|
||||
Edit <span class="edit-icon">✎</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
<?php foreach ($entity_fields as $field): ?>
|
||||
<td>{{ <?= $helper->getEntityFieldPrintCode($entity_twig_var_singular, $field) ?> }}</td>
|
||||
<?php endforeach; ?>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="<?= (count($entity_fields) + 1) ?>">no records found</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endblock %}
|
23
src/Resources/crud/templates/new.tpl.php
Normal file
23
src/Resources/crud/templates/new.tpl.php
Normal file
@ -0,0 +1,23 @@
|
||||
{% extends 'form.html.twig' %}
|
||||
|
||||
{% block title %}<?= $entity_class_name ?> - New{% endblock %}
|
||||
|
||||
{% block form %}
|
||||
<h2>Add <?= $entity_class_name ?></h2>
|
||||
|
||||
<div class="small callout">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="{{ path('<?= $route_name ?>_index') }}">Back to the list</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="large primary callout">
|
||||
{{ form_start(form) }}
|
||||
{{ form_widget(form) }}
|
||||
<button type="submit" class="success button expanded">Add</button>
|
||||
{{ form_end(form) }}
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
40
src/Resources/crud/templates/show.tpl.php
Normal file
40
src/Resources/crud/templates/show.tpl.php
Normal file
@ -0,0 +1,40 @@
|
||||
{% extends 'form.html.twig' %}
|
||||
|
||||
{% block title %}<?= $entity_class_name ?>{% endblock %}
|
||||
|
||||
{% block form %}
|
||||
<h2>Brand</h2>
|
||||
|
||||
<div class="callout">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="{{ path('<?= $route_name ?>_index') }}">Back to the list</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ path('<?= $route_name ?>_edit', {'<?= $entity_identifier ?>': <?= $entity_twig_var_singular ?>.<?= $entity_identifier ?>}) }}">Edit</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<hr/>
|
||||
|
||||
|
||||
<form method="post" action="{{ path('<?= $route_name ?>_delete', {'<?= $entity_identifier ?>': <?= $entity_twig_var_singular ?>.<?= $entity_identifier ?>}) }}" onsubmit="return confirm('Are you sure you want to delete this item?');">
|
||||
<input type="hidden" name="_token" value="{{ csrf_token('delete' ~ <?= $entity_twig_var_singular ?>.<?= $entity_identifier ?>) }}">
|
||||
<button type="submit" class="alert button expanded">Delete</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="large primary callout">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<?php foreach ($entity_fields as $field): ?>
|
||||
<tr>
|
||||
<th><?= ucfirst($field['fieldName']) ?></th>
|
||||
<td>{{ <?= $helper->getEntityFieldPrintCode($entity_twig_var_singular, $field) ?> }}</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endblock %}
|
5
src/Resources/help/MakeCrud.txt
Normal file
5
src/Resources/help/MakeCrud.txt
Normal file
@ -0,0 +1,5 @@
|
||||
The <info>%command.name%</info> command generates crud controller with templates for selected entity.
|
||||
|
||||
<info>php %command.full_name% BlogPost</info>
|
||||
|
||||
If the argument is missing, the command will ask for the entity class name interactively.
|
12
symfony.lock
12
symfony.lock
@ -381,6 +381,18 @@
|
||||
"config/routes.yaml"
|
||||
]
|
||||
},
|
||||
"symfony/security-bundle": {
|
||||
"version": "6.1",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "6.0",
|
||||
"ref": "8a5b112826f7d3d5b07027f93786ae11a1c7de48"
|
||||
},
|
||||
"files": [
|
||||
"config/packages/security.yaml"
|
||||
]
|
||||
},
|
||||
"symfony/service-contracts": {
|
||||
"version": "v2.2.0"
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user