1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Nette\Forms;
9:
10: use Nette;
11:
12:
13: 14: 15: 16: 17: 18: 19:
20: class Container extends Nette\ComponentModel\Container implements \ArrayAccess
21: {
22:
23: public $onValidate;
24:
25:
26: protected $currentGroup;
27:
28:
29: private $validated;
30:
31:
32:
33:
34:
35: 36: 37: 38: 39: 40:
41: public function setDefaults($values, $erase = false)
42: {
43: $form = $this->getForm(false);
44: if (!$form || !$form->isAnchored() || !$form->isSubmitted()) {
45: $this->setValues($values, $erase);
46: }
47: return $this;
48: }
49:
50:
51: 52: 53: 54: 55: 56: 57:
58: public function setValues($values, $erase = false)
59: {
60: if ($values instanceof \Traversable) {
61: $values = iterator_to_array($values);
62:
63: } elseif (!is_array($values)) {
64: throw new Nette\InvalidArgumentException(sprintf('First parameter must be an array, %s given.', gettype($values)));
65: }
66:
67: foreach ($this->getComponents() as $name => $control) {
68: if ($control instanceof IControl) {
69: if (array_key_exists($name, $values)) {
70: $control->setValue($values[$name]);
71:
72: } elseif ($erase) {
73: $control->setValue(null);
74: }
75:
76: } elseif ($control instanceof self) {
77: if (array_key_exists($name, $values)) {
78: $control->setValues($values[$name], $erase);
79:
80: } elseif ($erase) {
81: $control->setValues([], $erase);
82: }
83: }
84: }
85: return $this;
86: }
87:
88:
89: 90: 91: 92: 93:
94: public function getValues($asArray = false)
95: {
96: $values = $asArray ? [] : new Nette\Utils\ArrayHash;
97: foreach ($this->getComponents() as $name => $control) {
98: if ($control instanceof IControl && !$control->isOmitted()) {
99: $values[$name] = $control->getValue();
100:
101: } elseif ($control instanceof self) {
102: $values[$name] = $control->getValues($asArray);
103: }
104: }
105: return $values;
106: }
107:
108:
109:
110:
111:
112: 113: 114: 115:
116: public function isValid()
117: {
118: if (!$this->validated) {
119: if ($this->getErrors()) {
120: return false;
121: }
122: $this->validate();
123: }
124: return !$this->getErrors();
125: }
126:
127:
128: 129: 130: 131: 132:
133: public function validate(array $controls = null)
134: {
135: foreach ($controls === null ? $this->getComponents() : $controls as $control) {
136: if ($control instanceof IControl || $control instanceof self) {
137: $control->validate();
138: }
139: }
140: if ($this->onValidate !== null) {
141: if (!is_array($this->onValidate) && !$this->onValidate instanceof \Traversable) {
142: throw new Nette\UnexpectedValueException('Property Form::$onValidate must be array or Traversable, ' . gettype($this->onValidate) . ' given.');
143: }
144: foreach ($this->onValidate as $handler) {
145: $params = Nette\Utils\Callback::toReflection($handler)->getParameters();
146: $values = isset($params[1]) ? $this->getValues($params[1]->isArray()) : null;
147: Nette\Utils\Callback::invoke($handler, $this, $values);
148: }
149: }
150: $this->validated = true;
151: }
152:
153:
154: 155: 156: 157:
158: public function getErrors()
159: {
160: $errors = [];
161: foreach ($this->getControls() as $control) {
162: $errors = array_merge($errors, $control->getErrors());
163: }
164: return array_unique($errors);
165: }
166:
167:
168:
169:
170:
171: 172: 173:
174: public function setCurrentGroup(ControlGroup $group = null)
175: {
176: $this->currentGroup = $group;
177: return $this;
178: }
179:
180:
181: 182: 183: 184:
185: public function getCurrentGroup()
186: {
187: return $this->currentGroup;
188: }
189:
190:
191: 192: 193: 194: 195: 196: 197: 198:
199: public function addComponent(Nette\ComponentModel\IComponent $component, $name, $insertBefore = null)
200: {
201: parent::addComponent($component, $name, $insertBefore);
202: if ($this->currentGroup !== null) {
203: $this->currentGroup->add($component);
204: }
205: return $this;
206: }
207:
208:
209: 210: 211: 212:
213: public function getControls()
214: {
215: return $this->getComponents(true, IControl::class);
216: }
217:
218:
219: 220: 221: 222: 223:
224: public function getForm($throw = true)
225: {
226: return $this->lookup(Form::class, $throw);
227: }
228:
229:
230:
231:
232:
233: 234: 235: 236: 237: 238: 239: 240:
241: public function addText($name, $label = null, $cols = null, $maxLength = null)
242: {
243: return $this[$name] = (new Controls\TextInput($label, $maxLength))
244: ->setHtmlAttribute('size', $cols);
245: }
246:
247:
248: 249: 250: 251: 252: 253: 254: 255:
256: public function addPassword($name, $label = null, $cols = null, $maxLength = null)
257: {
258: return $this[$name] = (new Controls\TextInput($label, $maxLength))
259: ->setHtmlAttribute('size', $cols)
260: ->setHtmlType('password');
261: }
262:
263:
264: 265: 266: 267: 268: 269: 270: 271:
272: public function addTextArea($name, $label = null, $cols = null, $rows = null)
273: {
274: return $this[$name] = (new Controls\TextArea($label))
275: ->setHtmlAttribute('cols', $cols)->setHtmlAttribute('rows', $rows);
276: }
277:
278:
279: 280: 281: 282: 283: 284:
285: public function addEmail($name, $label = null)
286: {
287: return $this[$name] = (new Controls\TextInput($label))
288: ->setRequired(false)
289: ->addRule(Form::EMAIL);
290: }
291:
292:
293: 294: 295: 296: 297: 298:
299: public function addInteger($name, $label = null)
300: {
301: return $this[$name] = (new Controls\TextInput($label))
302: ->setNullable()
303: ->setRequired(false)
304: ->addRule(Form::INTEGER);
305: }
306:
307:
308: 309: 310: 311: 312: 313: 314:
315: public function addUpload($name, $label = null, $multiple = false)
316: {
317: return $this[$name] = new Controls\UploadControl($label, $multiple);
318: }
319:
320:
321: 322: 323: 324: 325: 326:
327: public function addMultiUpload($name, $label = null)
328: {
329: return $this[$name] = new Controls\UploadControl($label, true);
330: }
331:
332:
333: 334: 335: 336: 337: 338:
339: public function addHidden($name, $default = null)
340: {
341: return $this[$name] = (new Controls\HiddenField)
342: ->setDefaultValue($default);
343: }
344:
345:
346: 347: 348: 349: 350: 351:
352: public function addCheckbox($name, $caption = null)
353: {
354: return $this[$name] = new Controls\Checkbox($caption);
355: }
356:
357:
358: 359: 360: 361: 362: 363:
364: public function addRadioList($name, $label = null, array $items = null)
365: {
366: return $this[$name] = new Controls\RadioList($label, $items);
367: }
368:
369:
370: 371: 372: 373: 374: 375:
376: public function addCheckboxList($name, $label = null, array $items = null)
377: {
378: return $this[$name] = new Controls\CheckboxList($label, $items);
379: }
380:
381:
382: 383: 384: 385: 386: 387: 388: 389:
390: public function addSelect($name, $label = null, array $items = null, $size = null)
391: {
392: return $this[$name] = (new Controls\SelectBox($label, $items))
393: ->setHtmlAttribute('size', $size > 1 ? (int) $size : null);
394: }
395:
396:
397: 398: 399: 400: 401: 402: 403: 404:
405: public function addMultiSelect($name, $label = null, array $items = null, $size = null)
406: {
407: return $this[$name] = (new Controls\MultiSelectBox($label, $items))
408: ->setHtmlAttribute('size', $size > 1 ? (int) $size : null);
409: }
410:
411:
412: 413: 414: 415: 416: 417:
418: public function addSubmit($name, $caption = null)
419: {
420: return $this[$name] = new Controls\SubmitButton($caption);
421: }
422:
423:
424: 425: 426: 427: 428: 429:
430: public function addButton($name, $caption = null)
431: {
432: return $this[$name] = new Controls\Button($caption);
433: }
434:
435:
436: 437: 438: 439: 440: 441: 442:
443: public function addImage($name, $src = null, $alt = null)
444: {
445: return $this[$name] = new Controls\ImageButton($src, $alt);
446: }
447:
448:
449: 450: 451: 452: 453:
454: public function addContainer($name)
455: {
456: $control = new self;
457: $control->currentGroup = $this->currentGroup;
458: if ($this->currentGroup !== null) {
459: $this->currentGroup->add($control);
460: }
461: return $this[$name] = $control;
462: }
463:
464:
465:
466:
467:
468: public function __call($name, $args)
469: {
470: if ($callback = Nette\Utils\ObjectMixin::getExtensionMethod(__CLASS__, $name)) {
471: return Nette\Utils\Callback::invoke($callback, $this, ...$args);
472: }
473: return parent::__call($name, $args);
474: }
475:
476:
477: public static function extensionMethod($name, $callback = null)
478: {
479: if (strpos($name, '::') !== false) {
480: list(, $name) = explode('::', $name);
481: }
482: Nette\Utils\ObjectMixin::setExtensionMethod(__CLASS__, $name, $callback);
483: }
484:
485:
486:
487:
488:
489: 490: 491: 492: 493: 494:
495: public function offsetSet($name, $component)
496: {
497: $this->addComponent($component, $name);
498: }
499:
500:
501: 502: 503: 504: 505: 506:
507: public function offsetGet($name)
508: {
509: return $this->getComponent($name, true);
510: }
511:
512:
513: 514: 515: 516: 517:
518: public function offsetExists($name)
519: {
520: return $this->getComponent($name, false) !== null;
521: }
522:
523:
524: 525: 526: 527: 528:
529: public function offsetUnset($name)
530: {
531: $component = $this->getComponent($name, false);
532: if ($component !== null) {
533: $this->removeComponent($component);
534: }
535: }
536:
537:
538: 539: 540:
541: public function __clone()
542: {
543: throw new Nette\NotImplementedException('Form cloning is not supported yet.');
544: }
545: }
546: