1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Nette\Forms\Controls;
9:
10: use Nette;
11: use Nette\Forms\Form;
12: use Nette\Forms\IControl;
13: use Nette\Forms\Rules;
14: use Nette\Utils\Html;
15:
16:
17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35:
36: abstract class BaseControl extends Nette\ComponentModel\Component implements IControl
37: {
38:
39: public static $idMask = 'frm-%s';
40:
41:
42: public $caption;
43:
44:
45: protected $value;
46:
47:
48: protected $control;
49:
50:
51: protected $label;
52:
53:
54: protected $disabled = false;
55:
56:
57: private $errors = [];
58:
59:
60: private $omitted;
61:
62:
63: private $rules;
64:
65:
66: private $translator = true;
67:
68:
69: private $options = [];
70:
71:
72: private static $autoOptional = false;
73:
74:
75: 76: 77:
78: public function __construct($caption = null)
79: {
80: $this->monitor(Form::class);
81: parent::__construct();
82: $this->control = Html::el('input', ['type' => null, 'name' => null]);
83: $this->label = Html::el('label');
84: $this->caption = $caption;
85: $this->rules = new Rules($this);
86: if (self::$autoOptional) {
87: $this->setRequired(false);
88: }
89: $this->setValue(null);
90: }
91:
92:
93: 94: 95: 96: 97:
98: protected function attached($form)
99: {
100: if (!$this->isDisabled() && $form instanceof Form && $form->isAnchored() && $form->isSubmitted()) {
101: $this->loadHttpData();
102: }
103: }
104:
105:
106: 107: 108: 109: 110:
111: public function getForm($throw = true)
112: {
113: return $this->lookup(Form::class, $throw);
114: }
115:
116:
117: 118: 119: 120:
121: public function loadHttpData()
122: {
123: $this->setValue($this->getHttpData(Form::DATA_TEXT));
124: }
125:
126:
127: 128: 129: 130:
131: protected function getHttpData($type, $htmlTail = null)
132: {
133: return $this->getForm()->getHttpData($type, $this->getHtmlName() . $htmlTail);
134: }
135:
136:
137: 138: 139: 140:
141: public function getHtmlName()
142: {
143: return Nette\Forms\Helpers::generateHtmlName($this->lookupPath(Form::class));
144: }
145:
146:
147:
148:
149:
150: 151: 152: 153: 154:
155: public function setValue($value)
156: {
157: $this->value = $value;
158: return $this;
159: }
160:
161:
162: 163: 164: 165:
166: public function getValue()
167: {
168: return $this->value;
169: }
170:
171:
172: 173: 174: 175:
176: public function isFilled()
177: {
178: $value = $this->getValue();
179: return $value !== null && $value !== [] && $value !== '';
180: }
181:
182:
183: 184: 185: 186:
187: public function setDefaultValue($value)
188: {
189: $form = $this->getForm(false);
190: if ($this->isDisabled() || !$form || !$form->isAnchored() || !$form->isSubmitted()) {
191: $this->setValue($value);
192: }
193: return $this;
194: }
195:
196:
197: 198: 199: 200: 201:
202: public function setDisabled($value = true)
203: {
204: if ($this->disabled = (bool) $value) {
205: $this->setValue(null);
206: } elseif (($form = $this->getForm(false)) && $form->isAnchored() && $form->isSubmitted()) {
207: $this->loadHttpData();
208: }
209: return $this;
210: }
211:
212:
213: 214: 215: 216:
217: public function isDisabled()
218: {
219: return $this->disabled === true;
220: }
221:
222:
223: 224: 225: 226: 227:
228: public function setOmitted($value = true)
229: {
230: $this->omitted = (bool) $value;
231: return $this;
232: }
233:
234:
235: 236: 237: 238:
239: public function isOmitted()
240: {
241: return $this->omitted || ($this->isDisabled() && $this->omitted === null);
242: }
243:
244:
245:
246:
247:
248: 249: 250: 251:
252: public function getControl()
253: {
254: $this->setOption('rendered', true);
255: $el = clone $this->control;
256: return $el->addAttributes([
257: 'name' => $this->getHtmlName(),
258: 'id' => $this->getHtmlId(),
259: 'required' => $this->isRequired(),
260: 'disabled' => $this->isDisabled(),
261: 'data-nette-rules' => Nette\Forms\Helpers::exportRules($this->rules) ?: null,
262: ]);
263: }
264:
265:
266: 267: 268: 269: 270:
271: public function getLabel($caption = null)
272: {
273: $label = clone $this->label;
274: $label->for = $this->getHtmlId();
275: $label->setText($this->translate($caption === null ? $this->caption : $caption));
276: return $label;
277: }
278:
279:
280: 281: 282:
283: public function getControlPart()
284: {
285: return $this->getControl();
286: }
287:
288:
289: 290: 291:
292: public function getLabelPart()
293: {
294: return $this->getLabel();
295: }
296:
297:
298: 299: 300: 301:
302: public function getControlPrototype()
303: {
304: return $this->control;
305: }
306:
307:
308: 309: 310: 311:
312: public function getLabelPrototype()
313: {
314: return $this->label;
315: }
316:
317:
318: 319: 320: 321: 322:
323: public function setHtmlId($id)
324: {
325: $this->control->id = $id;
326: return $this;
327: }
328:
329:
330: 331: 332: 333:
334: public function getHtmlId()
335: {
336: if (!isset($this->control->id)) {
337: $this->control->id = sprintf(self::$idMask, $this->lookupPath());
338: }
339: return $this->control->id;
340: }
341:
342:
343: 344: 345: 346: 347: 348:
349: public function setHtmlAttribute($name, $value = true)
350: {
351: return $this->setAttribute($name, $value);
352: }
353:
354:
355: 356: 357: 358: 359: 360:
361: public function setAttribute($name, $value = true)
362: {
363: $this->control->$name = $value;
364: return $this;
365: }
366:
367:
368:
369:
370:
371: 372: 373: 374:
375: public function setTranslator(Nette\Localization\ITranslator $translator = null)
376: {
377: $this->translator = $translator;
378: return $this;
379: }
380:
381:
382: 383: 384: 385:
386: public function getTranslator()
387: {
388: if ($this->translator === true) {
389: return $this->getForm(false) ? $this->getForm()->getTranslator() : null;
390: }
391: return $this->translator;
392: }
393:
394:
395: 396: 397: 398: 399: 400:
401: public function translate($value, $count = null)
402: {
403: if ($translator = $this->getTranslator()) {
404: $tmp = is_array($value) ? [&$value] : [[&$value]];
405: foreach ($tmp[0] as &$v) {
406: if ($v != null && !$v instanceof Html) {
407: $v = $translator->translate($v, $count);
408: }
409: }
410: }
411: return $value;
412: }
413:
414:
415:
416:
417:
418: 419: 420: 421: 422: 423: 424:
425: public function addRule($validator, $errorMessage = null, $arg = null)
426: {
427: $this->rules->addRule($validator, $errorMessage, $arg);
428: return $this;
429: }
430:
431:
432: 433: 434: 435: 436: 437:
438: public function addCondition($validator, $value = null)
439: {
440: return $this->rules->addCondition($validator, $value);
441: }
442:
443:
444: 445: 446: 447: 448: 449: 450:
451: public function addConditionOn(IControl $control, $validator, $value = null)
452: {
453: return $this->rules->addConditionOn($control, $validator, $value);
454: }
455:
456:
457: 458: 459:
460: public function getRules()
461: {
462: return $this->rules;
463: }
464:
465:
466: 467: 468: 469: 470:
471: public function setRequired($value = true)
472: {
473: $this->rules->setRequired($value);
474: return $this;
475: }
476:
477:
478: 479: 480: 481:
482: public function isRequired()
483: {
484: return $this->rules->isRequired();
485: }
486:
487:
488: 489: 490: 491:
492: public function validate()
493: {
494: if ($this->isDisabled()) {
495: return;
496: }
497: $this->cleanErrors();
498: $this->rules->validate();
499: }
500:
501:
502: 503: 504: 505: 506:
507: public function addError($message, $translate = true)
508: {
509: $this->errors[] = $translate ? $this->translate($message) : $message;
510: }
511:
512:
513: 514: 515: 516:
517: public function getError()
518: {
519: return $this->errors ? implode(' ', array_unique($this->errors)) : null;
520: }
521:
522:
523: 524: 525: 526:
527: public function getErrors()
528: {
529: return array_unique($this->errors);
530: }
531:
532:
533: 534: 535:
536: public function hasErrors()
537: {
538: return (bool) $this->errors;
539: }
540:
541:
542: 543: 544:
545: public function cleanErrors()
546: {
547: $this->errors = [];
548: }
549:
550:
551: 552: 553: 554:
555: public static function enableAutoOptionalMode()
556: {
557: self::$autoOptional = true;
558: }
559:
560:
561:
562:
563:
564: 565: 566: 567:
568: public function setOption($key, $value)
569: {
570: if ($value === null) {
571: unset($this->options[$key]);
572: } else {
573: $this->options[$key] = $value;
574: }
575: return $this;
576: }
577:
578:
579: 580: 581: 582:
583: public function getOption($key, $default = null)
584: {
585: return isset($this->options[$key]) ? $this->options[$key] : $default;
586: }
587:
588:
589: 590: 591: 592:
593: public function getOptions()
594: {
595: return $this->options;
596: }
597:
598:
599:
600:
601:
602: public function __call($name, $args)
603: {
604: if ($callback = Nette\Utils\ObjectMixin::getExtensionMethod(get_class($this), $name)) {
605: return Nette\Utils\Callback::invoke($callback, $this, ...$args);
606: }
607: return parent::__call($name, $args);
608: }
609:
610:
611: public static function extensionMethod($name, $callback = null)
612: {
613: if (strpos($name, '::') !== false) {
614: list(, $name) = explode('::', $name);
615: }
616: Nette\Utils\ObjectMixin::setExtensionMethod(get_called_class(), $name, $callback);
617: }
618: }
619: