1: <?php
2:
3: /**
4: * This file is part of the Nette Framework (http://nette.org)
5: *
6: * Copyright (c) 2004 David Grudl (http://davidgrudl.com)
7: *
8: * For the full copyright and license information, please view
9: * the file license.txt that was distributed with this source code.
10: * @package Nette\Forms
11: */
12:
13:
14:
15: /**
16: * Container for form controls.
17: *
18: * @author David Grudl
19: *
20: * @property-write $defaults
21: * @property ArrayHash $values
22: * @property-read bool $valid
23: * @property FormGroup $currentGroup
24: * @property-read ArrayIterator $controls
25: * @property-read Form $form
26: * @package Nette\Forms
27: */
28: class FormContainer extends ComponentContainer implements ArrayAccess
29: {
30: /** @var array of function(Form $sender); Occurs when the form is validated */
31: public $onValidate;
32:
33: /** @var FormGroup */
34: protected $currentGroup;
35:
36: /** @var bool */
37: protected $valid;
38:
39:
40:
41: /********************* data exchange ****************d*g**/
42:
43:
44:
45: /**
46: * Fill-in with default values.
47: * @param array|Traversable values used to fill the form
48: * @param bool erase other default values?
49: * @return FormContainer provides a fluent interface
50: */
51: public function setDefaults($values, $erase = FALSE)
52: {
53: $form = $this->getForm(FALSE);
54: if (!$form || !$form->isAnchored() || !$form->isSubmitted()) {
55: $this->setValues($values, $erase);
56: }
57: return $this;
58: }
59:
60:
61:
62: /**
63: * Fill-in with values.
64: * @param array|Traversable values used to fill the form
65: * @param bool erase other controls?
66: * @return FormContainer provides a fluent interface
67: */
68: public function setValues($values, $erase = FALSE)
69: {
70: if ($values instanceof Traversable) {
71: $values = iterator_to_array($values);
72:
73: } elseif (!is_array($values)) {
74: throw new InvalidArgumentException("First parameter must be an array, " . gettype($values) ." given.");
75: }
76:
77: foreach ($this->getComponents() as $name => $control) {
78: if ($control instanceof IFormControl) {
79: if (array_key_exists($name, $values)) {
80: $control->setValue($values[$name]);
81:
82: } elseif ($erase) {
83: $control->setValue(NULL);
84: }
85:
86: } elseif ($control instanceof FormContainer) {
87: if (array_key_exists($name, $values)) {
88: $control->setValues($values[$name], $erase);
89:
90: } elseif ($erase) {
91: $control->setValues(array(), $erase);
92: }
93: }
94: }
95: return $this;
96: }
97:
98:
99:
100: /**
101: * Returns the values submitted by the form.
102: * @param bool return values as an array?
103: * @return ArrayHash|array
104: */
105: public function getValues($asArray = FALSE)
106: {
107: $values = $asArray ? array() : new ArrayHash;
108: foreach ($this->getComponents() as $name => $control) {
109: if ($control instanceof IFormControl && !$control->isDisabled() && !$control instanceof ISubmitterControl) {
110: $values[$name] = $control->getValue();
111:
112: } elseif ($control instanceof FormContainer) {
113: $values[$name] = $control->getValues($asArray);
114: }
115: }
116: return $values;
117: }
118:
119:
120:
121: /********************* validation ****************d*g**/
122:
123:
124:
125: /**
126: * Is form valid?
127: * @return bool
128: */
129: public function isValid()
130: {
131: if ($this->valid === NULL) {
132: $this->validate();
133: }
134: return $this->valid;
135: }
136:
137:
138:
139: /**
140: * Performs the server side validation.
141: * @return void
142: */
143: public function validate()
144: {
145: $this->valid = TRUE;
146: $this->onValidate($this);
147: foreach ($this->getControls() as $control) {
148: if (!$control->getRules()->validate()) {
149: $this->valid = FALSE;
150: }
151: }
152: }
153:
154:
155:
156: /********************* form building ****************d*g**/
157:
158:
159:
160: /**
161: * @param FormGroup
162: * @return FormContainer provides a fluent interface
163: */
164: public function setCurrentGroup(FormGroup $group = NULL)
165: {
166: $this->currentGroup = $group;
167: return $this;
168: }
169:
170:
171:
172: /**
173: * Returns current group.
174: * @return FormGroup
175: */
176: public function getCurrentGroup()
177: {
178: return $this->currentGroup;
179: }
180:
181:
182:
183: /**
184: * Adds the specified component to the IComponentContainer.
185: * @param IComponent
186: * @param string
187: * @param string
188: * @return FormContainer provides a fluent interface
189: * @throws InvalidStateException
190: */
191: public function addComponent(IComponent $component, $name, $insertBefore = NULL)
192: {
193: parent::addComponent($component, $name, $insertBefore);
194: if ($this->currentGroup !== NULL && $component instanceof IFormControl) {
195: $this->currentGroup->add($component);
196: }
197: return $this;
198: }
199:
200:
201:
202: /**
203: * Iterates over all form controls.
204: * @return ArrayIterator
205: */
206: public function getControls()
207: {
208: return $this->getComponents(TRUE, 'IFormControl');
209: }
210:
211:
212:
213: /**
214: * Returns form.
215: * @param bool throw exception if form doesn't exist?
216: * @return Form
217: */
218: public function getForm($need = TRUE)
219: {
220: return $this->lookup('Form', $need);
221: }
222:
223:
224:
225: /********************* control factories ****************d*g**/
226:
227:
228:
229: /**
230: * Adds single-line text input control to the form.
231: * @param string control name
232: * @param string label
233: * @param int width of the control
234: * @param int maximum number of characters the user may enter
235: * @return TextInput
236: */
237: public function addText($name, $label = NULL, $cols = NULL, $maxLength = NULL)
238: {
239: return $this[$name] = new TextInput($label, $cols, $maxLength);
240: }
241:
242:
243:
244: /**
245: * Adds single-line text input control used for sensitive input such as passwords.
246: * @param string control name
247: * @param string label
248: * @param int width of the control
249: * @param int maximum number of characters the user may enter
250: * @return TextInput
251: */
252: public function addPassword($name, $label = NULL, $cols = NULL, $maxLength = NULL)
253: {
254: $control = new TextInput($label, $cols, $maxLength);
255: $control->setType('password');
256: return $this[$name] = $control;
257: }
258:
259:
260:
261: /**
262: * Adds multi-line text input control to the form.
263: * @param string control name
264: * @param string label
265: * @param int width of the control
266: * @param int height of the control in text lines
267: * @return TextArea
268: */
269: public function addTextArea($name, $label = NULL, $cols = 40, $rows = 10)
270: {
271: return $this[$name] = new TextArea($label, $cols, $rows);
272: }
273:
274:
275:
276: /**
277: * Adds control that allows the user to upload files.
278: * @param string control name
279: * @param string label
280: * @return UploadControl
281: */
282: public function addUpload($name, $label = NULL)
283: {
284: return $this[$name] = new UploadControl($label);
285: }
286:
287:
288:
289: /**
290: * Adds hidden form control used to store a non-displayed value.
291: * @param string control name
292: * @param mixed default value
293: * @return HiddenField
294: */
295: public function addHidden($name, $default = NULL)
296: {
297: $control = new HiddenField;
298: $control->setDefaultValue($default);
299: return $this[$name] = $control;
300: }
301:
302:
303:
304: /**
305: * Adds check box control to the form.
306: * @param string control name
307: * @param string caption
308: * @return Checkbox
309: */
310: public function addCheckbox($name, $caption = NULL)
311: {
312: return $this[$name] = new Checkbox($caption);
313: }
314:
315:
316:
317: /**
318: * Adds set of radio button controls to the form.
319: * @param string control name
320: * @param string label
321: * @param array options from which to choose
322: * @return RadioList
323: */
324: public function addRadioList($name, $label = NULL, array $items = NULL)
325: {
326: return $this[$name] = new RadioList($label, $items);
327: }
328:
329:
330:
331: /**
332: * Adds select box control that allows single item selection.
333: * @param string control name
334: * @param string label
335: * @param array items from which to choose
336: * @param int number of rows that should be visible
337: * @return SelectBox
338: */
339: public function addSelect($name, $label = NULL, array $items = NULL, $size = NULL)
340: {
341: return $this[$name] = new SelectBox($label, $items, $size);
342: }
343:
344:
345:
346: /**
347: * Adds select box control that allows multiple item selection.
348: * @param string control name
349: * @param string label
350: * @param array options from which to choose
351: * @param int number of rows that should be visible
352: * @return MultiSelectBox
353: */
354: public function addMultiSelect($name, $label = NULL, array $items = NULL, $size = NULL)
355: {
356: return $this[$name] = new MultiSelectBox($label, $items, $size);
357: }
358:
359:
360:
361: /**
362: * Adds button used to submit form.
363: * @param string control name
364: * @param string caption
365: * @return SubmitButton
366: */
367: public function addSubmit($name, $caption = NULL)
368: {
369: return $this[$name] = new SubmitButton($caption);
370: }
371:
372:
373:
374: /**
375: * Adds push buttons with no default behavior.
376: * @param string control name
377: * @param string caption
378: * @return Button
379: */
380: public function addButton($name, $caption)
381: {
382: return $this[$name] = new Button($caption);
383: }
384:
385:
386:
387: /**
388: * Adds graphical button used to submit form.
389: * @param string control name
390: * @param string URI of the image
391: * @param string alternate text for the image
392: * @return ImageButton
393: */
394: public function addImage($name, $src = NULL, $alt = NULL)
395: {
396: return $this[$name] = new ImageButton($src, $alt);
397: }
398:
399:
400:
401: /**
402: * Adds naming container to the form.
403: * @param string name
404: * @return FormContainer
405: */
406: public function addContainer($name)
407: {
408: $control = new FormContainer;
409: $control->currentGroup = $this->currentGroup;
410: return $this[$name] = $control;
411: }
412:
413:
414:
415: /********************* interface ArrayAccess ****************d*g**/
416:
417:
418:
419: /**
420: * Adds the component to the container.
421: * @param string component name
422: * @param IComponent
423: * @return void
424: */
425: final public function offsetSet($name, $component)
426: {
427: $this->addComponent($component, $name);
428: }
429:
430:
431:
432: /**
433: * Returns component specified by name. Throws exception if component doesn't exist.
434: * @param string component name
435: * @return IComponent
436: * @throws InvalidArgumentException
437: */
438: final public function offsetGet($name)
439: {
440: return $this->getComponent($name, TRUE);
441: }
442:
443:
444:
445: /**
446: * Does component specified by name exists?
447: * @param string component name
448: * @return bool
449: */
450: final public function offsetExists($name)
451: {
452: return $this->getComponent($name, FALSE) !== NULL;
453: }
454:
455:
456:
457: /**
458: * Removes component from the container.
459: * @param string component name
460: * @return void
461: */
462: final public function offsetUnset($name)
463: {
464: $component = $this->getComponent($name, FALSE);
465: if ($component !== NULL) {
466: $this->removeComponent($component);
467: }
468: }
469:
470:
471:
472: /**
473: * Prevents cloning.
474: */
475: final public function __clone()
476: {
477: throw new NotImplementedException('Form cloning is not supported yet.');
478: }
479:
480:
481:
482: /********************* deprecated ****************d*g**/
483:
484: /** @deprecated */
485: function addFile($name, $label = NULL)
486: {
487: trigger_error(__METHOD__ . '() is deprecated; use addUpload() instead.', E_USER_WARNING);
488: return $this->addUpload($name, $label);
489: }
490:
491: }
492: