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 NArrayHash $values
22: * @property-read bool $valid
23: * @property NFormGroup $currentGroup
24: * @property-read ArrayIterator $controls
25: * @property-read NForm $form
26: * @package Nette\Forms
27: */
28: class NFormContainer extends NComponentContainer implements ArrayAccess
29: {
30: /** @var array of function(Form $sender); Occurs when the form is validated */
31: public $onValidate;
32:
33: /** @var NFormGroup */
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 NFormContainer 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 NFormContainer 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 NFormContainer) {
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: * @return NArrayHash|array
103: */
104: public function getValues($asArray = FALSE)
105: {
106: $values = $asArray ? array() : new NArrayHash;
107: foreach ($this->getComponents() as $name => $control) {
108: if ($control instanceof IFormControl && !$control->isDisabled() && !$control instanceof ISubmitterControl) {
109: $values[$name] = $control->getValue();
110:
111: } elseif ($control instanceof NFormContainer) {
112: $values[$name] = $control->getValues($asArray);
113: }
114: }
115: return $values;
116: }
117:
118:
119:
120: /********************* validation ****************d*g**/
121:
122:
123:
124: /**
125: * Is form valid?
126: * @return bool
127: */
128: public function isValid()
129: {
130: if ($this->valid === NULL) {
131: $this->validate();
132: }
133: return $this->valid;
134: }
135:
136:
137:
138: /**
139: * Performs the server side validation.
140: * @return void
141: */
142: public function validate()
143: {
144: $this->valid = TRUE;
145: $this->onValidate($this);
146: foreach ($this->getControls() as $control) {
147: if (!$control->getRules()->validate()) {
148: $this->valid = FALSE;
149: }
150: }
151: }
152:
153:
154:
155: /********************* form building ****************d*g**/
156:
157:
158:
159: /**
160: * @param NFormGroup
161: * @return NFormContainer provides a fluent interface
162: */
163: public function setCurrentGroup(NFormGroup $group = NULL)
164: {
165: $this->currentGroup = $group;
166: return $this;
167: }
168:
169:
170:
171: /**
172: * Returns current group.
173: * @return NFormGroup
174: */
175: public function getCurrentGroup()
176: {
177: return $this->currentGroup;
178: }
179:
180:
181:
182: /**
183: * Adds the specified component to the IComponentContainer.
184: * @param IComponent
185: * @param string
186: * @param string
187: * @return NFormContainer provides a fluent interface
188: * @throws InvalidStateException
189: */
190: public function addComponent(IComponent $component, $name, $insertBefore = NULL)
191: {
192: parent::addComponent($component, $name, $insertBefore);
193: if ($this->currentGroup !== NULL && $component instanceof IFormControl) {
194: $this->currentGroup->add($component);
195: }
196: return $this;
197: }
198:
199:
200:
201: /**
202: * Iterates over all form controls.
203: * @return ArrayIterator
204: */
205: public function getControls()
206: {
207: return $this->getComponents(TRUE, 'IFormControl');
208: }
209:
210:
211:
212: /**
213: * Returns form.
214: * @param bool throw exception if form doesn't exist?
215: * @return NForm
216: */
217: public function getForm($need = TRUE)
218: {
219: return $this->lookup('NForm', $need);
220: }
221:
222:
223:
224: /********************* control factories ****************d*g**/
225:
226:
227:
228: /**
229: * Adds single-line text input control to the form.
230: * @param string control name
231: * @param string label
232: * @param int width of the control
233: * @param int maximum number of characters the user may enter
234: * @return NTextInput
235: */
236: public function addText($name, $label = NULL, $cols = NULL, $maxLength = NULL)
237: {
238: return $this[$name] = new NTextInput($label, $cols, $maxLength);
239: }
240:
241:
242:
243: /**
244: * Adds single-line text input control used for sensitive input such as passwords.
245: * @param string control name
246: * @param string label
247: * @param int width of the control
248: * @param int maximum number of characters the user may enter
249: * @return NTextInput
250: */
251: public function addPassword($name, $label = NULL, $cols = NULL, $maxLength = NULL)
252: {
253: $control = new NTextInput($label, $cols, $maxLength);
254: $control->setType('password');
255: return $this[$name] = $control;
256: }
257:
258:
259:
260: /**
261: * Adds multi-line text input control to the form.
262: * @param string control name
263: * @param string label
264: * @param int width of the control
265: * @param int height of the control in text lines
266: * @return NTextArea
267: */
268: public function addTextArea($name, $label = NULL, $cols = 40, $rows = 10)
269: {
270: return $this[$name] = new NTextArea($label, $cols, $rows);
271: }
272:
273:
274:
275: /**
276: * Adds control that allows the user to upload files.
277: * @param string control name
278: * @param string label
279: * @return NUploadControl
280: */
281: public function addUpload($name, $label = NULL)
282: {
283: return $this[$name] = new NUploadControl($label);
284: }
285:
286:
287:
288: /**
289: * Adds hidden form control used to store a non-displayed value.
290: * @param string control name
291: * @param mixed default value
292: * @return NHiddenField
293: */
294: public function addHidden($name, $default = NULL)
295: {
296: $control = new NHiddenField;
297: $control->setDefaultValue($default);
298: return $this[$name] = $control;
299: }
300:
301:
302:
303: /**
304: * Adds check box control to the form.
305: * @param string control name
306: * @param string caption
307: * @return NCheckbox
308: */
309: public function addCheckbox($name, $caption = NULL)
310: {
311: return $this[$name] = new NCheckbox($caption);
312: }
313:
314:
315:
316: /**
317: * Adds set of radio button controls to the form.
318: * @param string control name
319: * @param string label
320: * @param array options from which to choose
321: * @return NRadioList
322: */
323: public function addRadioList($name, $label = NULL, array $items = NULL)
324: {
325: return $this[$name] = new NRadioList($label, $items);
326: }
327:
328:
329:
330: /**
331: * Adds select box control that allows single item selection.
332: * @param string control name
333: * @param string label
334: * @param array items from which to choose
335: * @param int number of rows that should be visible
336: * @return NSelectBox
337: */
338: public function addSelect($name, $label = NULL, array $items = NULL, $size = NULL)
339: {
340: return $this[$name] = new NSelectBox($label, $items, $size);
341: }
342:
343:
344:
345: /**
346: * Adds select box control that allows multiple item selection.
347: * @param string control name
348: * @param string label
349: * @param array options from which to choose
350: * @param int number of rows that should be visible
351: * @return NMultiSelectBox
352: */
353: public function addMultiSelect($name, $label = NULL, array $items = NULL, $size = NULL)
354: {
355: return $this[$name] = new NMultiSelectBox($label, $items, $size);
356: }
357:
358:
359:
360: /**
361: * Adds button used to submit form.
362: * @param string control name
363: * @param string caption
364: * @return NSubmitButton
365: */
366: public function addSubmit($name, $caption = NULL)
367: {
368: return $this[$name] = new NSubmitButton($caption);
369: }
370:
371:
372:
373: /**
374: * Adds push buttons with no default behavior.
375: * @param string control name
376: * @param string caption
377: * @return NButton
378: */
379: public function addButton($name, $caption)
380: {
381: return $this[$name] = new NButton($caption);
382: }
383:
384:
385:
386: /**
387: * Adds graphical button used to submit form.
388: * @param string control name
389: * @param string URI of the image
390: * @param string alternate text for the image
391: * @return NImageButton
392: */
393: public function addImage($name, $src = NULL, $alt = NULL)
394: {
395: return $this[$name] = new NImageButton($src, $alt);
396: }
397:
398:
399:
400: /**
401: * Adds naming container to the form.
402: * @param string name
403: * @return NFormContainer
404: */
405: public function addContainer($name)
406: {
407: $control = new NFormContainer;
408: $control->currentGroup = $this->currentGroup;
409: return $this[$name] = $control;
410: }
411:
412:
413:
414: /********************* interface ArrayAccess ****************d*g**/
415:
416:
417:
418: /**
419: * Adds the component to the container.
420: * @param string component name
421: * @param IComponent
422: * @return void
423: */
424: final public function offsetSet($name, $component)
425: {
426: $this->addComponent($component, $name);
427: }
428:
429:
430:
431: /**
432: * Returns component specified by name. Throws exception if component doesn't exist.
433: * @param string component name
434: * @return IComponent
435: * @throws InvalidArgumentException
436: */
437: final public function offsetGet($name)
438: {
439: return $this->getComponent($name, TRUE);
440: }
441:
442:
443:
444: /**
445: * Does component specified by name exists?
446: * @param string component name
447: * @return bool
448: */
449: final public function offsetExists($name)
450: {
451: return $this->getComponent($name, FALSE) !== NULL;
452: }
453:
454:
455:
456: /**
457: * Removes component from the container.
458: * @param string component name
459: * @return void
460: */
461: final public function offsetUnset($name)
462: {
463: $component = $this->getComponent($name, FALSE);
464: if ($component !== NULL) {
465: $this->removeComponent($component);
466: }
467: }
468:
469:
470:
471: /**
472: * Prevents cloning.
473: */
474: final public function __clone()
475: {
476: throw new NotImplementedException('Form cloning is not supported yet.');
477: }
478:
479:
480:
481: /********************* deprecated ****************d*g**/
482:
483: /** @deprecated */
484: function addFile($name, $label = NULL)
485: {
486: trigger_error(__METHOD__ . '() is deprecated; use addUpload() instead.', E_USER_WARNING);
487: return $this->addUpload($name, $label);
488: }
489:
490: }
491: