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

Classes

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