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: $form = $this->getForm(false);
338: $this->control->id = sprintf(self::$idMask, $this->lookupPath(), $form ? $form->getName() : '');
339: }
340: return $this->control->id;
341: }
342:
343:
344: 345: 346: 347: 348: 349:
350: public function setHtmlAttribute($name, $value = true)
351: {
352: return $this->setAttribute($name, $value);
353: }
354:
355:
356: 357: 358: 359: 360: 361:
362: public function setAttribute($name, $value = true)
363: {
364: $this->control->$name = $value;
365: return $this;
366: }
367:
368:
369:
370:
371:
372: 373: 374: 375:
376: public function setTranslator(Nette\Localization\ITranslator $translator = null)
377: {
378: $this->translator = $translator;
379: return $this;
380: }
381:
382:
383: 384: 385: 386:
387: public function getTranslator()
388: {
389: if ($this->translator === true) {
390: return $this->getForm(false) ? $this->getForm()->getTranslator() : null;
391: }
392: return $this->translator;
393: }
394:
395:
396: 397: 398: 399: 400: 401:
402: public function translate($value, $count = null)
403: {
404: if ($translator = $this->getTranslator()) {
405: $tmp = is_array($value) ? [&$value] : [[&$value]];
406: foreach ($tmp[0] as &$v) {
407: if ($v != null && !$v instanceof Html) {
408: $v = $translator->translate($v, $count);
409: }
410: }
411: }
412: return $value;
413: }
414:
415:
416:
417:
418:
419: 420: 421: 422: 423: 424: 425:
426: public function addRule($validator, $errorMessage = null, $arg = null)
427: {
428: $this->rules->addRule($validator, $errorMessage, $arg);
429: return $this;
430: }
431:
432:
433: 434: 435: 436: 437: 438:
439: public function addCondition($validator, $value = null)
440: {
441: return $this->rules->addCondition($validator, $value);
442: }
443:
444:
445: 446: 447: 448: 449: 450: 451:
452: public function addConditionOn(IControl $control, $validator, $value = null)
453: {
454: return $this->rules->addConditionOn($control, $validator, $value);
455: }
456:
457:
458: 459: 460:
461: public function getRules()
462: {
463: return $this->rules;
464: }
465:
466:
467: 468: 469: 470: 471:
472: public function setRequired($value = true)
473: {
474: $this->rules->setRequired($value);
475: return $this;
476: }
477:
478:
479: 480: 481: 482:
483: public function isRequired()
484: {
485: return $this->rules->isRequired();
486: }
487:
488:
489: 490: 491: 492:
493: public function validate()
494: {
495: if ($this->isDisabled()) {
496: return;
497: }
498: $this->cleanErrors();
499: $this->rules->validate();
500: }
501:
502:
503: 504: 505: 506: 507:
508: public function addError($message, $translate = true)
509: {
510: $this->errors[] = $translate ? $this->translate($message) : $message;
511: }
512:
513:
514: 515: 516: 517:
518: public function getError()
519: {
520: return $this->errors ? implode(' ', array_unique($this->errors)) : null;
521: }
522:
523:
524: 525: 526: 527:
528: public function getErrors()
529: {
530: return array_unique($this->errors);
531: }
532:
533:
534: 535: 536:
537: public function hasErrors()
538: {
539: return (bool) $this->errors;
540: }
541:
542:
543: 544: 545:
546: public function cleanErrors()
547: {
548: $this->errors = [];
549: }
550:
551:
552: 553: 554: 555:
556: public static function enableAutoOptionalMode()
557: {
558: self::$autoOptional = true;
559: }
560:
561:
562:
563:
564:
565: 566: 567: 568:
569: public function setOption($key, $value)
570: {
571: if ($value === null) {
572: unset($this->options[$key]);
573: } else {
574: $this->options[$key] = $value;
575: }
576: return $this;
577: }
578:
579:
580: 581: 582: 583:
584: public function getOption($key, $default = null)
585: {
586: return isset($this->options[$key]) ? $this->options[$key] : $default;
587: }
588:
589:
590: 591: 592: 593:
594: public function getOptions()
595: {
596: return $this->options;
597: }
598:
599:
600:
601:
602:
603: public function __call($name, $args)
604: {
605: if ($callback = Nette\Utils\ObjectMixin::getExtensionMethod(get_class($this), $name)) {
606: return Nette\Utils\Callback::invoke($callback, $this, ...$args);
607: }
608: return parent::__call($name, $args);
609: }
610:
611:
612: public static function extensionMethod($name, $callback = null)
613: {
614: if (strpos($name, '::') !== false) {
615: list(, $name) = explode('::', $name);
616: }
617: Nette\Utils\ObjectMixin::setExtensionMethod(get_called_class(), $name, $callback);
618: }
619: }
620: