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