Packages

  • Nette
    • Application
      • Diagnostics
      • Responses
      • Routers
      • UI
    • Caching
      • Storages
    • ComponentModel
    • Config
      • Adapters
      • Extensions
    • Database
      • Diagnostics
      • Drivers
      • Reflection
      • Table
    • DI
      • Diagnostics
    • Diagnostics
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Latte
      • Macros
    • Loaders
    • Localization
    • Mail
    • Reflection
    • Security
      • Diagnostics
    • Templating
    • Utils
      • PhpGenerator
  • NetteModule
  • None
  • PHP

Classes

  • Button
  • Checkbox
  • FormControl
  • HiddenField
  • ImageButton
  • MultiSelectBox
  • RadioList
  • SelectBox
  • SubmitButton
  • TextArea
  • TextBase
  • TextInput
  • UploadControl
  • Overview
  • Package
  • Class
  • Tree
  • Deprecated
  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:  * @package Nette\Forms\Controls
 11:  */
 12: 
 13: 
 14: 
 15: /**
 16:  * Base class that implements the basic functionality common to form controls.
 17:  *
 18:  * @author     David Grudl
 19:  *
 20:  * @property-read Form $form
 21:  * @property-read string $htmlName
 22:  * @property   string $htmlId
 23:  * @property-read array $options
 24:  * @property   ITranslator|NULL $translator
 25:  * @property   mixed $value
 26:  * @property-read bool $filled
 27:  * @property-write $defaultValue
 28:  * @property   bool $disabled
 29:  * @property-read Html $control
 30:  * @property-read Html $label
 31:  * @property-read Html $controlPrototype
 32:  * @property-read Html $labelPrototype
 33:  * @property-read Rules $rules
 34:  * @property   bool $required
 35:  * @property-read array $errors
 36:  * @package Nette\Forms\Controls
 37:  */
 38: abstract class FormControl extends Component implements IFormControl
 39: {
 40:     /** @var string */
 41:     public static $idMask = 'frm%s-%s';
 42: 
 43:     /** @var string textual caption or label */
 44:     public $caption;
 45: 
 46:     /** @var mixed unfiltered control value */
 47:     protected $value;
 48: 
 49:     /** @var Html  control element template */
 50:     protected $control;
 51: 
 52:     /** @var Html  label element template */
 53:     protected $label;
 54: 
 55:     /** @var array */
 56:     private $errors = array();
 57: 
 58:     /** @var bool */
 59:     private $disabled = FALSE;
 60: 
 61:     /** @var string */
 62:     private $htmlId;
 63: 
 64:     /** @var string */
 65:     private $htmlName;
 66: 
 67:     /** @var Rules */
 68:     private $rules;
 69: 
 70:     /** @var ITranslator */
 71:     private $translator = TRUE; // means autodetect
 72: 
 73:     /** @var array user options */
 74:     private $options = array();
 75: 
 76: 
 77:     /**
 78:      * @param  string  caption
 79:      */
 80:     public function __construct($caption = NULL)
 81:     {
 82:         $this->monitor('Form');
 83:         parent::__construct();
 84:         $this->control = Html::el('input');
 85:         $this->label = Html::el('label');
 86:         $this->caption = $caption;
 87:         $this->rules = new Rules($this);
 88:     }
 89: 
 90: 
 91:     /**
 92:      * This method will be called when the component becomes attached to Form.
 93:      * @param  IComponent
 94:      * @return void
 95:      */
 96:     protected function attached($form)
 97:     {
 98:         if (!$this->disabled && $form instanceof Form && $form->isAnchored() && $form->isSubmitted()) {
 99:             $this->htmlName = NULL;
100:             $this->loadHttpData();
101:         }
102:     }
103: 
104: 
105:     /**
106:      * Returns form.
107:      * @param  bool   throw exception if form doesn't exist?
108:      * @return Form
109:      */
110:     public function getForm($need = TRUE)
111:     {
112:         return $this->lookup('Form', $need);
113:     }
114: 
115: 
116:     /**
117:      * Returns HTML name of control.
118:      * @return string
119:      */
120:     public function getHtmlName()
121:     {
122:         if ($this->htmlName === NULL) {
123:             $name = str_replace(self::NAME_SEPARATOR, '][', $this->lookupPath('Form'), $count);
124:             if ($count) {
125:                 $name = substr_replace($name, '', strpos($name, ']'), 1) . ']';
126:             }
127:             if (is_numeric($name) || in_array($name, array('attributes','children','elements','focus','length','reset','style','submit','onsubmit'))) {
128:                 $name .= '_';
129:             }
130:             $this->htmlName = $name;
131:         }
132:         return $this->htmlName;
133:     }
134: 
135: 
136:     /**
137:      * Changes control's HTML id.
138:      * @param  string new ID, or FALSE or NULL
139:      * @return self
140:      */
141:     public function setHtmlId($id)
142:     {
143:         $this->htmlId = $id;
144:         return $this;
145:     }
146: 
147: 
148:     /**
149:      * Returns control's HTML id.
150:      * @return string
151:      */
152:     public function getHtmlId()
153:     {
154:         if ($this->htmlId === FALSE) {
155:             return NULL;
156: 
157:         } elseif ($this->htmlId === NULL) {
158:             $this->htmlId = sprintf(self::$idMask, $this->getForm()->getName(), $this->lookupPath('Form'));
159:         }
160:         return $this->htmlId;
161:     }
162: 
163: 
164:     /**
165:      * Changes control's HTML attribute.
166:      * @param  string name
167:      * @param  mixed  value
168:      * @return self
169:      */
170:     public function setAttribute($name, $value = TRUE)
171:     {
172:         $this->control->$name = $value;
173:         return $this;
174:     }
175: 
176: 
177:     /**
178:      * Sets user-specific option.
179:      * Options recognized by DefaultFormRenderer
180:      * - 'description' - textual or Html object description
181:      *
182:      * @param  string key
183:      * @param  mixed  value
184:      * @return self
185:      */
186:     public function setOption($key, $value)
187:     {
188:         if ($value === NULL) {
189:             unset($this->options[$key]);
190: 
191:         } else {
192:             $this->options[$key] = $value;
193:         }
194:         return $this;
195:     }
196: 
197: 
198:     /**
199:      * Returns user-specific option.
200:      * @param  string key
201:      * @param  mixed  default value
202:      * @return mixed
203:      */
204:     final public function getOption($key, $default = NULL)
205:     {
206:         return isset($this->options[$key]) ? $this->options[$key] : $default;
207:     }
208: 
209: 
210:     /**
211:      * Returns user-specific options.
212:      * @return array
213:      */
214:     final public function getOptions()
215:     {
216:         return $this->options;
217:     }
218: 
219: 
220:     /********************* translator ****************d*g**/
221: 
222: 
223:     /**
224:      * Sets translate adapter.
225:      * @return self
226:      */
227:     public function setTranslator(ITranslator $translator = NULL)
228:     {
229:         $this->translator = $translator;
230:         return $this;
231:     }
232: 
233: 
234:     /**
235:      * Returns translate adapter.
236:      * @return ITranslator|NULL
237:      */
238:     final public function getTranslator()
239:     {
240:         if ($this->translator === TRUE) {
241:             return $this->getForm(FALSE) ? $this->getForm()->getTranslator() : NULL;
242:         }
243:         return $this->translator;
244:     }
245: 
246: 
247:     /**
248:      * Returns translated string.
249:      * @param  string
250:      * @param  int      plural count
251:      * @return string
252:      */
253:     public function translate($s, $count = NULL)
254:     {
255:         $translator = $this->getTranslator();
256:         return $translator === NULL || $s == NULL ? $s : $translator->translate($s, $count); // intentionally ==
257:     }
258: 
259: 
260:     /********************* interface IFormControl ****************d*g**/
261: 
262: 
263:     /**
264:      * Sets control's value.
265:      * @return self
266:      */
267:     public function setValue($value)
268:     {
269:         $this->value = $value;
270:         return $this;
271:     }
272: 
273: 
274:     /**
275:      * Returns control's value.
276:      * @return mixed
277:      */
278:     public function getValue()
279:     {
280:         return $this->value;
281:     }
282: 
283: 
284:     /**
285:      * Is control filled?
286:      * @return bool
287:      */
288:     public function isFilled()
289:     {
290:         return (string) $this->getValue() !== ''; // NULL, FALSE, '' ==> FALSE
291:     }
292: 
293: 
294:     /**
295:      * Sets control's default value.
296:      * @return self
297:      */
298:     public function setDefaultValue($value)
299:     {
300:         $form = $this->getForm(FALSE);
301:         if (!$form || !$form->isAnchored() || !$form->isSubmitted()) {
302:             $this->setValue($value);
303:         }
304:         return $this;
305:     }
306: 
307: 
308:     /**
309:      * Loads HTTP data.
310:      * @return void
311:      */
312:     public function loadHttpData()
313:     {
314:         $path = explode('[', strtr(str_replace(array('[]', ']'), '', $this->getHtmlName()), '.', '_'));
315:         $this->setValue(Arrays::get($this->getForm()->getHttpData(), $path, NULL));
316:     }
317: 
318: 
319:     /**
320:      * Disables or enables control.
321:      * @param  bool
322:      * @return self
323:      */
324:     public function setDisabled($value = TRUE)
325:     {
326:         $this->disabled = (bool) $value;
327:         return $this;
328:     }
329: 
330: 
331:     /**
332:      * Is control disabled?
333:      * @return bool
334:      */
335:     public function isDisabled()
336:     {
337:         return $this->disabled;
338:     }
339: 
340: 
341:     /********************* rendering ****************d*g**/
342: 
343: 
344:     /**
345:      * Generates control's HTML element.
346:      * @return Html
347:      */
348:     public function getControl()
349:     {
350:         $this->setOption('rendered', TRUE);
351: 
352:         $control = clone $this->control;
353:         $control->name = $this->getHtmlName();
354:         $control->disabled = $this->disabled;
355:         $control->id = $this->getHtmlId();
356:         $control->required = $this->isRequired();
357: 
358:         $rules = self::exportRules($this->rules);
359:         $rules = substr(PHP_VERSION_ID >= 50400 ? json_encode($rules, JSON_UNESCAPED_UNICODE) : json_encode($rules), 1, -1);
360:         $rules = preg_replace('#"([a-z0-9_]+)":#i', '$1:', $rules);
361:         $rules = preg_replace('#(?<!\\\\)"(?!:[^a-z])([^\\\\\',]*)"#i', "'$1'", $rules);
362:         $control->data('nette-rules', $rules ? $rules : NULL);
363: 
364:         return $control;
365:     }
366: 
367: 
368:     /**
369:      * Generates label's HTML element.
370:      * @param  string
371:      * @return Html
372:      */
373:     public function getLabel($caption = NULL)
374:     {
375:         $label = clone $this->label;
376:         $label->for = $this->getHtmlId();
377:         if ($caption !== NULL) {
378:             $label->setText($this->translate($caption));
379: 
380:         } elseif ($this->caption instanceof Html) {
381:             $label->add($this->caption);
382: 
383:         } else {
384:             $label->setText($this->translate($this->caption));
385:         }
386:         return $label;
387:     }
388: 
389: 
390:     /**
391:      * Returns control's HTML element template.
392:      * @return Html
393:      */
394:     final public function getControlPrototype()
395:     {
396:         return $this->control;
397:     }
398: 
399: 
400:     /**
401:      * Returns label's HTML element template.
402:      * @return Html
403:      */
404:     final public function getLabelPrototype()
405:     {
406:         return $this->label;
407:     }
408: 
409: 
410:     /********************* rules ****************d*g**/
411: 
412: 
413:     /**
414:      * Adds a validation rule.
415:      * @param  mixed      rule type
416:      * @param  string     message to display for invalid data
417:      * @param  mixed      optional rule arguments
418:      * @return self
419:      */
420:     public function addRule($operation, $message = NULL, $arg = NULL)
421:     {
422:         $this->rules->addRule($operation, $message, $arg);
423:         return $this;
424:     }
425: 
426: 
427:     /**
428:      * Adds a validation condition a returns new branch.
429:      * @param  mixed     condition type
430:      * @param  mixed     optional condition arguments
431:      * @return Rules      new branch
432:      */
433:     public function addCondition($operation, $value = NULL)
434:     {
435:         return $this->rules->addCondition($operation, $value);
436:     }
437: 
438: 
439:     /**
440:      * Adds a validation condition based on another control a returns new branch.
441:      * @param  IFormControl form control
442:      * @param  mixed      condition type
443:      * @param  mixed      optional condition arguments
444:      * @return Rules      new branch
445:      */
446:     public function addConditionOn(IFormControl $control, $operation, $value = NULL)
447:     {
448:         return $this->rules->addConditionOn($control, $operation, $value);
449:     }
450: 
451: 
452:     /**
453:      * @return Rules
454:      */
455:     final public function getRules()
456:     {
457:         return $this->rules;
458:     }
459: 
460: 
461:     /**
462:      * Makes control mandatory.
463:      * @param  string  error message
464:      * @return self
465:      */
466:     final public function setRequired($message = NULL)
467:     {
468:         return $this->addRule(Form::FILLED, $message);
469:     }
470: 
471: 
472:     /**
473:      * Is control mandatory?
474:      * @return bool
475:      */
476:     final public function isRequired()
477:     {
478:         foreach ($this->rules as $rule) {
479:             if ($rule->type === Rule::VALIDATOR && !$rule->isNegative && $rule->operation === Form::FILLED) {
480:                 return TRUE;
481:             }
482:         }
483:         return FALSE;
484:     }
485: 
486: 
487:     /**
488:      * @return array
489:      */
490:     protected static function exportRules($rules)
491:     {
492:         $payload = array();
493:         foreach ($rules as $rule) {
494:             if (!is_string($op = $rule->operation)) {
495:                 $op = new Callback($op);
496:                 if (!$op->isStatic()) {
497:                     continue;
498:                 }
499:             }
500:             if ($rule->type === Rule::VALIDATOR) {
501:                 $item = array('op' => ($rule->isNegative ? '~' : '') . $op, 'msg' => $rules->formatMessage($rule, FALSE));
502: 
503:             } elseif ($rule->type === Rule::CONDITION) {
504:                 $item = array(
505:                     'op' => ($rule->isNegative ? '~' : '') . $op,
506:                     'rules' => self::exportRules($rule->subRules),
507:                     'control' => $rule->control->getHtmlName()
508:                 );
509:                 if ($rule->subRules->getToggles()) {
510:                     $item['toggle'] = $rule->subRules->getToggles();
511:                 }
512:             }
513: 
514:             if (is_array($rule->arg)) {
515:                 foreach ($rule->arg as $key => $value) {
516:                     $item['arg'][$key] = $value instanceof IFormControl ? (object) array('control' => $value->getHtmlName()) : $value;
517:                 }
518:             } elseif ($rule->arg !== NULL) {
519:                 $item['arg'] = $rule->arg instanceof IFormControl ? (object) array('control' => $rule->arg->getHtmlName()) : $rule->arg;
520:             }
521: 
522:             $payload[] = $item;
523:         }
524:         return $payload;
525:     }
526: 
527: 
528:     /********************* validation ****************d*g**/
529: 
530: 
531:     /**
532:      * Equal validator: are control's value and second parameter equal?
533:      * @return bool
534:      */
535:     public static function validateEqual(IFormControl $control, $arg)
536:     {
537:         $value = $control->getValue();
538:         foreach ((is_array($value) ? $value : array($value)) as $val) {
539:             foreach ((is_array($arg) ? $arg : array($arg)) as $item) {
540:                 if ((string) $val === (string) ($item instanceof IFormControl ? $item->value : $item)) {
541:                     return TRUE;
542:                 }
543:             }
544:         }
545:         return FALSE;
546:     }
547: 
548: 
549:     /**
550:      * Is control's value not equal with second parameter?
551:      * @return bool
552:      */
553:     public static function validateNotEqual(IFormControl $control, $arg)
554:     {
555:         return !self::validateEqual($control, $arg);
556:     }
557: 
558: 
559:     /**
560:      * Filled validator: is control filled?
561:      * @return bool
562:      */
563:     public static function validateFilled(IFormControl $control)
564:     {
565:         return $control->isFilled();
566:     }
567: 
568: 
569:     /**
570:      * Is control not filled?
571:      * @return bool
572:      */
573:     public static function validateBlank(IFormControl $control)
574:     {
575:         return !$control->isFilled();
576:     }
577: 
578: 
579:     /**
580:      * Valid validator: is control valid?
581:      * @return bool
582:      */
583:     public static function validateValid(IFormControl $control)
584:     {
585:         return $control->rules->validate(TRUE);
586:     }
587: 
588: 
589:     /**
590:      * Adds error message to the list.
591:      * @param  string  error message
592:      * @return void
593:      */
594:     public function addError($message)
595:     {
596:         if (!in_array($message, $this->errors, TRUE)) {
597:             $this->errors[] = $message;
598:         }
599:         $this->getForm()->addError($message);
600:     }
601: 
602: 
603:     /**
604:      * Returns errors corresponding to control.
605:      * @return array
606:      */
607:     public function getErrors()
608:     {
609:         return $this->errors;
610:     }
611: 
612: 
613:     /**
614:      * @return bool
615:      */
616:     public function hasErrors()
617:     {
618:         return (bool) $this->errors;
619:     }
620: 
621: 
622:     /**
623:      * @return void
624:      */
625:     public function cleanErrors()
626:     {
627:         $this->errors = array();
628:     }
629: 
630: }
631: 
Nette Framework 2.0.13 (for PHP 5.2, un-prefixed) API API documentation generated by ApiGen 2.8.0