• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nette / forms / 26452888331

26 May 2026 02:01PM UTC coverage: 93.307% (+0.07%) from 93.241%
26452888331

push

github

dg
added CLAUDE.md

2105 of 2256 relevant lines covered (93.31%)

0.93 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

70.27
/src/Forms/Blueprint.php
1
<?php declare(strict_types=1);
2

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

8
namespace Nette\Forms;
9

10
use Nette\Utils\Html;
11

12

13
/**
14
 * Generates blueprints for forms.
15
 */
16
final class Blueprint
17
{
18
        private const ClassNameSuffix = 'FormData';
19

20

21
        /**
22
         * Generates blueprint of form Latte template.
23
         */
24
        public static function latte(Form $form, bool $exit = true): void
25
        {
26
                $blueprint = new self;
×
27
                $blueprint->printBegin();
×
28
                $blueprint->printHeader('Form ' . $form->getName());
×
29
                $blueprint->printCode($blueprint->generateLatte($form), 'latte');
×
30
                $blueprint->printEnd();
×
31
                if ($exit) {
×
32
                        exit;
×
33
                }
34
        }
35

36

37
        /**
38
         * Generates blueprint of form data class.
39
         */
40
        public static function dataClass(Form $form, bool $exit = true): void
41
        {
42
                $blueprint = new self;
×
43
                $blueprint->printBegin();
×
44
                $blueprint->printHeader('Form Data Class ' . $form->getName());
×
45
                $blueprint->printCode($blueprint->generateDataClass($form), 'php');
×
46
                $blueprint->printCode($blueprint->generateDataClass($form, true), 'php');
×
47
                $blueprint->printEnd();
×
48
                if ($exit) {
×
49
                        exit;
×
50
                }
51
        }
52

53

54
        private function printBegin(): void
55
        {
56
                echo '<script src="https://nette.github.io/resources/prism/prism.js"></script>';
×
57
                echo '<link rel="stylesheet" href="https://nette.github.io/resources/prism/prism.css">';
×
58
                echo "<div style='all:initial; position:fixed; overflow:auto; z-index:1000; left:0; right:0; top:0; bottom:0; color:black; background:white; padding:1em'>\n";
×
59
        }
60

61

62
        private function printEnd(): void
63
        {
64
                echo "</div>\n";
×
65
        }
66

67

68
        private function printHeader(string $string): void
69
        {
70
                echo "<h1 style='all:initial; display:block; font-size:2em; margin:1em 0'>",
×
71
                        htmlspecialchars($string),
×
72
                        "</h1>\n";
×
73
        }
74

75

76
        private function printCode(string $code, string $lang): void
77
        {
78
                echo '<pre><code class="language-', htmlspecialchars($lang), '">',
×
79
                        htmlspecialchars($code),
×
80
                        "</code></pre>\n";
×
81
        }
82

83

84
        public function generateLatte(Form $form): string
1✔
85
        {
86
                $dict = new \SplObjectStorage;
1✔
87
                $dummyForm = new class extends Form {
1✔
88
                        protected function receiveHttpData(): array
89
                        {
90
                                return [];
1✔
91
                        }
92
                };
93

94
                foreach ($form->getControls() as $input) {
1✔
95
                        $dict[$input] = $dummyInput = new class extends Controls\BaseControl {
1✔
96
                                public Control $inner;
97

98

99
                                public function getLabel(string|\Stringable|null $caption = null): Html|string|null
1✔
100
                                {
101
                                        return $this->inner->getLabel()
1✔
102
                                                ? '{label ' . $this->inner->lookupPath(Form::class) . '/}'
1✔
103
                                                : null;
1✔
104
                                }
105

106

107
                                public function getControl(): Html|string
108
                                {
109
                                        return '{input ' . $this->inner->lookupPath(Form::class) . '}';
1✔
110
                                }
111

112

113
                                public function isRequired(): bool
114
                                {
115
                                        return $this->inner->isRequired();
1✔
116
                                }
117

118

119
                                public function getOption(mixed $key): mixed
1✔
120
                                {
121
                                        return $key === 'rendered'
1✔
122
                                                ? parent::getOption($key)
1✔
123
                                                : $this->inner->getOption($key);
1✔
124
                                }
125
                        };
126
                        $dummyInput->inner = $input;
1✔
127
                        $dummyForm->addComponent($dummyInput, (string) $dict->count());
1✔
128
                        $dummyInput->addError('{inputError ' . $input->lookupPath(Form::class) . '}');
1✔
129
                }
130

131
                foreach ($form->getGroups() as $group) {
1✔
132
                        $dummyGroup = $dummyForm->addGroup();
1✔
133
                        foreach ($group->getOptions() as $k => $v) {
1✔
134
                                $dummyGroup->setOption($k, $v);
1✔
135
                        }
136

137
                        foreach ($group->getControls() as $control) {
1✔
138
                                if ($dict[$control]) {
1✔
139
                                        $dummyGroup->add($dict[$control]);
1✔
140
                                }
141
                        }
142
                }
143

144
                $renderer = clone $form->getRenderer();
1✔
145
                $dummyForm->setRenderer($renderer);
1✔
146
                $dummyForm->onRender = $form->onRender;
1✔
147
                $dummyForm->fireRenderEvents();
1✔
148

149
                if ($renderer instanceof Rendering\DefaultFormRenderer) {
1✔
150
                        $renderer->wrappers['error']['container'] = $renderer->getWrapper('error container')->setAttribute('n:ifcontent', true);
1✔
151
                        $renderer->wrappers['error']['item'] = $renderer->getWrapper('error item')->setAttribute('n:foreach', '$form->getOwnErrors() as $error');
1✔
152
                        $renderer->wrappers['control']['errorcontainer'] = $renderer->getWrapper('control errorcontainer')->setAttribute('n:ifcontent', true);
1✔
153
                        $dummyForm->addError('{$error}');
1✔
154

155
                        ob_start();
1✔
156
                        $dummyForm->render('end');
1✔
157
                        $end = ob_get_clean();
1✔
158
                }
159

160
                ob_start();
1✔
161
                $dummyForm->render();
1✔
162
                $body = ob_get_clean();
1✔
163

164
                $body = str_replace($dummyForm->getElementPrototype()->startTag(), '<form n:name="' . $form->getName() . '">', $body);
1✔
165
                $body = str_replace($end ?? '', '</form>', $body);
1✔
166
                return $body;
1✔
167
        }
168

169

170
        public function generateDataClass(
1✔
171
                Container $container,
172
                ?bool $propertyPromotion = false,
173
                ?string $baseName = null,
174
        ): string
175
        {
176
                $baseName ??= preg_replace('~Form$~', '', ucwords((string) $container->getName()));
1✔
177
                $nextCode = '';
1✔
178
                $props = [];
1✔
179
                foreach ($container->getComponents() as $name => $input) {
1✔
180
                        if ($input instanceof Controls\BaseControl && $input->isOmitted()) {
1✔
181
                                continue;
1✔
182
                        } elseif ($input instanceof Controls\Checkbox) {
1✔
183
                                $type = 'bool';
1✔
184
                        } elseif ($input instanceof Controls\MultiChoiceControl) {
1✔
185
                                $type = 'array';
×
186
                        } elseif ($input instanceof Controls\ChoiceControl) {
1✔
187
                                $type = 'string|int';
×
188
                                if (!$input->isRequired()) {
×
189
                                        $type .= '|null';
×
190
                                }
191
                        } elseif ($input instanceof Controls\HiddenField || $input instanceof Controls\TextBase) {
1✔
192
                                $type = 'string';
1✔
193
                                foreach ($input->getRules() as $rule) {
1✔
194
                                        if ($rule->validator === Form::Integer) {
1✔
195
                                                $type = 'int';
1✔
196
                                                break;
1✔
197
                                        }
198
                                }
199

200
                                if (!$input->isRequired() && $input->isNullable()) {
1✔
201
                                        $type = '?' . $type;
1✔
202
                                }
203
                        } elseif ($input instanceof Controls\UploadControl) {
1✔
204
                                $type = '\Nette\Http\FileUpload';
×
205
                                if (!$input->isRequired()) {
×
206
                                        $type = '?' . $type;
×
207
                                }
208
                        } elseif ($input instanceof Container) {
1✔
209
                                $type = $baseName . ucwords((string) $name);
1✔
210
                                $nextCode .= $this->generateDataClass($input, $propertyPromotion, $type);
1✔
211
                                $type .= self::ClassNameSuffix;
1✔
212
                        } else {
213
                                $type = '';
×
214
                        }
215

216
                        $props[] = 'public ' . ($type ? $type . ' ' : '') . '$' . $name;
1✔
217
                }
218

219
                $class = $baseName . self::ClassNameSuffix;
1✔
220
                return "class $class\n"
1✔
221
                        . "{\n"
1✔
222
                        . ($propertyPromotion
1✔
223
                                ? "\tpublic function __construct(\n"
224
                                . ($props ? "\t\t" . implode(",\n\t\t", $props) . ",\n" : '')
1✔
225
                                . "\t) {\n\t}\n"
1✔
226
                                : ($props ? "\t" . implode(";\n\t", $props) . ";\n" : '')
1✔
227
                        )
228
                        . "}\n\n"
1✔
229
                        . $nextCode;
1✔
230
        }
231
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc