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