diff --git a/src/Bridges/FormsLatte/FormMacros.php b/src/Bridges/FormsLatte/FormMacros.php index 6db574965..fad962d82 100644 --- a/src/Bridges/FormsLatte/FormMacros.php +++ b/src/Bridges/FormsLatte/FormMacros.php @@ -322,7 +322,7 @@ public function macroFormPrint(MacroNode $node, PhpWriter $writer) $node->tokenizer->reset(); return $writer->write( - 'Nette\Bridges\FormsLatte\Runtime::render' . $node->name . '(' + 'Nette\Forms\Rendering\Blueprint::' . ($node->name === 'formPrint' ? 'latte' : 'dataClass') . '(' . ($name[0] === '$' ? 'is_object($ʟ_tmp = %node.word) ? $ʟ_tmp : $this->global->uiControl[$ʟ_tmp]' : '$this->global->uiControl[%node.word]') diff --git a/src/Bridges/FormsLatte/Nodes/FormPrintNode.php b/src/Bridges/FormsLatte/Nodes/FormPrintNode.php index 427beedbb..ae9221f94 100644 --- a/src/Bridges/FormsLatte/Nodes/FormPrintNode.php +++ b/src/Bridges/FormsLatte/Nodes/FormPrintNode.php @@ -39,12 +39,12 @@ public static function create(Tag $tag): static public function print(PrintContext $context): string { return $context->format( - 'Nette\Bridges\FormsLatte\Runtime::render%raw(' + 'Nette\Forms\Rendering\Blueprint::%raw(' . ($this->name ? 'is_object($ʟ_tmp = %node) ? $ʟ_tmp : $this->global->uiControl[$ʟ_tmp]' : 'end($this->global->formsStack)') . ') %2.line; exit;', - $this->mode, + $this->mode === 'formPrint' ? 'latte' : 'dataClass', $this->name, $this->position, ); diff --git a/src/Bridges/FormsLatte/Runtime.php b/src/Bridges/FormsLatte/Runtime.php index 4eee9413f..744acf54f 100644 --- a/src/Bridges/FormsLatte/Runtime.php +++ b/src/Bridges/FormsLatte/Runtime.php @@ -9,7 +9,6 @@ namespace Nette\Bridges\FormsLatte; -use Latte; use Nette; use Nette\Forms\Form; use Nette\Utils\Html; @@ -76,42 +75,6 @@ public static function renderFormEnd(Form $form, bool $withTags = true): string } - /** - * Generates blueprint of form. - */ - public static function renderFormPrint(Form $form): void - { - $blueprint = class_exists(Latte\Runtime\Blueprint::class) - ? new Latte\Runtime\Blueprint - : new Latte\Essential\Blueprint; - $end = $blueprint->printCanvas(); - $blueprint->printHeader('Form ' . $form->getName()); - $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 = class_exists(Latte\Runtime\Blueprint::class) - ? new Latte\Runtime\Blueprint - : new Latte\Essential\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; - } - - public static function item($item, $global): object { if (is_object($item)) { diff --git a/src/Forms/Rendering/Blueprint.php b/src/Forms/Rendering/Blueprint.php new file mode 100644 index 000000000..c5477bfd5 --- /dev/null +++ b/src/Forms/Rendering/Blueprint.php @@ -0,0 +1,238 @@ +printBegin(); + $bp->printHeader('Form ' . $form->getName()); + $bp->printCode($bp->generateLatte($form), 'latte'); + $bp->printEnd(); + if ($exit) { + exit; + } + } + + + /** + * Generates blueprint of form data class. + */ + public static function dataClass(Form $form, bool $exit = true): void + { + $bp = new self; + $bp->printBegin(); + $bp->printHeader('Form Data Class ' . $form->getName()); + $bp->printCode($bp->generateDataClass($form), 'php'); + if (PHP_VERSION_ID >= 80000) { + $bp->printCode($bp->generateDataClass($form, true), 'php'); + } + $bp->printEnd(); + if ($exit) { + exit; + } + } + + + private function printBegin(): void + { + echo ''; + echo ''; + echo "
',
+ htmlspecialchars($code),
+ "
\n";
+ }
+
+
+ public function generateLatte(Form $form): string
+ {
+ $dict = new \SplObjectStorage;
+ $dummyForm = new class extends Form {
+ protected function receiveHttpData(): ?array
+ {
+ return [];
+ }
+ };
+
+ foreach ($form->getControls() as $input) {
+ $dict[$input] = $dummyInput = new class extends Nette\Forms\Controls\BaseControl {
+ public $inner;
+
+
+ public function getLabel($caption = null)
+ {
+ return $this->inner->getLabel()
+ ? '{label ' . $this->inner->lookupPath(Form::class) . '/}'
+ : null;
+ }
+
+
+ public function getControl()
+ {
+ return '{input ' . $this->inner->lookupPath(Form::class) . '}';
+ }
+
+
+ public function isRequired(): bool
+ {
+ return $this->inner->isRequired();
+ }
+
+
+ public function getOption($key)
+ {
+ return $key === 'rendered'
+ ? parent::getOption($key)
+ : $this->inner->getOption($key);
+ }
+ };
+ $dummyInput->inner = $input;
+ $dummyForm->addComponent($dummyInput, (string) $dict->count());
+ $dummyInput->addError('{inputError ' . $input->lookupPath(Form::class) . '}');
+ }
+
+ foreach ($form->getGroups() as $group) {
+ $dummyGroup = $dummyForm->addGroup();
+ foreach ($group->getOptions() as $k => $v) {
+ $dummyGroup->setOption($k, $v);
+ }
+
+ foreach ($group->getControls() as $control) {
+ if ($dict[$control]) {
+ $dummyGroup->add($dict[$control]);
+ }
+ }
+ }
+
+ $renderer = clone $form->getRenderer();
+ $dummyForm->setRenderer($renderer);
+ $dummyForm->onRender = $form->onRender;
+ $dummyForm->fireRenderEvents();
+
+ if ($renderer instanceof DefaultFormRenderer) {
+ $renderer->wrappers['error']['container'] = $renderer->getWrapper('error container')->setAttribute('n:ifcontent', true);
+ $renderer->wrappers['error']['item'] = $renderer->getWrapper('error item')->setAttribute('n:foreach', '$form->getOwnErrors() as $error');
+ $renderer->wrappers['control']['errorcontainer'] = $renderer->getWrapper('control errorcontainer')->setAttribute('n:ifcontent', true);
+ $dummyForm->addError('{$error}');
+
+ ob_start();
+ $dummyForm->render('end');
+ $end = ob_get_clean();
+ }
+
+ ob_start();
+ $dummyForm->render();
+ $body = ob_get_clean();
+
+ $body = str_replace($dummyForm->getElementPrototype()->startTag(), '', $body);
+ return $body;
+ }
+
+
+ public function generateDataClass(
+ Container $container,
+ ?bool $propertyPromotion = false,
+ ?string $baseName = null
+ ): string
+ {
+ $baseName = $baseName ?? preg_replace('~Form$~', '', ucwords((string) $container->getName()));
+ $nextCode = '';
+ $props = [];
+ foreach ($container->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 = $baseName . ucwords($name);
+ $nextCode .= $this->generateDataClass($input, $propertyPromotion, $type);
+ $type .= self::ClassNameSuffix;
+ } else {
+ $type = '';
+ }
+
+ $props[] = 'public ' . ($type ? $type . ' ' : '') . '$' . $name;
+ }
+
+ $class = $baseName . self::ClassNameSuffix;
+ return "class $class\n"
+ . "{\n"
+ . ($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"
+ . $nextCode;
+ }
+}
diff --git a/src/Forms/Rendering/DataClassGenerator.php b/src/Forms/Rendering/DataClassGenerator.php
index 2c4f5f60f..fdd06aa13 100644
--- a/src/Forms/Rendering/DataClassGenerator.php
+++ b/src/Forms/Rendering/DataClassGenerator.php
@@ -9,13 +9,12 @@
namespace Nette\Forms\Rendering;
-use Nette\Forms\Container;
-use Nette\Forms\Controls;
use Nette\Forms\Form;
/**
* Generates blueprint of form data class.
+ * @deprecated
*/
final class DataClassGenerator
{
@@ -31,66 +30,7 @@ final class DataClassGenerator
public function generateCode(Form $form, ?string $baseName = null): string
{
- $baseName = $baseName ?? preg_replace('~Form$~', '', ucwords((string) $form->getName()));
- return $this->processContainer($form, $baseName);
- }
-
-
- private function processContainer(Container $container, string $baseName): string
- {
- $nextCode = '';
- $props = [];
- foreach ($container->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 = $baseName . ucwords($name);
- $nextCode .= $this->processContainer($input, $type);
- $type .= $this->classNameSuffix;
- } else {
- $type = '';
- }
-
- $props[] = 'public ' . ($type ? $type . ' ' : '') . '$' . $name;
- }
-
- $class = $baseName . $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"
- . $nextCode;
+ trigger_error(__METHOD__ . '() is deprecated, use ' . Blueprint::class . '::dataClass()', E_USER_DEPRECATED);
+ return (new Blueprint)->generateDataClass($form, $this->propertyPromotion, $baseName);
}
}
diff --git a/src/Forms/Rendering/LatteRenderer.php b/src/Forms/Rendering/LatteRenderer.php
index b800e9063..1c716f5ee 100644
--- a/src/Forms/Rendering/LatteRenderer.php
+++ b/src/Forms/Rendering/LatteRenderer.php
@@ -9,97 +9,18 @@
namespace Nette\Forms\Rendering;
-use Nette;
use Nette\Forms\Form;
/**
* Generates Latte blueprint of form.
+ * @deprecated
*/
final class LatteRenderer
{
public function render(Form $form): string
{
- $dict = new \SplObjectStorage;
- $dummyForm = new class extends Form {
- protected function receiveHttpData(): ?array
- {
- return [];
- }
- };
-
- foreach ($form->getControls() as $name => $input) {
- $dict[$input] = $dummyInput = new class extends Nette\Forms\Controls\BaseControl {
- public $inner;
-
-
- public function getLabel($name = null)
- {
- return $this->inner->getLabel()
- ? '{label ' . $this->inner->lookupPath(Form::class) . '/}'
- : null;
- }
-
-
- public function getControl()
- {
- return '{input ' . $this->inner->lookupPath(Form::class) . '}';
- }
-
-
- public function isRequired(): bool
- {
- return $this->inner->isRequired();
- }
-
-
- public function getOption($key)
- {
- return $key === 'rendered'
- ? parent::getOption($key)
- : $this->inner->getOption($key);
- }
- };
- $dummyInput->inner = $input;
- $dummyForm->addComponent($dummyInput, (string) $dict->count());
- $dummyInput->addError('{inputError ' . $input->lookupPath(Form::class) . '}');
- }
-
- foreach ($form->getGroups() as $group) {
- $dummyGroup = $dummyForm->addGroup();
- foreach ($group->getOptions() as $k => $v) {
- $dummyGroup->setOption($k, $v);
- }
-
- foreach ($group->getControls() as $control) {
- if ($dict[$control]) {
- $dummyGroup->add($dict[$control]);
- }
- }
- }
-
- $renderer = clone $form->getRenderer();
- $dummyForm->setRenderer($renderer);
- $dummyForm->onRender = $form->onRender;
- $dummyForm->fireRenderEvents();
-
- if ($renderer instanceof DefaultFormRenderer) {
- $renderer->wrappers['error']['container'] = $renderer->getWrapper('error container')->setAttribute('n:ifcontent', true);
- $renderer->wrappers['error']['item'] = $renderer->getWrapper('error item')->setAttribute('n:foreach', '$form->getOwnErrors() as $error');
- $renderer->wrappers['control']['errorcontainer'] = $renderer->getWrapper('control errorcontainer')->setAttribute('n:ifcontent', true);
- $dummyForm->addError('{$error}');
-
- ob_start();
- $dummyForm->render('end');
- $end = ob_get_clean();
- }
-
- ob_start();
- $dummyForm->render();
- $body = ob_get_clean();
-
- $body = str_replace($dummyForm->getElementPrototype()->startTag(), '', $body);
- return $body;
+ trigger_error(__METHOD__ . '() is deprecated, use ' . Blueprint::class . '::latte()', E_USER_DEPRECATED);
+ return (new Blueprint)->generateLatte($form);
}
}
diff --git a/tests/Forms.Latte3/Runtime.renderFormClassPrint.phpt b/tests/Forms.Latte3/Runtime.renderFormClassPrint.phpt
deleted file mode 100644
index eee743057..000000000
--- a/tests/Forms.Latte3/Runtime.renderFormClassPrint.phpt
+++ /dev/null
@@ -1,43 +0,0 @@
-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,
-);
-
-Assert::match(
- '%A%class SignFormData
-{
- use \Nette\SmartObject;
-
- public function __construct(
- public string $name,
- ) {
- }
-}
-%A%',
- $res,
-);
diff --git a/tests/Forms.Latte3/Runtime.renderFormPrint.phpt b/tests/Forms.Latte3/Runtime.renderFormPrint.phpt
deleted file mode 100644
index 832275444..000000000
--- a/tests/Forms.Latte3/Runtime.renderFormPrint.phpt
+++ /dev/null
@@ -1,51 +0,0 @@
-addGroup('Personal data');
-$form->addText('name')->setRequired('Enter your name');
-$form->addSubmit('submit', 'Send');
-
-ob_start();
-Nette\Bridges\FormsLatte\Runtime::renderFormPrint($form);
-$res = ob_get_clean();
-
-Assert::match(
- '%A%%A%',
- html_entity_decode($res),
-);
diff --git a/tests/Forms/DataClassGenerator.phpt b/tests/Forms/Blueprint.dataClass.phpt
similarity index 76%
rename from tests/Forms/DataClassGenerator.phpt
rename to tests/Forms/Blueprint.dataClass.phpt
index 04d1e9acb..f58704de5 100644
--- a/tests/Forms/DataClassGenerator.phpt
+++ b/tests/Forms/Blueprint.dataClass.phpt
@@ -3,6 +3,7 @@
declare(strict_types=1);
use Nette\Forms\Form;
+use Nette\Forms\Rendering\Blueprint;
use Tester\Assert;
@@ -18,14 +19,11 @@ $form->addHidden('id');
$form->addCheckbox('agree');
$form->addSubmit('submit', 'Send');
-$generator = new Nette\Forms\Rendering\DataClassGenerator;
-$res = $generator->generateCode($form);
+$res = (new Blueprint)->generateDataClass($form);
Assert::match(
'class SignFormData
{
- use \Nette\SmartObject;
-
public string $name;
public ?int $age;
public SignContFormData $cont;
@@ -35,17 +33,13 @@ Assert::match(
class SignContFormData
{
- use \Nette\SmartObject;
-
public ?string $name;
}
',
$res
);
-$generator->propertyPromotion = true;
-$generator->useSmartObject = false;
-$res = $generator->generateCode($form);
+$res = (new Blueprint)->generateDataClass($form, true);
Assert::match(
'class SignFormData
diff --git a/tests/Forms/LatteRenderer.phpt b/tests/Forms/Blueprint.latte.phpt
similarity index 94%
rename from tests/Forms/LatteRenderer.phpt
rename to tests/Forms/Blueprint.latte.phpt
index c653be0a0..bf601b1d1 100644
--- a/tests/Forms/LatteRenderer.phpt
+++ b/tests/Forms/Blueprint.latte.phpt
@@ -3,6 +3,7 @@
declare(strict_types=1);
use Nette\Forms\Form;
+use Nette\Forms\Rendering\Blueprint;
use Nette\Utils\Html;
use Tester\Assert;
@@ -33,8 +34,7 @@ $form->onRender[] = function ($form) {
$renderer->wrappers['label']['suffix'] = ':';
};
-$renderer = new Nette\Forms\Rendering\LatteRenderer;
-$res = $renderer->render($form);
+$res = (new Blueprint)->generateLatte($form);
Assert::match(
'