PluginProbe
Packeta / 1.6.2
Packeta v1.6.2
2.3.2 2.3.1 trunk 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.3.0 1.3.1 1.3.2 1.4 1.4.1 1.4.2 1.4.3 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 All 56 releases
packeta / deps / nette / forms / src / Forms / Container.php

Container.php in Packeta 1.6.2, at deps/nette/forms/src/Forms/Container.php

395 lines 13.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
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 declare (strict_types=1);
8 namespace Packetery\Nette\Forms;
9
10 use Packetery\Nette;
11 use Packetery\Nette\Utils\ArrayHash;
12 /**
13 * Container for form controls.
14 *
15 * @property ArrayHash $values
16 * @property-read \Iterator $controls
17 * @property-read Form|null $form
18 */
19 class Container extends \Packetery\Nette\ComponentModel\Container implements \ArrayAccess
20 {
21 use \Packetery\Nette\ComponentModel\ArrayAccess;
22 private const ARRAY = 'array';
23 /**
24 * Occurs when the form was validated
25 * @var array<callable(self, array|object): void|callable(array|object): void>
26 */
27 public $onValidate = [];
28 /** @var ControlGroup|null */
29 protected $currentGroup;
30 /** @var callable[] extension methods */
31 private static $extMethods = [];
32 /** @var bool */
33 private $validated;
34 /** @var ?string */
35 private $mappedType;
36 /********************* data exchange ****************d*g**/
37 /**
38 * Fill-in with default values.
39 * @param array|object $data
40 * @return static
41 */
42 public function setDefaults($data, bool $erase = \false)
43 {
44 $form = $this->getForm(\false);
45 if (!$form || !$form->isAnchored() || !$form->isSubmitted()) {
46 $this->setValues($data, $erase);
47 }
48 return $this;
49 }
50 /**
51 * Fill-in with values.
52 * @param array|object $data
53 * @return static
54 * @internal
55 */
56 public function setValues($data, bool $erase = \false)
57 {
58 if ($data instanceof \Traversable) {
59 $values = \iterator_to_array($data);
60 } elseif (\is_object($data) || \is_array($data) || $data === null) {
61 $values = (array) $data;
62 } else {
63 throw new \Packetery\Nette\InvalidArgumentException(\sprintf('First parameter must be an array or object, %s given.', \gettype($data)));
64 }
65 foreach ($this->getComponents() as $name => $control) {
66 if ($control instanceof Control) {
67 if (\array_key_exists($name, $values)) {
68 $control->setValue($values[$name]);
69 } elseif ($erase) {
70 $control->setValue(null);
71 }
72 } elseif ($control instanceof self) {
73 if (\array_key_exists($name, $values)) {
74 $control->setValues($values[$name], $erase);
75 } elseif ($erase) {
76 $control->setValues([], $erase);
77 }
78 }
79 }
80 return $this;
81 }
82 /**
83 * Returns the values submitted by the form.
84 * @param string|object|null $returnType 'array' for array
85 * @param Control[]|null $controls
86 * @return object|array
87 */
88 public function getValues($returnType = null, array $controls = null)
89 {
90 $form = $this->getForm(\false);
91 if ($form && ($submitter = $form->isSubmitted())) {
92 if (!$this->isValid()) {
93 \trigger_error(__METHOD__ . '() invoked but the form is not valid.', \E_USER_WARNING);
94 }
95 if ($controls === null && $submitter instanceof SubmitterControl) {
96 $controls = $submitter->getValidationScope();
97 }
98 }
99 $returnType = $returnType === \true ? self::ARRAY : $returnType;
100 return $this->getUnsafeValues($returnType, $controls);
101 }
102 /**
103 * Returns the potentially unvalidated values submitted by the form.
104 * @param string|object|null $returnType 'array' for array
105 * @param Control[]|null $controls
106 * @return object|array
107 */
108 public function getUnsafeValues($returnType, array $controls = null)
109 {
110 if (\is_object($returnType)) {
111 $obj = $returnType;
112 } else {
113 $returnType = $returnType ?? $this->mappedType ?? ArrayHash::class;
114 $obj = $returnType === self::ARRAY ? new \stdClass() : new $returnType();
115 }
116 $rc = new \ReflectionClass($obj);
117 foreach ($this->getComponents() as $name => $control) {
118 $allowed = $controls === null || \in_array($control, $controls, \true);
119 $name = (string) $name;
120 if ($control instanceof Control && $allowed && !$control->isOmitted()) {
121 $obj->{$name} = $control->getValue();
122 } elseif ($control instanceof self) {
123 $type = $returnType === self::ARRAY && !$control->mappedType ? self::ARRAY : ($rc->hasProperty($name) ? \Packetery\Nette\Utils\Reflection::getPropertyType($rc->getProperty($name)) : null);
124 $obj->{$name} = $control->getUnsafeValues($type, $allowed ? null : $controls);
125 }
126 }
127 return $returnType === self::ARRAY ? (array) $obj : $obj;
128 }
129 /** @return static */
130 public function setMappedType(string $type)
131 {
132 $this->mappedType = $type;
133 return $this;
134 }
135 /********************* validation ****************d*g**/
136 /**
137 * Is form valid?
138 */
139 public function isValid() : bool
140 {
141 if (!$this->validated) {
142 if ($this->getErrors()) {
143 return \false;
144 }
145 $this->validate();
146 }
147 return !$this->getErrors();
148 }
149 /**
150 * Performs the server side validation.
151 * @param Control[]|null $controls
152 */
153 public function validate(array $controls = null) : void
154 {
155 foreach ($controls ?? $this->getComponents() as $control) {
156 if ($control instanceof Control || $control instanceof self) {
157 $control->validate();
158 }
159 }
160 $this->validated = \true;
161 foreach ($this->onValidate as $handler) {
162 $params = \Packetery\Nette\Utils\Callback::toReflection($handler)->getParameters();
163 $types = \array_map([\Packetery\Nette\Utils\Reflection::class, 'getParameterType'], $params);
164 $args = isset($types[0]) && !$this instanceof $types[0] ? [$this->getUnsafeValues($types[0])] : [$this, isset($params[1]) ? $this->getUnsafeValues($types[1]) : null];
165 $handler(...$args);
166 }
167 }
168 /**
169 * Returns all validation errors.
170 */
171 public function getErrors() : array
172 {
173 $errors = [];
174 foreach ($this->getControls() as $control) {
175 $errors = \array_merge($errors, $control->getErrors());
176 }
177 return \array_unique($errors);
178 }
179 /********************* form building ****************d*g**/
180 /** @return static */
181 public function setCurrentGroup(ControlGroup $group = null)
182 {
183 $this->currentGroup = $group;
184 return $this;
185 }
186 /**
187 * Returns current group.
188 */
189 public function getCurrentGroup() : ?ControlGroup
190 {
191 return $this->currentGroup;
192 }
193 /**
194 * Adds the specified component to the IContainer.
195 * @return static
196 * @throws \Packetery\Nette\InvalidStateException
197 */
198 public function addComponent(\Packetery\Nette\ComponentModel\IComponent $component, ?string $name, string $insertBefore = null)
199 {
200 parent::addComponent($component, $name, $insertBefore);
201 if ($this->currentGroup !== null) {
202 $this->currentGroup->add($component);
203 }
204 return $this;
205 }
206 /**
207 * Iterates over all form controls.
208 */
209 public function getControls() : \Iterator
210 {
211 return $this->getComponents(\true, Control::class);
212 }
213 /**
214 * Returns form.
215 */
216 public function getForm(bool $throw = \true) : ?Form
217 {
218 return $this->lookup(Form::class, $throw);
219 }
220 /********************* control factories ****************d*g**/
221 /**
222 * Adds single-line text input control to the form.
223 * @param string|object $label
224 */
225 public function addText(string $name, $label = null, int $cols = null, int $maxLength = null) : Controls\TextInput
226 {
227 return $this[$name] = (new Controls\TextInput($label, $maxLength))->setHtmlAttribute('size', $cols);
228 }
229 /**
230 * Adds single-line text input control used for sensitive input such as passwords.
231 * @param string|object $label
232 */
233 public function addPassword(string $name, $label = null, int $cols = null, int $maxLength = null) : Controls\TextInput
234 {
235 return $this[$name] = (new Controls\TextInput($label, $maxLength))->setHtmlAttribute('size', $cols)->setHtmlType('password');
236 }
237 /**
238 * Adds multi-line text input control to the form.
239 * @param string|object $label
240 */
241 public function addTextArea(string $name, $label = null, int $cols = null, int $rows = null) : Controls\TextArea
242 {
243 return $this[$name] = (new Controls\TextArea($label))->setHtmlAttribute('cols', $cols)->setHtmlAttribute('rows', $rows);
244 }
245 /**
246 * Adds input for email.
247 * @param string|object $label
248 */
249 public function addEmail(string $name, $label = null) : Controls\TextInput
250 {
251 return $this[$name] = (new Controls\TextInput($label))->addRule(Form::EMAIL);
252 }
253 /**
254 * Adds input for integer.
255 * @param string|object $label
256 */
257 public function addInteger(string $name, $label = null) : Controls\TextInput
258 {
259 return $this[$name] = (new Controls\TextInput($label))->setNullable()->addRule(Form::INTEGER);
260 }
261 /**
262 * Adds control that allows the user to upload files.
263 * @param string|object $label
264 */
265 public function addUpload(string $name, $label = null) : Controls\UploadControl
266 {
267 return $this[$name] = new Controls\UploadControl($label, \false);
268 }
269 /**
270 * Adds control that allows the user to upload multiple files.
271 * @param string|object $label
272 */
273 public function addMultiUpload(string $name, $label = null) : Controls\UploadControl
274 {
275 return $this[$name] = new Controls\UploadControl($label, \true);
276 }
277 /**
278 * Adds hidden form control used to store a non-displayed value.
279 */
280 public function addHidden(string $name, $default = null) : Controls\HiddenField
281 {
282 return $this[$name] = (new Controls\HiddenField())->setDefaultValue($default);
283 }
284 /**
285 * Adds check box control to the form.
286 * @param string|object $caption
287 */
288 public function addCheckbox(string $name, $caption = null) : Controls\Checkbox
289 {
290 return $this[$name] = new Controls\Checkbox($caption);
291 }
292 /**
293 * Adds set of radio button controls to the form.
294 * @param string|object $label
295 */
296 public function addRadioList(string $name, $label = null, array $items = null) : Controls\RadioList
297 {
298 return $this[$name] = new Controls\RadioList($label, $items);
299 }
300 /**
301 * Adds set of checkbox controls to the form.
302 * @param string|object $label
303 */
304 public function addCheckboxList(string $name, $label = null, array $items = null) : Controls\CheckboxList
305 {
306 return $this[$name] = new Controls\CheckboxList($label, $items);
307 }
308 /**
309 * Adds select box control that allows single item selection.
310 * @param string|object $label
311 */
312 public function addSelect(string $name, $label = null, array $items = null, int $size = null) : Controls\SelectBox
313 {
314 return $this[$name] = (new Controls\SelectBox($label, $items))->setHtmlAttribute('size', $size > 1 ? $size : null);
315 }
316 /**
317 * Adds select box control that allows multiple item selection.
318 * @param string|object $label
319 */
320 public function addMultiSelect(string $name, $label = null, array $items = null, int $size = null) : Controls\MultiSelectBox
321 {
322 return $this[$name] = (new Controls\MultiSelectBox($label, $items))->setHtmlAttribute('size', $size > 1 ? $size : null);
323 }
324 /**
325 * Adds button used to submit form.
326 * @param string|object $caption
327 */
328 public function addSubmit(string $name, $caption = null) : Controls\SubmitButton
329 {
330 return $this[$name] = new Controls\SubmitButton($caption);
331 }
332 /**
333 * Adds push buttons with no default behavior.
334 * @param string|object $caption
335 */
336 public function addButton(string $name, $caption = null) : Controls\Button
337 {
338 return $this[$name] = new Controls\Button($caption);
339 }
340 /**
341 * Adds graphical button used to submit form.
342 * @param string $src URI of the image
343 * @param string $alt alternate text for the image
344 */
345 public function addImageButton(string $name, string $src = null, string $alt = null) : Controls\ImageButton
346 {
347 return $this[$name] = new Controls\ImageButton($src, $alt);
348 }
349 /** @deprecated use addImageButton() */
350 public function addImage() : Controls\ImageButton
351 {
352 return $this->addImageButton(...\func_get_args());
353 }
354 /**
355 * Adds naming container to the form.
356 * @param string|int $name
357 */
358 public function addContainer($name) : self
359 {
360 $control = new self();
361 $control->currentGroup = $this->currentGroup;
362 if ($this->currentGroup !== null) {
363 $this->currentGroup->add($control);
364 }
365 return $this[$name] = $control;
366 }
367 /********************* extension methods ****************d*g**/
368 public function __call(string $name, array $args)
369 {
370 if (isset(self::$extMethods[$name])) {
371 return self::$extMethods[$name]($this, ...$args);
372 }
373 return parent::__call($name, $args);
374 }
375 public static function extensionMethod(
376 string $name,
377 /*callable*/
378 $callback
379 ) : void
380 {
381 if (\strpos($name, '::') !== \false) {
382 // back compatibility
383 [, $name] = \explode('::', $name);
384 }
385 self::$extMethods[$name] = $callback;
386 }
387 /**
388 * Prevents cloning.
389 */
390 public function __clone()
391 {
392 throw new \Packetery\Nette\NotImplementedException('Form cloning is not supported yet.');
393 }
394 }
395