From 1d39b938ea5c32ebebe6625f34c392b9a6bbbd31 Mon Sep 17 00:00:00 2001 From: Clemens Krack Date: Tue, 23 Oct 2018 17:09:32 +0200 Subject: [PATCH 1/7] Modify make:form to allow non-entities Before this modification the make:form command allowed passing a fully specified classname. It would generate a form with the correct bound class, but not read the fields. This also affects DTOs. This modification makes our lives easier by reading all fields of the bound class. --- src/Maker/MakeForm.php | 4 ++++ src/Util/ClassDetails.php | 45 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 src/Util/ClassDetails.php diff --git a/src/Maker/MakeForm.php b/src/Maker/MakeForm.php index a5d36ab68..f06a51524 100644 --- a/src/Maker/MakeForm.php +++ b/src/Maker/MakeForm.php @@ -19,6 +19,7 @@ use Symfony\Bundle\MakerBundle\InputConfiguration; use Symfony\Bundle\MakerBundle\Str; use Symfony\Bundle\MakerBundle\Validator; +use Symfony\Bundle\MakerBundle\Util\ClassDetails; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -95,6 +96,9 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen if (null !== $doctrineEntityDetails) { $formFields = $doctrineEntityDetails->getFormFields(); + } else { + $classDetails = new ClassDetails($boundClassDetails->getFullName()); + $formFields = $classDetails->getFormFields(); } $boundClassVars = [ diff --git a/src/Util/ClassDetails.php b/src/Util/ClassDetails.php new file mode 100644 index 000000000..222993426 --- /dev/null +++ b/src/Util/ClassDetails.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\MakerBundle\Util; + +use Symfony\Bundle\MakerBundle\Str; + +final class ClassDetails +{ + private $fullClassName; + + public function __construct(string $fullClassName) + { + $this->fullClassName = $fullClassName; + } + + public function getProperties():? array + { + $reflect = new \ReflectionClass($this->fullClassName); + $props = $reflect->getProperties(); + + $propertiesList = []; + + foreach ($props as $prop) { + $propertiesList[] = $prop->getName(); + } + + return $propertiesList; + } + + public function getFormFields() + { + $properties = $this->getProperties(); + + return array_diff($properties,['id']); + } +} From 6c36259485a60c83c5244e4e3ac10dc836ba763c Mon Sep 17 00:00:00 2001 From: Clemens Krack Date: Tue, 23 Oct 2018 17:11:30 +0200 Subject: [PATCH 2/7] Add missing space after comma --- src/Util/ClassDetails.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Util/ClassDetails.php b/src/Util/ClassDetails.php index 222993426..17bec3923 100644 --- a/src/Util/ClassDetails.php +++ b/src/Util/ClassDetails.php @@ -40,6 +40,6 @@ public function getFormFields() { $properties = $this->getProperties(); - return array_diff($properties,['id']); + return array_diff($properties, ['id']); } } From 597baf9aae1ec55236790b68c826a6bc5e7fd76c Mon Sep 17 00:00:00 2001 From: Clemens Krack Date: Tue, 23 Oct 2018 23:34:13 +0200 Subject: [PATCH 3/7] Cleanup code Make method private, Mark class as internal. Add DocComment to public method. Remove copied use statement. --- src/Util/ClassDetails.php | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/Util/ClassDetails.php b/src/Util/ClassDetails.php index 17bec3923..e99230469 100644 --- a/src/Util/ClassDetails.php +++ b/src/Util/ClassDetails.php @@ -11,8 +11,9 @@ namespace Symfony\Bundle\MakerBundle\Util; -use Symfony\Bundle\MakerBundle\Str; - +/** + * @internal + */ final class ClassDetails { private $fullClassName; @@ -22,7 +23,19 @@ public function __construct(string $fullClassName) $this->fullClassName = $fullClassName; } - public function getProperties():? array + /** + * Get list of property names except "id" for use in a make:form context + * + * @return array|null + */ + public function getFormFields(): array + { + $properties = $this->getProperties(); + + return array_diff($properties, ['id']); + } + + private function getProperties(): array { $reflect = new \ReflectionClass($this->fullClassName); $props = $reflect->getProperties(); @@ -35,11 +48,4 @@ public function getProperties():? array return $propertiesList; } - - public function getFormFields() - { - $properties = $this->getProperties(); - - return array_diff($properties, ['id']); - } } From 0145a928085dcd43a6e4011a98b405de5c4ffd7b Mon Sep 17 00:00:00 2001 From: Clemens Krack Date: Thu, 25 Oct 2018 18:25:27 +0200 Subject: [PATCH 4/7] Run php-cs-fixer --- src/Maker/MakeForm.php | 2 +- src/Util/ClassDetails.php | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Maker/MakeForm.php b/src/Maker/MakeForm.php index f06a51524..616d121fb 100644 --- a/src/Maker/MakeForm.php +++ b/src/Maker/MakeForm.php @@ -18,8 +18,8 @@ use Symfony\Bundle\MakerBundle\Generator; use Symfony\Bundle\MakerBundle\InputConfiguration; use Symfony\Bundle\MakerBundle\Str; -use Symfony\Bundle\MakerBundle\Validator; use Symfony\Bundle\MakerBundle\Util\ClassDetails; +use Symfony\Bundle\MakerBundle\Validator; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; diff --git a/src/Util/ClassDetails.php b/src/Util/ClassDetails.php index e99230469..6d326f85f 100644 --- a/src/Util/ClassDetails.php +++ b/src/Util/ClassDetails.php @@ -23,19 +23,19 @@ public function __construct(string $fullClassName) $this->fullClassName = $fullClassName; } - /** - * Get list of property names except "id" for use in a make:form context - * - * @return array|null - */ - public function getFormFields(): array + /** + * Get list of property names except "id" for use in a make:form context. + * + * @return array|null + */ + public function getFormFields(): array { $properties = $this->getProperties(); return array_diff($properties, ['id']); - } + } - private function getProperties(): array + private function getProperties(): array { $reflect = new \ReflectionClass($this->fullClassName); $props = $reflect->getProperties(); From 2d8b256853b8e8c206ca568eb09b2de6e6ff406b Mon Sep 17 00:00:00 2001 From: Clemens Krack Date: Mon, 29 Oct 2018 22:12:28 +0100 Subject: [PATCH 5/7] Add test for non-entity form generation --- src/Maker/MakeForm.php | 2 +- tests/Maker/FunctionalTest.php | 10 +++ .../src/Form/Data/TaskData.php | 69 +++++++++++++++++++ .../tests/GeneratedFormTest.php | 49 +++++++++++++ 4 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 tests/fixtures/MakeFormForNonEntityDto/src/Form/Data/TaskData.php create mode 100644 tests/fixtures/MakeFormForNonEntityDto/tests/GeneratedFormTest.php diff --git a/src/Maker/MakeForm.php b/src/Maker/MakeForm.php index 616d121fb..09b4eac8d 100644 --- a/src/Maker/MakeForm.php +++ b/src/Maker/MakeForm.php @@ -96,7 +96,7 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen if (null !== $doctrineEntityDetails) { $formFields = $doctrineEntityDetails->getFormFields(); - } else { + } else { $classDetails = new ClassDetails($boundClassDetails->getFullName()); $formFields = $classDetails->getFormFields(); } diff --git a/tests/Maker/FunctionalTest.php b/tests/Maker/FunctionalTest.php index af1038973..a7702c6bb 100644 --- a/tests/Maker/FunctionalTest.php +++ b/tests/Maker/FunctionalTest.php @@ -188,6 +188,16 @@ public function getCommandTests() ]) ->addExtraDependencies('orm') ->setFixtureFilesPath(__DIR__.'/../fixtures/MakeFormForEntity') + ]; + + yield 'form_for_non_entity_dto' => [MakerTestDetails::createTest( + $this->getMakerInstance(MakeForm::class), + [ + // Entity name + 'TaskType', + '\\App\\Form\\Data\\TaskData', + ]) + ->setFixtureFilesPath(__DIR__.'/../fixtures/MakeFormForNonEntityDto') ]; yield 'form_for_sti_entity' => [MakerTestDetails::createTest( diff --git a/tests/fixtures/MakeFormForNonEntityDto/src/Form/Data/TaskData.php b/tests/fixtures/MakeFormForNonEntityDto/src/Form/Data/TaskData.php new file mode 100644 index 000000000..c30770976 --- /dev/null +++ b/tests/fixtures/MakeFormForNonEntityDto/src/Form/Data/TaskData.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace App\Form\Data; + +use App\Entity\Task; +use Symfony\Component\Validator\Constraints as Assert; + +/** + * Data transfer object for Task. + * Add your constraints as annotations to the properties. + */ +class TaskData +{ + /** + * @Assert\NotBlank() + */ + public $task; + + public $dueDate; + + /** + * Create DTO, optionally extracting data from a model. + * + * @param Task|null $task + */ + public function __construct(? Task $task = null) + { + if ($task instanceof Task) { + $this->extract($task); + } + } + + /** + * Fill entity with data from the DTO. + * + * @param Task $task + */ + public function fill(Task $task): Task + { + $task + ->setTask($this->task) + ->setDueDate($this->dueDate) + ; + + return $task; + } + + /** + * Extract data from entity into the DTO. + * + * @param Task $task + */ + public function extract(Task $task): self + { + $this->task = $task->getTask(); + $this->dueDate = $task->getDueDate(); + + return $this; + } +} diff --git a/tests/fixtures/MakeFormForNonEntityDto/tests/GeneratedFormTest.php b/tests/fixtures/MakeFormForNonEntityDto/tests/GeneratedFormTest.php new file mode 100644 index 000000000..31ace6937 --- /dev/null +++ b/tests/fixtures/MakeFormForNonEntityDto/tests/GeneratedFormTest.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace App\Tests; + +use App\Form\Data\TaskData; +use App\Form\TaskType; +use Symfony\Component\Form\Test\TypeTestCase; + +class GeneratedFormTest extends TypeTestCase +{ + public function testGeneratedForm() + { + $dateTimeObject = new \DateTime(); + + $formData = [ + 'task' => 'Acme', + 'dueDate' => $dateTimeObject, + ]; + + $objectToCompare = new TaskData(); + + $form = $this->factory->create(TaskType::class, $objectToCompare); + $form->submit($formData); + + $object = new TaskData(); + $object->task = 'Acme'; + $object->dueDate = $dateTimeObject; + + $this->assertTrue($form->isSynchronized()); + $this->assertEquals($object, $objectToCompare); + $this->assertEquals($object, $form->getData()); + + $view = $form->createView(); + $children = $view->children; + + foreach (array_keys($formData) as $key) { + $this->assertArrayHasKey($key, $children); + } + } +} From 01e78592d608bdfc3676e0db6248ca5b190720ba Mon Sep 17 00:00:00 2001 From: Clemens Krack Date: Mon, 29 Oct 2018 22:23:43 +0100 Subject: [PATCH 6/7] Fix spaces --- src/Maker/MakeForm.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Maker/MakeForm.php b/src/Maker/MakeForm.php index 09b4eac8d..616d121fb 100644 --- a/src/Maker/MakeForm.php +++ b/src/Maker/MakeForm.php @@ -96,7 +96,7 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen if (null !== $doctrineEntityDetails) { $formFields = $doctrineEntityDetails->getFormFields(); - } else { + } else { $classDetails = new ClassDetails($boundClassDetails->getFullName()); $formFields = $classDetails->getFormFields(); } From da8f0e04d124d87d4846067224eaba6d068efa3c Mon Sep 17 00:00:00 2001 From: Clemens Krack Date: Mon, 29 Oct 2018 22:24:02 +0100 Subject: [PATCH 7/7] Simplify DTO and make it PHP 7.0 compatible --- .../src/Form/Data/TaskData.php | 47 ------------------- 1 file changed, 47 deletions(-) diff --git a/tests/fixtures/MakeFormForNonEntityDto/src/Form/Data/TaskData.php b/tests/fixtures/MakeFormForNonEntityDto/src/Form/Data/TaskData.php index c30770976..14b791313 100644 --- a/tests/fixtures/MakeFormForNonEntityDto/src/Form/Data/TaskData.php +++ b/tests/fixtures/MakeFormForNonEntityDto/src/Form/Data/TaskData.php @@ -11,59 +11,12 @@ namespace App\Form\Data; -use App\Entity\Task; -use Symfony\Component\Validator\Constraints as Assert; - /** * Data transfer object for Task. - * Add your constraints as annotations to the properties. */ class TaskData { - /** - * @Assert\NotBlank() - */ public $task; public $dueDate; - - /** - * Create DTO, optionally extracting data from a model. - * - * @param Task|null $task - */ - public function __construct(? Task $task = null) - { - if ($task instanceof Task) { - $this->extract($task); - } - } - - /** - * Fill entity with data from the DTO. - * - * @param Task $task - */ - public function fill(Task $task): Task - { - $task - ->setTask($this->task) - ->setDueDate($this->dueDate) - ; - - return $task; - } - - /** - * Extract data from entity into the DTO. - * - * @param Task $task - */ - public function extract(Task $task): self - { - $this->task = $task->getTask(); - $this->dueDate = $task->getDueDate(); - - return $this; - } }