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