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