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