Skip to content

Commit

Permalink
added DataClassGenerator & tag {formClassPrint}
Browse files Browse the repository at this point in the history
  • Loading branch information
dg committed Nov 8, 2021
1 parent dd38933 commit 31e7c7f
Show file tree
Hide file tree
Showing 5 changed files with 236 additions and 2 deletions.
6 changes: 4 additions & 2 deletions src/Bridges/FormsLatte/FormMacros.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public static function install(Latte\Compiler $compiler): void
$me->addMacro('name', [$me, 'macroName'], [$me, 'macroNameEnd'], [$me, 'macroNameAttr']);
$me->addMacro('inputError', [$me, 'macroInputError']);
$me->addMacro('formPrint', [$me, 'macroFormPrint']);
$me->addMacro('formClassPrint', [$me, 'macroFormPrint']);
}


Expand Down Expand Up @@ -281,7 +282,8 @@ public function macroInputError(MacroNode $node, PhpWriter $writer)


/**
* {formPrint [ClassName]}
* {formPrint ClassName}
* {formClassPrint ClassName}
*/
public function macroFormPrint(MacroNode $node, PhpWriter $writer)
{
Expand All @@ -291,7 +293,7 @@ public function macroFormPrint(MacroNode $node, PhpWriter $writer)
}
$node->tokenizer->reset();
return $writer->write(
'Nette\Bridges\FormsLatte\Runtime::renderBlueprint('
'Nette\Bridges\FormsLatte\Runtime::render' . $node->name . '('
. ($name[0] === '$' ? 'is_object(%node.word) ? %node.word : ' : '')
. '$this->global->uiControl[%node.word]); exit;'
);
Expand Down
18 changes: 18 additions & 0 deletions src/Bridges/FormsLatte/Runtime.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,22 @@ public static function renderBlueprint(Form $form): void
$blueprint->printCode((new Nette\Forms\Rendering\LatteRenderer)->render($form), 'latte');
echo $end;
}


/**
* Generates blueprint of form data class.
*/
public static function renderFormClassPrint(Form $form): void
{
$blueprint = new Latte\Runtime\Blueprint;
$end = $blueprint->printCanvas();
$blueprint->printHeader('Form Data Class ' . $form->getName());
$generator = new Nette\Forms\Rendering\DataClassGenerator;
$blueprint->printCode($generator->generateCode($form));
if (PHP_VERSION_ID >= 80000) {
$generator->propertyPromotion = true;
$blueprint->printCode($generator->generateCode($form));
}
echo $end;
}
}
98 changes: 98 additions & 0 deletions src/Forms/Rendering/DataClassGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<?php

/**
* This file is part of the Nette Framework (https://nette.org)
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
*/

declare(strict_types=1);

namespace Nette\Forms\Rendering;

use Latte;
use Nette\Forms\Container;
use Nette\Forms\Controls;
use Nette\Forms\Form;


/**
* Generates blueprint of form data class.
*/
final class DataClassGenerator
{
/** @var string */
public $classNameSuffix = 'FormData';

/** @var string */
public $baseClassName;

/** @var bool */
public $propertyPromotion = false;

/** @var bool */
public $useSmartObject = true;


public function generateCode(Form $form): string
{
$baseClassName = $this->baseClassName ?? preg_replace('~Form$~', '', ucwords((string) $form->getName()));
return $this->generateClass($form, $baseClassName);
}


private function generateClass(Container $form, string $baseClass): string
{
$code = '';
$props = [];
foreach ($form->getComponents() as $name => $input) {
if ($input instanceof Controls\BaseControl && $input->isOmitted()) {
continue;
} elseif ($input instanceof Controls\Checkbox) {
$type = 'bool';
} elseif ($input instanceof Controls\MultiChoiceControl) {
$type = 'array';
} elseif ($input instanceof Controls\ChoiceControl) {
$type = 'string|int';
if (!$input->isRequired()) {
$type .= '|null';
}
} elseif ($input instanceof Controls\HiddenField || $input instanceof Controls\TextBase) {
$type = 'string';
foreach ($input->getRules() as $rule) {
if ($rule->validator === Form::INTEGER) {
$type = 'int';
break;
}
}
if (!$input->isRequired()) {
$type = '?' . $type;
}
} elseif ($input instanceof Controls\UploadControl) {
$type = '\Nette\Http\FileUpload';
if (!$input->isRequired()) {
$type = '?' . $type;
}
} elseif ($input instanceof Container) {
$type = $baseClass . ucwords($name);
$code .= self::generateClass($input, $type);
$type .= $this->classNameSuffix;
} else {
$type = '';
}

$props[] = 'public ' . ($type ? $type . ' ' : '') . '$' . $name;
}

$class = $baseClass . $this->classNameSuffix;
return "class $class\n"
. "{\n"
. ($this->useSmartObject ? "\tuse \\Nette\\SmartObject;\n\n" : '')
. ($this->propertyPromotion
? "\tpublic function __construct(\n"
. ($props ? "\t\t" . implode(",\n\t\t", $props) . ",\n" : '')
. "\t) {\n\t}\n"
: ($props ? "\t" . implode(";\n\t", $props) . ";\n" : '')
)
. "}\n\n" . $code;
}
}
44 changes: 44 additions & 0 deletions tests/Forms.Latte/Runtime.renderFormClassPrint.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

declare(strict_types=1);

use Nette\Forms\Form;
use Tester\Assert;


require __DIR__ . '/../bootstrap.php';


$form = new Form('signForm');
$form->addText('name')->setRequired();

ob_start();
Nette\Bridges\FormsLatte\Runtime::renderFormClassPrint($form);
$res = ob_get_clean();

Assert::match(
'%A%class SignFormData
{
use \Nette\SmartObject;
public string $name;
}
%A%',
$res
);

if (PHP_VERSION_ID >= 80000) {
Assert::match(
'%A%class SignFormData
{
use \Nette\SmartObject;
public function __construct(
public string $name,
) {
}
}
%A%',
$res
);
}
72 changes: 72 additions & 0 deletions tests/Forms/DataClassGenerator.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

declare(strict_types=1);

use Nette\Forms\Form;
use Tester\Assert;


require __DIR__ . '/../bootstrap.php';


$form = new Form('signForm');
$form->addText('name')->setRequired();
$form->addInteger('age');
$form->addContainer('cont')
->addText('name');
$form->addHidden('id');
$form->addCheckbox('agree');
$form->addSubmit('submit', 'Send');

$generator = new Nette\Forms\Rendering\DataClassGenerator;
$res = $generator->generateCode($form);

Assert::match(
'class SignFormData
{
use \Nette\SmartObject;
public string $name;
public ?int $age;
public SignContFormData $cont;
public ?string $id;
public bool $agree;
}
class SignContFormData
{
use \Nette\SmartObject;
public ?string $name;
}
',
$res
);

$generator->propertyPromotion = true;
$generator->useSmartObject = false;
$res = $generator->generateCode($form);

Assert::match(
'class SignFormData
{
public function __construct(
public string $name,
public ?int $age,
public SignContFormData $cont,
public ?string $id,
public bool $agree,
) {
}
}
class SignContFormData
{
public function __construct(
public ?string $name,
) {
}
}
',
$res
);

0 comments on commit 31e7c7f

Please sign in to comment.