Skip to content

Commit fb18b88

Browse files
committed
feature #113 Add make:crud & improve make:form commands (sadikoff, weaverryan)
This PR was merged into the 1.0-dev branch. Discussion ---------- Add make:crud & improve make:form commands Hi all, ## Improved make:form command Main improvement is that command works in two ways. First way simple, it just generates skeleton form class and user can do with it whatever he want. Second way is generation from entity class. Generator checks if entity exists and ask user if he wants to generate form from entity or user can use `--entity` flag if he knows that entity exists. ### Generation features: - Simple skeleton form class - Doctrine entity form class ### Examples: **Simple class generation** ```bash root@striX: ~/project_root# bin/console make:form The name of the form class (e.g. GentleElephantType): > SimpleForm Enter the class or entity name that the new form will be bound to (empty for none): > created: src/Form/SimpleFormType.php Success! ``` **Entity form generation** ```bash root@striX: ~/project_root# bin/console make:form The name of the form class (e.g. GentleElephantType): > EntityFormType Enter the class or entity name that the new form will be bound to (empty for none): > Entity created: src/Form/EntityFormType.php Success! ``` Closes: #114 ## New make:crud command Want to introduce `make:crud` command it is similar to doctrine:generate:crud from SensioGeneratorBundle. All you need to generate cool crud is Entity :) ### Features - complete crud generation ### Example: ```bash root@striX: ~/project_root# bin/console make:crud SweetFood created: src/Controller/SweetFoodController.php created: src/Form/SweetFoodType.php created: templates/sweet_food/_delete_form.html.twig created: templates/sweet_food/_form.html.twig created: templates/sweet_food/index.html.twig created: templates/sweet_food/show.html.twig created: templates/sweet_food/new.html.twig created: templates/sweet_food/edit.html.twig Success! ``` Closes: #3 Commits ------- 2683114 some improvements 2ec8ff0 fix form variables 0d9e637 added validation on arguments ada7db0 fix phpcs f740e2e fix variables to follow official recommendations b3bf878 some make:form improvements + experiments 204f5aa final fix for skeleton templates 96d54a7 Merge branch 'master' of https://github.com/sadikoff/maker-bundle 38b2864 Merge remote-tracking branch 'upstream/master' ee98865 making test name less specific so we can run only it ba0438f phpcs fix 3d319bd some skeleton improvements da37780 Merge remote-tracking branch 'upstream/master' 2ebff8c fix cs and improvements according to review request 772f0db fix make:form 44b7851 completed improvement of make:form command db30634 added tests for crud testig ad8a4fb fix tests on appveyor e8eabd6 Merge remote-tracking branch 'upstream/master' 60c0005 php cs fix 404c38f update all to lastest bundle code cc4b758 Merge remote-tracking branch 'upstream/master' 1b95509 add csrf dependency it's necessary for delete action in crud and for better security 52a20bb improve make:crud command to use make:form logic for form generation e24b22c fix to use with php 7.0 7555a3a improved make:form command 4045695 removed unnecessary code 7ed6b8b improved tests 3787739 cs fix 71d2e90 some code refactoring + testing new GeneratorHelper to simplify skeleton templates 6f1c296 array style fix 96f2af2 Merge remote-tracking branch 'upstream/master' ab0a0dc Merge remote-tracking branch 'upstream/master' 422d31b Merge branch 'master' of https://github.com/sadikoff/maker-bundle ff9a08d some fixes 42047a6 fix Token parameter name in delete action 5157e0a fix interception of /new url by show action 7783313 fix fobpot.io cs 31d9e81 fix templates generation with date fields in entity 1b0f17a test remove some phpdoc comments to pass travis.ci 207d8be add exception if entity doesn't exists 478b457 fix fabpot.io cs 3 791241d fix fabpot.io cs 2 9961f43 fix fabpot.io cs f189efd fix tests 8c98b45 trying tests 2 08d4bc5 trying tests 8e9bd67 fix skeleton according to community recommendations e9f91ef add MakeCrud class to makers.xml 6cdda8f final fix fabpot.ci CS 4c0b639 fix fabpot.ci CS bf9a5c1 make:crud command initial commit
2 parents 6d59659 + 2683114 commit fb18b88

26 files changed

+1195
-20
lines changed
+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony MakerBundle package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\MakerBundle\Doctrine;
13+
14+
/**
15+
* @author Sadicov Vladimir <[email protected]>
16+
*
17+
* @internal
18+
*/
19+
final class DoctrineEntityDetails
20+
{
21+
private $repositoryClass;
22+
23+
private $identifier;
24+
private $displayFields;
25+
private $formFields;
26+
27+
public function __construct($repositoryClass, $identifier, $displayFields, $formFields)
28+
{
29+
$this->repositoryClass = $repositoryClass;
30+
$this->identifier = $identifier;
31+
$this->displayFields = $displayFields;
32+
$this->formFields = $formFields;
33+
}
34+
35+
public function getRepositoryClass()
36+
{
37+
return $this->repositoryClass;
38+
}
39+
40+
public function getIdentifier()
41+
{
42+
return $this->identifier;
43+
}
44+
45+
public function getDisplayFields()
46+
{
47+
return $this->displayFields;
48+
}
49+
50+
public function getFormFields()
51+
{
52+
return $this->formFields;
53+
}
54+
}

src/Doctrine/DoctrineEntityHelper.php

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony MakerBundle package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\MakerBundle\Doctrine;
13+
14+
use Doctrine\Common\Persistence\ManagerRegistry;
15+
use Doctrine\ORM\Mapping\ClassMetadataInfo;
16+
use Symfony\Bundle\MakerBundle\Util\ClassNameDetails;
17+
18+
/**
19+
* @author Sadicov Vladimir <[email protected]>
20+
*
21+
* @internal
22+
*/
23+
final class DoctrineEntityHelper
24+
{
25+
private $metadataFactory;
26+
27+
public function __construct(ManagerRegistry $registry = null)
28+
{
29+
$this->metadataFactory = null !== $registry ? new DoctrineMetadataFactory($registry) : null;
30+
}
31+
32+
private function isDoctrineInstalled(): bool
33+
{
34+
return null !== $this->metadataFactory;
35+
}
36+
37+
public function getEntitiesForAutocomplete(): array
38+
{
39+
$entities = [];
40+
41+
if ($this->isDoctrineInstalled()) {
42+
$allMetadata = $this->metadataFactory->getAllMetadata();
43+
/** @var ClassMetadataInfo $metadata */
44+
foreach ($allMetadata as $metadata) {
45+
$entityClassDetails = new ClassNameDetails($metadata->name, 'App\\Entity');
46+
$entities[] = $entityClassDetails->getRelativeName();
47+
}
48+
}
49+
50+
return $entities;
51+
}
52+
53+
/**
54+
* @param string $entityClassName
55+
*
56+
* @return null|DoctrineEntityDetails
57+
*
58+
* @throws \Exception
59+
*/
60+
public function createDoctrineDetails(string $entityClassName)
61+
{
62+
$metadata = $this->getEntityMetadata($entityClassName);
63+
64+
if (null !== $metadata) {
65+
return new DoctrineEntityDetails(
66+
$metadata->customRepositoryClassName,
67+
$metadata->identifier[0],
68+
$metadata->fieldMappings,
69+
$this->getFormFieldsFromEntity($metadata)
70+
);
71+
}
72+
73+
return null;
74+
}
75+
76+
public function getFormFieldsFromEntity(ClassMetadataInfo $metadata): array
77+
{
78+
$fields = (array) $metadata->fieldNames;
79+
// Remove the primary key field if it's not managed manually
80+
if (!$metadata->isIdentifierNatural()) {
81+
$fields = array_diff($fields, $metadata->identifier);
82+
}
83+
foreach ($metadata->associationMappings as $fieldName => $relation) {
84+
if (ClassMetadataInfo::ONE_TO_MANY !== $relation['type']) {
85+
$fields[] = $fieldName;
86+
}
87+
}
88+
89+
return $fields;
90+
}
91+
92+
public function getEntityMetadata($entityClassName)
93+
{
94+
if (null === $this->metadataFactory) {
95+
throw new \Exception('Somehow the doctrine service is missing. Is DoctrineBundle installed?');
96+
}
97+
98+
return $this->metadataFactory->getMetadataForClass($entityClassName);
99+
}
100+
}
+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony MakerBundle package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\MakerBundle\Doctrine;
13+
14+
use Doctrine\Common\Persistence\ManagerRegistry;
15+
use Doctrine\ORM\Mapping\ClassMetadata;
16+
use Doctrine\ORM\Tools\DisconnectedClassMetadataFactory;
17+
18+
/**
19+
* Simpler version of DoctrineBundle's DisconnectedMetadataFactory, to
20+
* avoid PSR-4 issues.
21+
*
22+
* @internal
23+
*
24+
* @author Fabien Potencier <[email protected]>
25+
* @author Ryan Weaver <[email protected]>
26+
*/
27+
final class DoctrineMetadataFactory
28+
{
29+
private $registry;
30+
31+
/**
32+
* Constructor.
33+
*
34+
* @param ManagerRegistry $registry A ManagerRegistry instance
35+
*/
36+
public function __construct(ManagerRegistry $registry)
37+
{
38+
$this->registry = $registry;
39+
}
40+
41+
/**
42+
* @param string $namespace
43+
*
44+
* @return array|ClassMetadata[]
45+
*/
46+
public function getMetadataForNamespace($namespace)
47+
{
48+
$metadata = [];
49+
foreach ($this->getAllMetadata() as $m) {
50+
if (0 === strpos($m->name, $namespace)) {
51+
$metadata[] = $m;
52+
}
53+
}
54+
55+
return $metadata;
56+
}
57+
58+
/**
59+
* @param string $entity
60+
*
61+
* @return ClassMetadata|null
62+
*/
63+
public function getMetadataForClass(string $entity)
64+
{
65+
foreach ($this->registry->getManagers() as $em) {
66+
$cmf = new DisconnectedClassMetadataFactory();
67+
$cmf->setEntityManager($em);
68+
69+
if (!$cmf->isTransient($entity)) {
70+
return $cmf->getMetadataFor($entity);
71+
}
72+
}
73+
74+
return null;
75+
}
76+
77+
/**
78+
* @return array
79+
*/
80+
public function getAllMetadata()
81+
{
82+
$metadata = [];
83+
foreach ($this->registry->getManagers() as $em) {
84+
$cmf = new DisconnectedClassMetadataFactory();
85+
$cmf->setEntityManager($em);
86+
foreach ($cmf->getAllMetadata() as $m) {
87+
$metadata[] = $m;
88+
}
89+
}
90+
91+
return $metadata;
92+
}
93+
}

src/Generator.php

+6
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,14 @@
2121
class Generator
2222
{
2323
private $fileManager;
24+
private $twigHelper;
2425
private $pendingOperations = [];
2526
private $namespacePrefix;
2627

2728
public function __construct(FileManager $fileManager, $namespacePrefix)
2829
{
2930
$this->fileManager = $fileManager;
31+
$this->twigHelper = new GeneratorTwigHelper($fileManager);
3032
$this->namespacePrefix = rtrim($namespacePrefix, '\\');
3133
}
3234

@@ -60,6 +62,10 @@ public function generateClass(string $className, string $templateName, array $va
6062
*/
6163
public function generateFile(string $targetPath, string $templateName, array $variables)
6264
{
65+
$variables = array_merge($variables, [
66+
'helper' => $this->twigHelper,
67+
]);
68+
6369
$this->addOperation($targetPath, $templateName, $variables);
6470
}
6571

src/GeneratorTwigHelper.php

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony MakerBundle package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\MakerBundle;
13+
14+
/**
15+
* @author Sadicov Vladimir <[email protected]>
16+
*/
17+
final class GeneratorTwigHelper
18+
{
19+
private $fileManager;
20+
21+
public function __construct(FileManager $fileManager)
22+
{
23+
$this->fileManager = $fileManager;
24+
}
25+
26+
public function getEntityFieldPrintCode($entity, $field): string
27+
{
28+
$printCode = $entity.'.'.$field['fieldName'];
29+
30+
switch ($field['type']) {
31+
case 'datetime':
32+
$printCode .= ' ? '.$printCode.'|date(\'Y-m-d H:i:s\') : \'\'';
33+
break;
34+
case 'date':
35+
$printCode .= ' ? '.$printCode.'|date(\'Y-m-d\') : \'\'';
36+
break;
37+
case 'time':
38+
$printCode .= ' ? '.$printCode.'|date(\'H:i:s\') : \'\'';
39+
break;
40+
case 'array':
41+
$printCode .= ' ? '.$printCode.'|join(\', \') : \'\'';
42+
break;
43+
case 'boolean':
44+
$printCode .= ' ? \'Yes\' : \'No\'';
45+
break;
46+
}
47+
48+
return $printCode;
49+
}
50+
51+
public function getHeadPrintCode($title): string
52+
{
53+
if ($this->fileManager->fileExists('templates/base.html.twig')) {
54+
return <<<TWIG
55+
{% extends 'base.html.twig' %}
56+
57+
{% block title %}$title{% endblock %}
58+
59+
TWIG;
60+
}
61+
62+
return <<<HTML
63+
<!DOCTYPE html>
64+
65+
<title>$title</title>
66+
67+
HTML;
68+
}
69+
}

0 commit comments

Comments
 (0)