Skip to content

Commit 53b85ac

Browse files
committed
FormMacros: added {formPrint $form}
1 parent fd5d69d commit 53b85ac

File tree

5 files changed

+201
-2
lines changed

5 files changed

+201
-2
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
"require-dev": {
2424
"nette/di": "^3.0",
2525
"nette/tester": "^2.0",
26-
"latte/latte": "^2.4.1",
26+
"latte/latte": "^2.7",
2727
"tracy/tracy": "^2.4",
2828
"phpstan/phpstan-nette": "^0.12"
2929
},

src/Bridges/FormsLatte/FormMacros.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public static function install(Latte\Compiler $compiler): void
3636
$me->addMacro('input', [$me, 'macroInput']);
3737
$me->addMacro('name', [$me, 'macroName'], [$me, 'macroNameEnd'], [$me, 'macroNameAttr']);
3838
$me->addMacro('inputError', [$me, 'macroInputError']);
39+
$me->addMacro('formPrint', [$me, 'macroFormPrint']);
3940
}
4041

4142

@@ -240,4 +241,22 @@ public function macroInputError(MacroNode $node, PhpWriter $writer)
240241
return $writer->write('echo %escape(end($this->global->formsStack)[%0.word]->getError());', $name);
241242
}
242243
}
244+
245+
246+
/**
247+
* {formPrint [ClassName]}
248+
*/
249+
public function macroFormPrint(MacroNode $node, PhpWriter $writer)
250+
{
251+
$name = $node->tokenizer->fetchWord();
252+
if ($name == null) { // null or false
253+
throw new CompileException('Missing form name in ' . $node->getNotation());
254+
}
255+
$node->tokenizer->reset();
256+
return $writer->write(
257+
'Nette\Bridges\FormsLatte\Runtime::renderBlueprint('
258+
. ($name[0] === '$' ? 'is_object(%node.word) ? %node.word : ' : '')
259+
. '$this->global->uiControl[%node.word]); exit;'
260+
);
261+
}
243262
}

src/Bridges/FormsLatte/Runtime.php

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
namespace Nette\Bridges\FormsLatte;
1111

12+
use Latte;
1213
use Nette;
1314
use Nette\Forms\Form;
1415
use Nette\Utils\Html;
@@ -71,4 +72,85 @@ public static function renderFormEnd(Form $form, bool $withTags = true): string
7172

7273
return $s . ($withTags ? $form->getElementPrototype()->endTag() . "\n" : '');
7374
}
75+
76+
77+
/**
78+
* Generates blueprint of form.
79+
*/
80+
public static function renderBlueprint($form): void
81+
{
82+
$dummyForm = new Form;
83+
$dict = new \SplObjectStorage;
84+
foreach ($form->getControls() as $name => $input) {
85+
$dict[$input] = $dummyInput = new class extends Nette\Forms\Controls\BaseControl {
86+
public $inner;
87+
88+
89+
public function getLabel($name = null)
90+
{
91+
return $this->inner->getLabel() ? '{label ' . $this->inner->lookupPath(Form::class) . '}' : null;
92+
}
93+
94+
95+
public function getControl()
96+
{
97+
return '{input ' . $this->inner->lookupPath(Form::class) . '}';
98+
}
99+
100+
101+
public function isRequired(): bool
102+
{
103+
return $this->inner->isRequired();
104+
}
105+
106+
107+
public function getOption($key, $default = null)
108+
{
109+
return $key === 'rendered' ? parent::getOption($key) : $this->inner->getOption($key, $default);
110+
}
111+
};
112+
$dummyInput->inner = $input;
113+
$dummyForm->addComponent($dummyInput, (string) $dict->count());
114+
$dummyInput->addError('{inputError ' . $input->lookupPath(Form::class) . '}');
115+
}
116+
117+
foreach ($form->getGroups() as $group) {
118+
$dummyGroup = $dummyForm->addGroup();
119+
foreach ($group->getOptions() as $k => $v) {
120+
$dummyGroup->setOption($k, $v);
121+
}
122+
foreach ($group->getControls() as $control) {
123+
if ($dict[$control]) {
124+
$dummyGroup->add($dict[$control]);
125+
}
126+
}
127+
}
128+
129+
$renderer = clone $form->getRenderer();
130+
$dummyForm->setRenderer($renderer);
131+
132+
if ($renderer instanceof Nette\Forms\Rendering\DefaultFormRenderer) {
133+
$renderer->wrappers['error']['container'] = $renderer->getWrapper('error container')->setAttribute('n:ifcontent', true);
134+
$renderer->wrappers['error']['item'] = $renderer->getWrapper('error item')->setAttribute('n:foreach', '$form->getOwnErrors() as $error');
135+
$renderer->wrappers['control']['errorcontainer'] = $renderer->getWrapper('control errorcontainer')->setAttribute('n:ifcontent', true);
136+
$dummyForm->addError('{$error}');
137+
138+
ob_start();
139+
$dummyForm->render('end');
140+
$end = ob_get_clean();
141+
}
142+
143+
ob_start();
144+
$dummyForm->render();
145+
$body = ob_get_clean();
146+
147+
$body = str_replace($dummyForm->getElementPrototype()->startTag(), '<form n:name="' . $form->getName() . '">', $body);
148+
$body = str_replace($end ?? '', '</form>', $body);
149+
150+
$blueprint = new Latte\Runtime\Blueprint;
151+
$end = $blueprint->printCanvas();
152+
$blueprint->printHeader('Form ' . $form->getName());
153+
$blueprint->printCode($body);
154+
echo $end;
155+
}
74156
}

src/Forms/Rendering/DefaultFormRenderer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,7 @@ public function renderControl(Nette\Forms\IControl $control): Html
462462
}
463463

464464

465-
protected function getWrapper(string $name): Html
465+
public function getWrapper(string $name): Html
466466
{
467467
$data = $this->getValue($name);
468468
return $data instanceof Html ? clone $data : Html::el($data);
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Nette\Forms\Form;
6+
use Nette\Utils\Html;
7+
use Tester\Assert;
8+
9+
10+
require __DIR__ . '/../bootstrap.php';
11+
12+
13+
$form = new Form('signForm');
14+
$form->addGroup('Personal data');
15+
$form->addText('name')->setRequired('Enter your name');
16+
$form->addContainer('cont')
17+
->addText('name');
18+
$form->addHidden('id');
19+
$form->addCheckbox('agree');
20+
$form->addGroup();
21+
$form->addSubmit('submit', 'Send');
22+
23+
$renderer = $form->getRenderer();
24+
$renderer->wrappers['form']['container'] = Html::el('div')->id('form');
25+
$renderer->wrappers['group']['container'] = null;
26+
$renderer->wrappers['group']['label'] = 'h3';
27+
$renderer->wrappers['pair']['container'] = null;
28+
$renderer->wrappers['controls']['container'] = 'dl';
29+
$renderer->wrappers['control']['container'] = 'dd';
30+
$renderer->wrappers['control']['.odd'] = 'odd';
31+
$renderer->wrappers['label']['container'] = 'dt';
32+
$renderer->wrappers['label']['suffix'] = ':';
33+
34+
35+
ob_start();
36+
Nette\Bridges\FormsLatte\Runtime::renderBlueprint($form);
37+
$res = ob_get_clean();
38+
39+
Assert::match(
40+
'%A%<form n:name="signForm">
41+
42+
<ul class="error" n:ifcontent>
43+
<li n:foreach="$form->getOwnErrors() as $error">{$error}</li>
44+
</ul>
45+
46+
<div id="form">
47+
48+
<h3>Personal data</h3>
49+
50+
<dl>
51+
52+
<dt>{label name}:</dt>
53+
54+
<dd>{input name}
55+
56+
<span class="error" n:ifcontent>
57+
{inputError name}
58+
</span>
59+
</dd>
60+
61+
62+
63+
<dt>{label cont-name}:</dt>
64+
65+
<dd class="odd">{input cont-name}
66+
67+
<span class="error" n:ifcontent>
68+
{inputError cont-name}
69+
</span>
70+
</dd>
71+
72+
73+
74+
<dt></dt>
75+
76+
<dd>{input agree}
77+
78+
<span class="error" n:ifcontent>
79+
{inputError agree}
80+
</span>
81+
</dd>
82+
83+
</dl>
84+
85+
86+
87+
<dl>
88+
89+
<dt></dt>
90+
91+
<dd>{input submit}</dd>
92+
93+
</dl>
94+
95+
</div>
96+
</form>%A%',
97+
$res
98+
);

0 commit comments

Comments
 (0)