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