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