1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10: 11:
12:
13:
14:
15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35:
36: abstract class FormControl extends Component implements IFormControl
37: {
38:
39: public static $idMask = 'frm%s-%s';
40:
41:
42: public $caption;
43:
44:
45: protected $value;
46:
47:
48: protected $control;
49:
50:
51: protected $label;
52:
53:
54: private $errors = array();
55:
56:
57: private $disabled = FALSE;
58:
59:
60: private $htmlId;
61:
62:
63: private $htmlName;
64:
65:
66: private $rules;
67:
68:
69: private $translator = TRUE; 70:
71:
72: private $options = array();
73:
74:
75:
76: 77: 78:
79: public function __construct($caption = NULL)
80: {
81: $this->monitor('Form');
82: parent::__construct();
83: $this->control = Html::el('input');
84: $this->label = Html::el('label');
85: $this->caption = $caption;
86: $this->rules = new Rules($this);
87: }
88:
89:
90:
91: 92: 93: 94: 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: 107: 108: 109: 110:
111: public function getForm($need = TRUE)
112: {
113: return $this->lookup('Form', $need);
114: }
115:
116:
117:
118: 119: 120: 121:
122: public function getHtmlName()
123: {
124: if ($this->htmlName === NULL) {
125: $s = '';
126: $name = $this->getName();
127: $obj = $this->lookup('FormContainer', TRUE);
128: while (!($obj instanceof Form)) {
129: $s = "[$name]$s";
130: $name = $obj->getName();
131: $obj = $obj->lookup('FormContainer', TRUE);
132: }
133: $name .= $s;
134: if (is_numeric($name) || in_array($name, array('attributes','children','elements','focus','length','reset','style','submit','onsubmit'))) {
135: $name .= '_';
136: }
137: $this->htmlName = $name;
138: }
139: return $this->htmlName;
140: }
141:
142:
143:
144: 145: 146: 147: 148:
149: public function setHtmlId($id)
150: {
151: $this->htmlId = $id;
152: return $this;
153: }
154:
155:
156:
157: 158: 159: 160:
161: public function getHtmlId()
162: {
163: if ($this->htmlId === FALSE) {
164: return NULL;
165:
166: } elseif ($this->htmlId === NULL) {
167: $this->htmlId = sprintf(self::$idMask, $this->getForm()->getName(), $this->getHtmlName());
168: $this->htmlId = str_replace(array('[]', '[', ']'), array('', '-', ''), $this->htmlId);
169: }
170: return $this->htmlId;
171: }
172:
173:
174:
175: 176: 177: 178: 179: 180:
181: public function setAttribute($name, $value = TRUE)
182: {
183: $this->control->$name = $value;
184: return $this;
185: }
186:
187:
188:
189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199:
200: public function setOption($key, $value)
201: {
202: if ($value === NULL) {
203: unset($this->options[$key]);
204:
205: } else {
206: $this->options[$key] = $value;
207: }
208: return $this;
209: }
210:
211:
212:
213: 214: 215: 216: 217: 218:
219: final public function getOption($key, $default = NULL)
220: {
221: return isset($this->options[$key]) ? $this->options[$key] : $default;
222: }
223:
224:
225:
226: 227: 228: 229:
230: final public function getOptions()
231: {
232: return $this->options;
233: }
234:
235:
236:
237:
238:
239:
240:
241: 242: 243: 244: 245:
246: public function setTranslator(ITranslator $translator = NULL)
247: {
248: $this->translator = $translator;
249: return $this;
250: }
251:
252:
253:
254: 255: 256: 257:
258: final public function getTranslator()
259: {
260: if ($this->translator === TRUE) {
261: return $this->getForm(FALSE) ? $this->getForm()->getTranslator() : NULL;
262: }
263: return $this->translator;
264: }
265:
266:
267:
268: 269: 270: 271: 272: 273:
274: public function translate($s, $count = NULL)
275: {
276: $translator = $this->getTranslator();
277: return $translator === NULL || $s == NULL ? $s : $translator->translate($s, $count); 278: }
279:
280:
281:
282:
283:
284:
285:
286: 287: 288: 289: 290:
291: public function setValue($value)
292: {
293: $this->value = $value;
294: return $this;
295: }
296:
297:
298:
299: 300: 301: 302:
303: public function getValue()
304: {
305: return $this->value;
306: }
307:
308:
309:
310: 311: 312: 313: 314:
315: public function setDefaultValue($value)
316: {
317: $form = $this->getForm(FALSE);
318: if (!$form || !$form->isAnchored() || !$form->isSubmitted()) {
319: $this->setValue($value);
320: }
321: return $this;
322: }
323:
324:
325:
326: 327: 328: 329:
330: public function loadHttpData()
331: {
332: $path = explode('[', strtr(str_replace(array('[]', ']'), '', $this->getHtmlName()), '.', '_'));
333: $this->setValue(ArrayTools::get($this->getForm()->getHttpData(), $path));
334: }
335:
336:
337:
338: 339: 340: 341: 342:
343: public function setDisabled($value = TRUE)
344: {
345: $this->disabled = (bool) $value;
346: return $this;
347: }
348:
349:
350:
351: 352: 353: 354:
355: public function isDisabled()
356: {
357: return $this->disabled;
358: }
359:
360:
361:
362:
363:
364:
365:
366: 367: 368: 369:
370: public function getControl()
371: {
372: $this->setOption('rendered', TRUE);
373: $control = clone $this->control;
374: $control->name = $this->getHtmlName();
375: $control->disabled = $this->disabled;
376: $control->id = $this->getHtmlId();
377: $rules = self::exportRules($this->rules);
378: $rules = substr(json_encode($rules), 1, -1);
379: $rules = preg_replace('#"([a-z0-9]+)":#i', '$1:', $rules);
380: $rules = preg_replace('#(?<!\\\\)"([^\\\\\',]*)"#i', "'$1'", $rules);
381: $control->data['nette-rules'] = $rules ? $rules : NULL;
382: return $control;
383: }
384:
385:
386:
387: 388: 389: 390: 391:
392: public function getLabel($caption = NULL)
393: {
394: $label = clone $this->label;
395: $label->for = $this->getHtmlId();
396: if ($caption !== NULL) {
397: $label->setText($this->translate($caption));
398:
399: } elseif ($this->caption instanceof Html) {
400: $label->add($this->caption);
401:
402: } else {
403: $label->setText($this->translate($this->caption));
404: }
405: return $label;
406: }
407:
408:
409:
410: 411: 412: 413:
414: final public function getControlPrototype()
415: {
416: return $this->control;
417: }
418:
419:
420:
421: 422: 423: 424:
425: final public function getLabelPrototype()
426: {
427: return $this->label;
428: }
429:
430:
431:
432: 433: 434: 435: 436: 437:
438: public function setRendered($value = TRUE)
439: {
440: $this->setOption('rendered', $value);
441: return $this;
442: }
443:
444:
445:
446: 447: 448: 449: 450:
451: public function isRendered()
452: {
453: return !empty($this->options['rendered']);
454: }
455:
456:
457:
458:
459:
460:
461:
462: 463: 464: 465: 466: 467: 468:
469: public function addRule($operation, $message = NULL, $arg = NULL)
470: {
471: $this->rules->addRule($operation, $message, $arg);
472: return $this;
473: }
474:
475:
476:
477: 478: 479: 480: 481: 482:
483: public function addCondition($operation, $value = NULL)
484: {
485: return $this->rules->addCondition($operation, $value);
486: }
487:
488:
489:
490: 491: 492: 493: 494: 495: 496:
497: public function addConditionOn(IFormControl $control, $operation, $value = NULL)
498: {
499: return $this->rules->addConditionOn($control, $operation, $value);
500: }
501:
502:
503:
504: 505: 506:
507: final public function getRules()
508: {
509: return $this->rules;
510: }
511:
512:
513:
514: 515: 516: 517: 518: 519:
520: final public function setRequired($message = NULL)
521: {
522: $this->rules->addRule(Form::FILLED, $message);
523: return $this;
524: }
525:
526:
527:
528: 529: 530: 531: 532:
533: final public function isRequired()
534: {
535: return !empty($this->options['required']);
536: }
537:
538:
539:
540: 541: 542: 543: 544:
545: public function notifyRule(Rule $rule)
546: {
547: if (is_string($rule->operation) && strcasecmp($rule->operation, ':filled') === 0) {
548: $this->setOption('required', TRUE);
549: }
550: }
551:
552:
553:
554: 555: 556:
557: private static function exportRules($rules)
558: {
559: $payload = array();
560: foreach ($rules as $rule) {
561: if (!is_string($rule->operation)) {
562: continue;
563:
564: } elseif ($rule->type === Rule::VALIDATOR) {
565: $item = array('op' => ($rule->isNegative ? '~' : '') . $rule->operation, 'msg' => $rules->formatMessage($rule, FALSE));
566:
567: } elseif ($rule->type === Rule::CONDITION) {
568: $item = array('op' => ($rule->isNegative ? '~' : '') . $rule->operation, 'rules' => self::exportRules($rule->subRules), 'control' => $rule->control->getHtmlName());
569: if ($rule->subRules->getToggles()) {
570: $item['toggle'] = $rule->subRules->getToggles();
571: }
572: }
573:
574: if (is_array($rule->arg)) {
575: foreach ($rule->arg as $key => $value) {
576: $item['arg'][$key] = $value instanceof IFormControl ? (object) array('control' => $value->getHtmlName()) : $value;
577: }
578: } elseif ($rule->arg !== NULL) {
579: $item['arg'] = $rule->arg instanceof IFormControl ? (object) array('control' => $rule->arg->getHtmlName()) : $rule->arg;
580: }
581:
582: $payload[] = $item;
583: }
584: return $payload;
585: }
586:
587:
588:
589:
590:
591:
592:
593: 594: 595: 596: 597: 598:
599: public static function validateEqual(IFormControl $control, $arg)
600: {
601: $value = $control->getValue();
602: foreach ((is_array($value) ? $value : array($value)) as $val) {
603: foreach ((is_array($arg) ? $arg : array($arg)) as $item) {
604: if ((string) $val === (string) ($item instanceof IFormControl ? $item->value : $item)) {
605: return TRUE;
606: }
607: }
608: }
609: return FALSE;
610: }
611:
612:
613:
614: 615: 616: 617: 618:
619: public static function validateFilled(IFormControl $control)
620: {
621: return (string) $control->getValue() !== ''; 622: }
623:
624:
625:
626: 627: 628: 629: 630:
631: public static function validateValid(IFormControl $control)
632: {
633: return $control->rules->validate(TRUE);
634: }
635:
636:
637:
638: 639: 640: 641: 642:
643: public function addError($message)
644: {
645: if (!in_array($message, $this->errors, TRUE)) {
646: $this->errors[] = $message;
647: }
648: $this->getForm()->addError($message);
649: }
650:
651:
652:
653: 654: 655: 656:
657: public function getErrors()
658: {
659: return $this->errors;
660: }
661:
662:
663:
664: 665: 666:
667: public function hasErrors()
668: {
669: return (bool) $this->errors;
670: }
671:
672:
673:
674: 675: 676:
677: public function cleanErrors()
678: {
679: $this->errors = array();
680: }
681:
682: }
683: